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