mysql 复制表耗时_聊一下mysql的表复制
1 insert...from的問(wèn)題
insert … select 是很常見(jiàn)的在兩個(gè)表之間拷貝數(shù)據(jù)的方法。需要注意,在可重復(fù)讀隔離級(jí)別下,這個(gè)語(yǔ)句會(huì)給
select的表里掃描到的記錄和間隙加讀鎖。
以下對(duì)insert...select 進(jìn)行一下測(cè)試
全表讀或主鍵排序讀
sessionA
mysql> insert into trajectory_min_section_0511_copy select * from trajectory_min_section_0511;
sessionB
mysql> show engine innodb status;
1451 lock struct(s), heap size 155856, 121231 row lock(s), undo log entries 119783
mysql> show engine innodb status;
3088 lock struct(s), heap size 303312, 258340 row lock(s), undo log entries 255254
mysql> show engine innodb status;
15004 lock struct(s), heap size 1466576, 1256305 row lock(s), undo log entries 1241304
終止sessionA后
sessionB
mysql> show engine innodb status;
ROLLING BACK 23737 lock struct(s), heap size 2302160, 1987628 row lock(s), undo log entries 471818
mysql> show engine innodb status;
ROLLING BACK 23737 lock struct(s), heap size 2302160, 1987628 row lock(s), undo log entries 20019
通過(guò)測(cè)試可以看到row locks是一個(gè)慢慢增長(zhǎng)的過(guò)程。undo log entries也在一直增長(zhǎng),這個(gè)的作用是為了rollback恢復(fù)。
用主鍵升序插入以及用主鍵降序插入:
select * from trajectory_min_section_0511 order id(PK) ASC
select * from trajectory_min_section_0511 order id(PK) DESC
也是一樣的效果,感興趣的可以測(cè)試一下。
從上面測(cè)試可知:通過(guò)主鍵排序或則不加排序字段的導(dǎo)入操作"insert into A select * from B",是會(huì)鎖B表,但他的鎖是逐步地鎖定已經(jīng)掃描過(guò)的記錄。
當(dāng)終止后可以看到undo log entries數(shù)量慢慢降下去
非主鍵排序讀
sessionA
mysql> insert into trajectory_min_section_0511_copy select * from trajectory_min_section_0511 order by t_distance desc;
sessionB
sessionA執(zhí)行過(guò)程中執(zhí)行n次
mysql> show engine innodb status;
63595 lock struct(s), heap size 6168784, 5325513 row lock(s), undo log entries 5118624
從上面測(cè)試可知:非主鍵排序的導(dǎo)入操作,是會(huì)鎖表,而且糟糕的是,鎖是一開(kāi)始就會(huì)鎖定整張表。
讀寫(xiě)驗(yàn)證
sessionA
mysql> insert into trajectory_min_section_0511_copy select * from trajectory_min_section_0511;
sessionB
mysql> select * from trajectory_min_section_0511 where id=1715
搜索靠前主鍵數(shù)據(jù)可以正常返回
mysql> update trajectory_min_section_0511 set direction=2 where id=1715
更新靠前主鍵數(shù)據(jù)超時(shí)無(wú)法返回
sessionA
mysql> insert into trajectory_min_section_0511_copy select * from trajectory_min_section_0511 order by id desc;
sessionB
mysql> select * from trajectory_min_section_0511 where id=9999
搜索靠后主鍵數(shù)據(jù)可以正常返回
mysql> update trajectory_min_section_0511 set direction=2 where id=1715
更新靠后主鍵數(shù)據(jù)超時(shí)無(wú)法返回
以上測(cè)試在上鎖過(guò)程中,不能dml操作任何被上鎖的行,直到鎖釋放。
如果已知對(duì)源表的掃描行數(shù)和加鎖范圍很小的話,簡(jiǎn)單地使用insert … select 語(yǔ)句即可實(shí)現(xiàn)。如果對(duì)線上重點(diǎn)表遷移,為了避免對(duì)源表加讀鎖,更穩(wěn)妥
的方案是先將數(shù)據(jù)寫(xiě)到外部文本文件,然后再寫(xiě)回目標(biāo)表。
2 高效加載數(shù)據(jù)
提高寫(xiě)入、加載速度的幾個(gè)原則,這些原則在任何數(shù)據(jù)庫(kù)上都是通用的:
把數(shù)據(jù)從緩存刷新到磁盤(pán)次數(shù)越少,數(shù)據(jù)加載的越快。因此,批量加載一定是比單條加載效率更高,因?yàn)榕坎迦氲男锌梢韵刃芯彺?#xff0c;然后在
加載操作時(shí)候一次性刷到磁盤(pán)上,減少磁盤(pán)的隨機(jī)讀操作。
表的索引越少,加載速度越快,如果表有多個(gè)列存在索引,每次插入都需要更新所有索引完后才會(huì)識(shí)別到新的行加入。所以不要建無(wú)謂的索引。
短sql比長(zhǎng)sql加載速度更快,因?yàn)樵诜?wù)器上解析操作耗時(shí)更少,并且可以更快的通過(guò)網(wǎng)絡(luò)發(fā)送到服務(wù)器。
加載數(shù)據(jù)一般有insert和load兩種,接下來(lái)詳細(xì)解讀一下底層實(shí)現(xiàn)的不同。
2.1 load主備同步流程
主庫(kù)執(zhí)行完成后,將/root/table.txt文件的內(nèi)容直接寫(xiě)到一個(gè)外部文件中。
往binlog文件中寫(xiě)入語(yǔ)句load data local infile ‘/tmp/SQL_LOAD_MB-1-0’ INTO TABLE db2.t。
讀取本地文件
把這個(gè)binlog日志傳到備庫(kù)。
備庫(kù)的apply線程在執(zhí)行這個(gè)事務(wù)日志時(shí):
先將binlog中t.csv文件的內(nèi)容讀出來(lái),寫(xiě)入到本地臨時(shí)目錄/tmp/SQL_LOAD_MB-1-0 中;
再執(zhí)行l(wèi)oad data語(yǔ)句,往備庫(kù)的db2.t表中插入跟主庫(kù)相同的數(shù)據(jù)。
147d3f6f.png
2.2 總結(jié)
總的來(lái)說(shuō),根據(jù)官方介紹load可以快出insert 20多倍
load 的底層實(shí)現(xiàn)的優(yōu)化:
1 跳過(guò)sql解析,直接生成數(shù)據(jù)文件;
2 在導(dǎo)入之前會(huì)關(guān)掉索引,導(dǎo)入完成后更新索引;而與之對(duì)比的Insert的處理機(jī)制是:每插入一條則更新一次數(shù)據(jù)庫(kù),更新一次索引。
官網(wǎng)的一些解答
f01fa40b.png
4 數(shù)據(jù)拷貝方法
介紹三種mysql數(shù)據(jù)拷貝的主流方法,分別是mysqldump(sql語(yǔ)句拷貝)、load(csv文件拷貝)、以及物理文件拷貝。
4.1 mysql dump
mysqldump -h$host -P$port -u$user --add-locks=0 --no-create-info --single-transaction --set-gtid-purged=OFF db1 t --where="a>900" --result-file=/client_tmp/t.sql
主要參數(shù)含義如下:
–single-transaction的作用是,在導(dǎo)出數(shù)據(jù)的時(shí)候不需要對(duì)表db1.t加表鎖,而是使用START TRANSACTION WITH CONSISTENT SNAPSHOT的方法;
–add-locks設(shè)置為0,表示在輸出的文件結(jié)果里,不增加" LOCK TABLES t WRITE;" ;
–no-create-info的意思是,不需要導(dǎo)出表結(jié)構(gòu);
–set-gtid-purged=off表示的是,不輸出跟GTID相關(guān)的信息;
–result-file指定了輸出文件的路徑,其中client表示生成的文件是在客戶端機(jī)器上的。
通過(guò)這條mysqldump命令生成的t.sql文件中就包含了如圖1所示的INSERT語(yǔ)句。
ae0f129a.png
可以看到一個(gè)insert中包含多個(gè)value對(duì),這樣插入執(zhí)行速度可以加快。
這也是navicat 上 data transfer功能
148c3bcb.png
4.2 load
show variables like ‘%secure_file_priv%’;
如果設(shè)置為empty,表示不限制文件生成的位置,這是不安全的設(shè)置;
如果設(shè)置為一個(gè)表示路徑的字符串,就要求生成的文件只能放在這個(gè)指定的目錄,或者它的子目錄;
如果設(shè)置為NULL,就表示禁止在這個(gè)MySQL實(shí)例上執(zhí)行select … into outfile 操作。
通過(guò)修改配置文件永久生效
4f4991f4.png
設(shè)置后可以通過(guò)命令導(dǎo)出
select * from db1.t where a>900 into outfile '/server_tmp/t.csv';
導(dǎo)出的csv文件可以用如下命令導(dǎo)入
load data infile '/server_tmp/t.csv' into table db2.t;
拷貝到數(shù)據(jù)庫(kù)本地后,可以用本地導(dǎo)入,比遠(yuǎn)程導(dǎo)入更快,因?yàn)槭∪チ嗽趌oad這個(gè)事務(wù)中網(wǎng)絡(luò)傳輸,減輕數(shù)據(jù)庫(kù)壓力。
load data local infile '/server_tmp/t.csv' into table db2.t;
4.3 物理拷貝
在掌握了邏輯拷貝的方法后,是否有物理導(dǎo)數(shù)據(jù)的方法呢?比如,直接把db1.t表的.frm文件和.ibd文件拷貝到db2目錄下,是否可行呢?
答案是不行的。
因?yàn)?#xff0c;一個(gè)InnoDB表,除了包含這兩個(gè)物理文件外,還需要在數(shù)據(jù)字典中注冊(cè)。直接拷貝這兩個(gè)文件的話,因?yàn)閿?shù)據(jù)字典中沒(méi)有db2.t這個(gè)表,系統(tǒng)是不會(huì)識(shí)別和接受它們的。
不過(guò),在MySQL 5.6版本引入了可傳輸表空間(transportable tablespace)的方法,可以通過(guò)導(dǎo)出+導(dǎo)入表空間的方式,實(shí)現(xiàn)物理拷貝表的功能。
假設(shè)我們現(xiàn)在的目標(biāo)是在db1庫(kù)下,復(fù)制一個(gè)跟表trajectory_min_section_0511相同的表trajectory_min_section_0511_copy,具體的執(zhí)行步驟如下:
-- 源端執(zhí)行
flush table trajectory_min_section_0511 for export
這時(shí)候數(shù)據(jù)庫(kù)目錄下會(huì)生成一個(gè)trajectory_min_section_0511.cfg文件;
cp trajectory_min_section_0511.ibd trajectory_min_section_0511_copy.ibd;
在db1目錄下執(zhí)行cp trajectory_min_section_0511.cfg trajectory_min_section_0511_copy.cfg; 這兩個(gè)命令(
這里需要注意的是,拷貝得到的兩個(gè)文件,MySQL進(jìn)程要有讀寫(xiě)權(quán)限);
unlock tables
trajectory_min_section_0511.cfg文件會(huì)被刪除;
-- 目標(biāo)端執(zhí)行
create table trajectory_min_section_0511_copy like trajectory_min_section_0511;
首先創(chuàng)建一個(gè)相同表結(jié)構(gòu)的空表
alter table trajectory_min_section_0511_copy discard tablespace;
這時(shí)候trajectory_min_section_0511_copy.ibd文件會(huì)被刪除
alter table trajectory_min_section_0511_copy import tablespace
將這個(gè)trajectory_min_section_0511_copy.ibd文件作為表trajectory_min_section_0511_copy的新的表空間,
由于這個(gè)文件的數(shù)據(jù)內(nèi)容和trajectory_min_section_0511.ibd是相同的,所以表trajectory_min_section_0511_copy中
就有了和表trajectory_min_section_0511相同的數(shù)據(jù)。
測(cè)試后最后一步導(dǎo)入500w耗時(shí)10s左右,其他操作都是立刻執(zhí)行
7c9e2e74.png
幾點(diǎn)注意:
在第3步執(zhí)行完flush table命令之后,整個(gè)表trajectory_min_section_0511處于只讀狀態(tài),直到執(zhí)行unlock tables命令后才釋放讀鎖;
在執(zhí)行import tablespace的時(shí)候,為了讓文件里的表空間id和數(shù)據(jù)字典中的一致,會(huì)修改trajectory_min_section_0511_copy.ibd的表空間id。
而這個(gè)表空間id存在于每一個(gè)數(shù)據(jù)頁(yè)中。因此,如果是一個(gè)很大的文件(比如TB級(jí)別),每個(gè)數(shù)據(jù)頁(yè)都需要修改,import語(yǔ)句的執(zhí)行是需要一些時(shí)間的。
當(dāng)然,如果是相比于邏輯導(dǎo)入的方法,import語(yǔ)句的耗時(shí)是非常短的。
4.4 三種方式對(duì)比
對(duì)比一下這三種方法的優(yōu)缺點(diǎn)。
物理拷貝的方式速度最快,尤其對(duì)于大表拷貝來(lái)說(shuō)是最快的方法。如果出現(xiàn)誤刪表的情況,用備份恢復(fù)出誤刪之前的臨時(shí)庫(kù),然后再把臨時(shí)庫(kù)中的表拷貝到生產(chǎn)庫(kù)上,是恢復(fù)數(shù)據(jù)最快的方法。但是,這種方法的使用也有一定的局限性:
a) 必須是全表拷貝,不能只拷貝部分?jǐn)?shù)據(jù);
b) 需要到服務(wù)器上拷貝數(shù)據(jù),在用戶無(wú)法登錄數(shù)據(jù)庫(kù)主機(jī)的場(chǎng)景下無(wú)法使用;
c) 由于是通過(guò)拷貝物理文件實(shí)現(xiàn)的,源表和目標(biāo)表都是使用InnoDB引擎時(shí)才能使用。
用mysqldump生成包含INSERT語(yǔ)句文件的方法,可以在where參數(shù)增加過(guò)濾條件,來(lái)實(shí)現(xiàn)只導(dǎo)出部分?jǐn)?shù)據(jù)。這個(gè)方式的不足之一是,不能使用join這種比較復(fù)雜的where條件寫(xiě)法。
用select … into outfile的方法是最靈活的,支持所有的SQL寫(xiě)法。但,這個(gè)方法的缺點(diǎn)之一就是,每次只能導(dǎo)出一張表的數(shù)據(jù),而且表結(jié)構(gòu)也需要另外的語(yǔ)句單獨(dú)備份。
后兩種方式都是邏輯備份方式,是可以跨引擎使用的。
5 當(dāng)數(shù)據(jù)量很大時(shí)
拋開(kāi)最后一種物理拷貝,因?yàn)樵诰€上時(shí)不一定可以登錄數(shù)據(jù)庫(kù)主機(jī),而且也可能只拷貝部分?jǐn)?shù)據(jù)。那一般就是使用load data infile方式導(dǎo)入。
當(dāng)數(shù)據(jù)量極大(10+G,千萬(wàn)級(jí)別)時(shí),用之前介紹的方式會(huì)有很大問(wèn)題。
load為一個(gè)長(zhǎng)事務(wù),最后commit后才插入數(shù)據(jù)庫(kù)。
undo增長(zhǎng)快速,無(wú)法回收。數(shù)據(jù)庫(kù)性能下降,undo大小大于buffer pool,就會(huì)開(kāi)始內(nèi)存和磁盤(pán)的交換。
可以大文件拆小,監(jiān)控cpu后,在合理的利用率情況下,多線程load。
6 擴(kuò)展知識(shí)點(diǎn)
針對(duì)并發(fā)場(chǎng)景的一致性問(wèn)題,第一個(gè)能想到的是加鎖,但是數(shù)據(jù)庫(kù)中所有操作都上鎖勢(shì)必會(huì)帶來(lái)性能的低下,mysql的隔離級(jí)別(isolation level)最高級(jí)叫串行化,
其設(shè)計(jì)上可以認(rèn)為就是加一把大鎖,讀的時(shí)候加共享鎖,不能寫(xiě),寫(xiě)的時(shí)候,加的是排它鎖,阻塞其它事務(wù)的寫(xiě)入和讀取,若是其它的事務(wù)長(zhǎng)時(shí)間不能寫(xiě)入就會(huì)直接報(bào)超時(shí),所以它的性能也是最差的,對(duì)于它來(lái)就沒(méi)有什么并發(fā)性可言。
在InnoDB 的讀提交和可重復(fù)讀兩種級(jí)別都使用了多版本并發(fā)控制模型(MVCC)
比如在實(shí)現(xiàn)可重復(fù)讀的隔離級(jí)別,只需要在事務(wù)開(kāi)始的時(shí)候創(chuàng)建一致性視圖,也叫做快照,之后的查詢(xún)里都共用這個(gè)一致性視圖,后續(xù)的事務(wù)對(duì)數(shù)據(jù)的更改
是對(duì)當(dāng)前事務(wù)是不可見(jiàn)的,這樣就實(shí)現(xiàn)了可重復(fù)讀。 中每一個(gè)事務(wù)都有一個(gè)自己的事務(wù)id,并且是唯一的,遞增的 。
71a299d8.png
最開(kāi)始數(shù)據(jù)的版本是V0;
T1時(shí)刻發(fā)起了一個(gè)寫(xiě)任務(wù),這是把數(shù)據(jù)clone了一份,進(jìn)行修改,版本變?yōu)閂1,但任務(wù)還未完成;
T2時(shí)刻并發(fā)了一個(gè)讀任務(wù),依然可以讀V0版本的數(shù)據(jù);
T3時(shí)刻又并發(fā)了一個(gè)讀任務(wù),依然不會(huì)阻塞;
對(duì)于Mysql中的每一個(gè)數(shù)據(jù)行都有可能存在多個(gè)版本,在每次事務(wù)更新數(shù)據(jù)的時(shí)候,都會(huì)生成一個(gè)新的數(shù)據(jù)版本,并且把自己的數(shù)據(jù)id賦值給當(dāng)前版本的row trx_id。
e6536994.png
如圖中所示,假如三個(gè)事務(wù)更新了同一行數(shù)據(jù),那么就會(huì)有對(duì)應(yīng)的三個(gè)數(shù)據(jù)版本。
實(shí)際上版本1、版本2并非實(shí)際物理存在的,而圖中的U1和U2實(shí)際就是undo log,這v1和v2版本是根據(jù)當(dāng)前v3和undo log計(jì)算出來(lái)的。
當(dāng)出現(xiàn)事務(wù)回滾的時(shí)候,通過(guò)undo log反向重現(xiàn)redo log的過(guò)程,就可以將當(dāng)前數(shù)據(jù)回退回事務(wù)開(kāi)始前數(shù)據(jù)狀態(tài)。
當(dāng)事務(wù)提交后undo塊就會(huì)慢慢的回收。要避免長(zhǎng)事務(wù),因?yàn)槿绻L(zhǎng)事務(wù)中有更新,就意味著占用著行鎖,導(dǎo)致別的語(yǔ)句更新被鎖。還有讀的事務(wù)會(huì)導(dǎo)致undo log不能回收,導(dǎo)致回滾段空間膨脹。
擴(kuò)展閱讀 萬(wàn)字長(zhǎng)文,幫你梳理存儲(chǔ)引擎之Heap表關(guān)鍵知識(shí)點(diǎn)
總結(jié)
以上是生活随笔為你收集整理的mysql 复制表耗时_聊一下mysql的表复制的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 鸣潮今汐声骸怎么搭配 鸣潮今汐声骸搭配攻
- 下一篇: 确定了 国内油价将在今晚大涨 95汽油将