RDS for MySQL 大表操作
1. 名詞解釋 2. 實作原理 3. 推薦方法RDS for MySQL 的大表操作(比如 空間回收、添加字段、調整索引)一直是比較困擾 RDS for MySQL 使用者的問題。
近期相關咨詢和 回報的問題(比如 主執行個體上執行 optimize table 導緻隻讀執行個體高延遲)比較多,下面彙總說明下操作的幾種方法及其各自的特點。
https://www.atatech.org/articles/114969#1
https://www.atatech.org/articles/114969#2
https://www.atatech.org/articles/114969#3 2.1 Online DDL
Online DDL 主要思路是将 DDL 執行過程中的 DML 增量資料儲存在獨立的日志檔案中,在完成全量資料後進行資料回放 來支援并發 DML (仍舊存在某些操作不支援并發 DML)。
由此帶來了幾個問題:
- 大表 DDL 操作執行時間長,如果 DML 增量資料過多,會導緻存放增量資料的日志空間不夠,引發 DDL 操作失敗。
- DML 增量資料存儲在獨立日志中,如果表上有唯一鍵限制,DDL 執行過程中會 忽略 唯一性限制,會有一定機率在重放增量資料時由于 duplicated key 錯誤而導緻 DDL 操作失敗。
- 不是全部操作都支援并發 DML。
- DDL 一旦開始執行無法暫停,并且無法控制完成時間點。
- 大表 DDL 操作執行時間長,完成後複制到隻讀執行個體上執行,會導緻隻讀執行個體複制延遲。
- 在 DDL 開始和結束部分都需要擷取表 Metadata Lock 的 排他鎖,容易引發表 MDL 鎖等待進而導緻表不可通路。
https://www.atatech.org/articles/114969#4 2.2 Percona Toolkit - Online Schema Change
PT-OSC 的主要思路是 建立目标結構的新表,将原表中資料拷貝到新表中,同時通過原表上的 Insert、Update 和 Delete 觸發器(Trigger)同步拷貝過程中的增量資料;資料同步後 rename 新表為原表。
由于是基于 Trigger 的實作,Trigger 綁定在并發 DML 操作的事務中執行,由此帶來一些問題。
- 速度慢,Trigger 是基于 行(per-row)變化來解釋執行(每行變化都要解釋執行一次,無法預編譯,無法批量執行)。
- 不支援原表上存在 Trigger。
- 原表必須定義主鍵 或 唯一鍵。
- Trigger 對被綁定的事務引入 新表 上的鎖競争。
- Rename 表 和 删除表上建立的 Trigger 都需要擷取表 Metadata Lock 的 排他鎖,容易引發表 MDL 鎖等待進而導緻表不可通路。
https://www.atatech.org/articles/114969#5 2.3 GH - Online Schema Transfer
GH-OST 結合 RDS for MySQL 使用的主要思路是建立目标結構的新表,然後通過将自身模拟為一個 Slave 執行個體,擷取表上的增量資料并将之再應用到新表上;規避了使用 Trigger 帶來的問題。
Step 1、RDS for MySQL 主執行個體上持續存在 對 原表 old_table 的 DML 操作。
Step2、GH-OST 用戶端連接配接 RDS for MySQL 主執行個體。
Step 3、根據 DDL 建立影子表 ghost_table。
Step 4、GH-OST 用戶端将自身注冊為一個 Slave 執行個體,擷取 RDS for MySQL 主執行個體的增量 binlog 資訊(要求登入賬戶有 replication slave,replication client 權限)。
Step 5、并發拷貝 表 old_table 中的全量 和 增量資料(來自 binlog)到影子表 ghost_table。
Step 6、當使用者觸發切換,阻止原表 old_table 上的 DML 操作。
Step 7、等待影子表 ghost_table 和 原表 old_table 資料一緻。
Step 8、切換(rename)影子表 ghost_table 為 原表 old_table。
https://www.atatech.org/articles/114969#6 2.4 方法對比
下面小節下各個工具的優缺點:
RDS for MySQL Online DDL 使用 RDS for MySQL 表上 Metadata Lock 的産生和處理 MySQL 5.7 Online DDL Operations MySQL 5.6 Online DDL Operations MySQL 5.7 Online DDL Limitations MySQL 5.6 Online DDL Limitations RDS for MySQL 如何使用 Percona Toolkit Percona Toolkit Documentation gh-ost: GitHub's online schema migration tool for MySQL GH-OST Requirements and limitations MySQL · 社群動态 · Online DDL 工具 gh-ost 支援阿裡雲 RDShttps://www.atatech.org/articles/114969#7
對于不會導緻鎖表的操作,并且能夠支援隻讀執行個體延遲或不使用隻讀執行個體一定時間的業務,可以考慮直接使用 Online DDL。
對于要批量執行的低風險操作,可以考慮直接使用 Online DDL。
對于有隻讀執行個體并且對延遲敏感的一個或幾個大表操作,建議考慮 GH-OST。
https://www.atatech.org/articles/114969#8 3.1 Online DDL
需要注意的地方:
- 調整 Innodb_online_alter_log_max_size 到最大值避免增量日志空間問題。
- alter 語句帶 algorithm=inplace, lock=none 選項執行,避免自動降級鎖表。
- 正式執行前建立帶部分資料的同結構測試表,估算空閑空間使用情況和是否能支援并發 DML (DDL 結束後傳回 N rows affected;如果 N = 0 則證明采用 inplace 方式執行)。
https://www.atatech.org/articles/114969#9 3.2 GH-OST
樣例一 - 直接連接配接主執行個體收縮表:
gh-ost \
--aliyun-rds="true" \
--critical-load=Threads_running=100 \
--max-load=Threads_running=70 \
--chunk-size=1000 \
--assume-master-host="jacky01.mysql.rds.aliyuncs.com:3306" \
--assume-rbr \
--initially-drop-old-table \
--initially-drop-ghost-table \
--initially-drop-socket-file \
--ok-to-drop-table \
--host="jacky01.mysql.rds.aliyuncs.com" \
--port=3306 \
--user="jacky" \
--password="xxxx" \
--database="jacky" \
--table="ghost" \
--verbose \
--alter="engine=innodb" \
--allow-on-master \
--cut-over=default \
--default-retries=120 \
--panic-flag-file=/tmp/ghost.panic.flag \
--postpone-cut-over-flag-file=/tmp/ghost.postpone.flag \
--execute
# --chunk-size 指定每批次操作的表行數,預設 1000 行,取值範圍: 100-100,000
# --initially-drop-old-table 如果存在已經存在的 OLD table(也許來自上次直接退出的 GH-OST 執行),則删除該表;不帶該參數運作如果發現存在該表,則直接退出。
# --initially-drop-ghost-table 如果存在已經存在的 ghost table(也許來自上次直接退出的 GH-OST 執行),則删除該表;不帶該參數運作如果發現存在該表,則直接退出。
# --initially-drop-socket-file 如果存在已經存在的 socket file(也許來自上次直接退出的 GH-OST 執行),則删除該 socket 檔案;不帶該參數運作如果發現存在該檔案,則直接退出。
# --host 指定要操作的 RDS for MySQL 執行個體
# --port 指定該執行個體端口
# --user 指定操作使用的賬戶,該賬戶必須有 replication slave、replication client 權限
# --password 指定賬戶密碼
# --database 指定操作的 庫 名
# --table 指定要操作的表,該參數不能為空
# --alter 指定 DDL 操作
# --panic-flag-file 當這個檔案被建立後,GH-OST 直接終止退出,不做任何清理操作(比如删除 ghost table)。
樣例二 - 同時連接配接隻讀執行個體監控隻讀執行個體複制延遲
gh-ost \
--aliyun-rds="true" \
--critical-load=Threads_running=64 \
--max_load=Threads_running=32 \
--chunk-size=1000 \
--throttle-control-replicas="rr-2ze559088x3836.mysql.rds.aliyuncs.com:3306" \
--max-lag-millis=1500 \
--assume-master-host="jacky01.mysql.rds.aliyuncs.com:3306" \
--assume-rbr \
--initially-drop-old-table \
--initially-drop-ghost-table \
--initially-drop-socket-file \
--ok-to-drop-table \
--host="jacky01.mysql.rds.aliyuncs.com" \
--port=3306 \
--user="jacky" \
--password="xxxx" \
--database="jacky" \
--table="ghost" \
--verbose \
--alter="engine=innodb" \
--allow-on-master \
--cut-over=default \
--default-retries=120 \
--panic-flag-file=/tmp/ghost.panic.flag \
--postpone-cut-over-flag-file=/tmp/ghost.postpone.flag \
--execute