社區投稿 | MySQL 跨實例 copy 大表解決方案

作者:任坤

一、背景

某天晚上 20:00 左右開發人員找到我,要求把 pre-prod 環境上的某張表導入到 prod ,第二天早上 7 點上線要用。
該表有數億條數據,壓縮後 ibd 文件大約 25G 左右,表結構比較簡單:

CREATE TABLE `t` (
`UNIQUE_KEY` varchar(32) NOT NULL,
`DESC` varchar(64) DEFAULT NULL ,
`NUM_ID` int(10) DEFAULT '0' ,
PRIMARY KEY (`UNIQUE_KEY`),
KEY `index_NumID` (`NUM_ID`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPRESSED

MySQL 版本:pre-prod 和 prod 都採用 5.7.25 ,單向主從結構。

二、解決方案

最簡單的方法是採用 mysqldump + source ,但是該表數量比較多,之前測試的時候至少耗時 4h+ ,這次任務時間窗口比較短,如果中間執行失敗再重試,可能會影響業務正式上線。
採用 select into outfile + load infile 會快一點,但是該方案有個致命問題:
該命令在主庫會把所有數據當成單個事務執行,只有把數據全部成功插入後,纔會將 binlog 複製到從庫,這樣會造成從庫嚴重延遲,而且生成的單個 binlog 大小嚴重超標,在磁盤空間不足時可能會把磁盤佔滿。

經過比較,最終採用了可傳輸表空間方案,MySQL 5.6 借鑑 Oracle 引入該技術,允許在 2 個不同實例間快速的 copy innodb 大表。
該方案規避了昂貴的 sql 解析和 B+tree 葉節點分裂,目標庫可直接重用其他實例已有的 ibd 文件,只需同步一下數據字典,並對 ibd 文件頁進行一下校驗,即可完成數據同步操作。

具體操作步驟如下:

    1. 目標庫,創建表結構,然後執行 ALTER TABLE t DISCARD TABLESPACE ,此時表t只剩下 frm 文件
    1. 源庫,開啓 2 個會話

session1:執行 FLUSH TABLES t FOR EXPORT ,該命令會對 t 加鎖,將t的髒數據從 buffer pool 同步到表文件,同時新生成 1 個文件 t.cfg ,該文件存儲了表的數據字典信息
session2:保持 session1 打開狀態,此時將 t.cfg 和 t.ibd 遠程傳輸到目標庫的數據目錄,如果目標庫是主從結構,需要分別傳輸到主從兩個實例,傳輸完畢後修改屬主爲 mysql:mysql

    1. 源庫,session1 執行 unlock tables ,解鎖表 t ,此時 t 恢復正常讀寫
    1. 目標庫,執行 ALTER TABLE t IMPORT TABLESPACE ,如果是主從結構,只需要在主庫執行即可

 

三、實測

針對該表,執行 ALTER TABLE ... IMPORT TABLESPACE 命令只需要 6 分鐘完成,且 IO 消耗和主從延遲都被控制到合理範圍。
原本需要數個小時的操作,只需 10 多分鐘完成(算上數據傳輸耗時)。
如果線上有空表需要一次性加載大量數據,可以考慮先將數據導入到測試環境,然後通過可傳輸表空間技術同步到線上,可節約大量執行時間和服務器資源。

四、總結

可傳輸表空間,有如下使用限制:

  • 源庫和目標庫版本一致
  • 只適用於 innodb 引擎表
  • 源庫執行 flush tables t for export 時,該表會不可寫
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章