天天看點

DM運維踩坑實踐總結

作者:dbapower​

一、 背景

在早期從MySQL到TiDB實施同步操作過程中,我們大多數用的是mydumper+loader進行整體全量備份的導出,之後拿到meta資訊後,通過syncer實作增量同步,整體操作起來比較麻煩,涉及的配置檔案較多,其基本原理就是Syncer 通過把自己注冊為一個 MySQL Slave 的方式,和 MySQL Master 進行通信,然後不斷讀取 MySQL Binlog,進行 Binlog Event 解析,規則過濾和資料同步。其架構如下:

​​

DM運維踩坑實踐總結

​​

而後pingcap官方推出了TiDB Data Migration (DM)套件,這一套件極大地降低了同步工具使用的門檻。

DM是一體化的資料遷移任務管理平台,支援從 MySQL 或 MariaDB 到 TiDB 的全量資料遷移和增量資料複制。使用 DM 工具有利于簡化錯誤處理流程,降低運維成本。後續更是有dm-portal工具友善dba通過圖形化界面的方式進行選擇性導出和自動生成配置檔案,雖然有一些小bug和不夠人性化的方面,但無傷大雅,可惜的是這個項目最後咨詢官方得知被砍掉了,不再進行維護。

二、 原因

我有幸從DM内測版本開始就接觸和使用這一工具,直至其最新版1.0.6,見證了DM功能不斷的完善,真切體會到了這一工具給我們帶來的幫助,我認為這是每一個DBA和TiDB使用者都應該了解甚至熟練掌握的工具,因為大多數場景下,我們使用TiDB并不是全新的系統上去直接建庫建表,而是從MySQL遷移過來,先進行性能對比和測試,而後進行資料遷移的,是以熟練掌握DM工具可以讓你的工作事半功倍。

三、架構

叢集配置

叢集版本:v3.0.5
叢集配置:普通SSD磁盤,128G記憶體,40 核cpu
tidb21 TiDB/PD/pump/prometheus/grafana/CCS
tidb22 TiDB/PD/pump
tidb23 TiDB/PD/pump
tidb01 TiKV
tidb02 TiKV
tidb03 TiKV
tidb04 TiKV
tidb05 TiKV
tidb06 TiKV
tidb07 TiKV
tidb08 TiKV
tidb09 TiKV
tidb10 TiKV
tidb11 TiKV
tidb12 TiKV
tidb13 TiKV
tidb14 TiKV/DM-prometheus/DM-grafana/DMM
tidb15 TiKV/DMW1
tidb16 TiKV/DMW1      

正常來說,官方建議抽出單獨的機器來部署DM,且推薦每個節點上部署單個 DM-Worker 執行個體。除非機器擁有性能遠超 TiDB 軟體和硬體環境要求中推薦配置的 CPU 和記憶體,并且每個節點配置 2 塊以上的硬碟或大于 2T 的 SSD,才推薦單個節點上部署不超過 2 個 DM-Worker 執行個體。而我們的線上環境機器比較吃緊,是以一直以來都是和TiKV進行混部的,例如上述架構中,選取了tidb14,tidb15,tidb16進行dm相關的軟體部署。所幸運作起來效果還可以,但有條件的建議大家還是老老實實按照官方文檔,單獨部署。

DM架構

​​

DM運維踩坑實踐總結

​​

DM架構如上圖所示,主要包括三個元件:DM-master,DM-worker 和 dmctl。

DM-master 負責管理和排程資料遷移任務的各項操作

DM-worker 負責執行具體的資料遷移任務

dmctl 是用來控制 DM 叢集的指令行工具

具體的功能本文不再贅述,可參考官方文檔了解每一個子產品的詳細功能

DM特性

Table routing 合并遷移

Block & allow table lists 白名單

Binlog event filter binlog級别過濾

Shard support 合庫合表

四、新特性

dm從内測版本開始,每一個版本的疊代都修複和新加入了不少功能,這裡我單獨拎出來1.0.5這個版本,因為從這個版本開始,它支援了以前從來沒有但又讓人早就期盼已久的功能:online ddl的支援。

衆所周知,MySQL在資料量大了後,沒有人會直接去對原表進行alter,大多數人都通過第三方工具pt-online-schema-change或者gh-ost來進行改表,以此削減改表期間對線上業務的影響。而早期的dm不支援該功能時,每次上遊改完表後,由于臨時表(_xxxx_new,_xxx_gho等)不在白名單裡,會直接導緻下遊tidb丢失該DDL,引發同步異常報警。當然,如果你是全庫同步,那自然不會有這個問題,但絕大多數場景下都是部分表導入到TiDB,用到了白名單功能的情況下就會導緻該問題出現。而1.0.5版本後,這便不再是問題,雖然目前僅僅隻能同時支援一種改表工具,但對比之前來說,無疑是我認為最好的改進,還沒用的朋友們不妨試一試。

例如:

上遊通過pt-online-schema-change工具為表helei5新增一列

–alter=“ADD column hl2 varchar(10) not null default ‘’;” D=h_2,t=helei5

先看下沒有配置跳過的情況

​​

DM運維踩坑實踐總結

​​

幾個關鍵的詞:

skip event 因為不在白名單中被跳過
skip event, need handled ddls is empty ,中間表因為被過濾掉在下遊不存在,
是以提示is empty,也被跳過RENAME TABLE `h_2`.`helei5` TO `h_2`.`_helei5_old`",
"RENAME TABLE `h_2`.`_helei5_new` TO `h_2`.`helei5`
rename操作在上遊為了保證原子性是一條SQL實作表名互換的,
我們可以看到,好在拆分後也依舊是被跳過的,這是因為中間表不存在
rename的ddl裡部分表例如_helei5_new是空的,是以整個SQL不會被執行
"RENAME TABLE `h_2`.`helei5` TO `h_2`.`_helei5_old`的話被執行了是我們不希望看到的      

此時task狀态依舊是running,但下遊已經沒有新增的列

{
            "taskName": "task_4369",
            "taskStatus": "Running",
            "workers": [
                “192.168.1.248:8262"
            ]
 
此時插入包含新列h2的資料
mysql> insert into helei5 values(14,'cccc','pt alter');      

這個時候dm就會報錯

{
            "taskName": "task_4369",
            "taskStatus": "Error - Some error occurred in subtask. Please run `query-status task_4369` to get more details.",
            "workers": [
                “192.168.1.248:8262"
            ]

報錯資訊是:
msg": "[code=36027:class=sync-unit:scope=internal:level=high] current pos (4369-binlog|000001.000021, 62771055): gen insert sqls failed, schema: h_2, table: helei5: Column count doesn't match value count: 2 (columns) vs 3 (values)
 
點位:
sync": {
                        "totalEvents": "3",
                        "totalTps": "0",
                        "recentTps": "0",
                        "masterBinlog": "(4369-binlog.000021, 62774081)",
                        "masterBinlogGtid": "1c3add9b-7c26-11e7-81bf-70e28411103e:1-910975,1d1872fd-7c26-11e7-81bf-70e284110e52:1-3,200ccab3-f941-11e8-b6de-6c92bf96384c:1-800846",
                        "syncerBinlog": "(4369-binlog|000001.000021, 62765733)",      

因為少列,是以報錯,我們在下遊添加列,然後跳過

mysql> alter table helei5 add column h2 varchar(10) not null default '';
跳過語句是:
» sql-skip --worker=192.168.1.248:8262 --binlog-pos=4369-binlog|000001.000021:62765733 task_4369
{
    "result": true,
    "msg": "",
    "workers": [
        {
            "result": true,
            "worker": "",
            "msg": ""
        }
    ]
}      

dm-worker的日志有如下内容:

[2020/05/14 15:48:05.883 +08:00] [INFO] [operator.go:136] ["set a new operator"] [task=task_4369] [unit="binlog replication"] ["new operator"="uuid: b52784e4-e804-46f5-974c-ca811a34dc30, pos: (4369-binlog|000001.000021, 62765733), op: SKIP, args: "]      

執行恢複任務

» resume-task task_4369  
 
        {
            "taskName": "task_4369",
            "taskStatus": "Running",
            "workers": [
                “192.168.1.248:8262"      

在添加online-ddl-scheme: "pt"參數後,下遊成功讀取到pt-online-schama-change加的新列:

​​

DM運維踩坑實踐總結

​​

gh-ost工具同理:

幾個關鍵詞:
ghc表的建立DM是忽略的:
prepare to handle ddls
skip event, need handled ddls is empty
gho表的建立和新DDL也是忽略的,而是把該 DDL 記錄到 dm_meta.{task_name}\"_onlineddl 以及記憶體中:
prepare to handle ddls
skip event, need handled ddls is empty
del表的建立也是忽略的:
rename /* gh-ost */ table `h_2`.`helei5` to `h_2`.`_helei5_del`, `h_2`.`_helei5_gho` to `h_2`.`helei5`
不執行 rename to _helei5_del。當要執行 rename ghost_table to origin table 的時候,并不執行 rename 語句,而是把記錄在記憶體中的 DDL 讀取出來,然後把 ghost_table、ghost_schema 替換為 origin_table 以及對應的 schema,再執行替換後的 DDL。
操作完日志會有:
finish online ddl and clear online ddl metadata in normal mode      

在添加online-ddl-scheme: "gh-ost"的參數後,下遊也讀取到了加的列

​​

DM運維踩坑實踐總結

​​

五、1062的踩坑

在早期,我們有一個業務通過DM同步到TiDB,但每過幾個小時後,同步總是中斷,而每次我們人工resume task又能恢複,咨詢後得知resume操作後,dm内部前幾分鐘是debug模式,執行的replace。

當時的報錯内容如下:

>> query-status task_3306
...
...
 "msg": "[code=10006:class=database:scope=not-set:level=high] execute statement failed: commit: Error 1062: Duplicate entry '21277ed5f5e7c3b646a5229269d54d3a7fccc08bf34c8f2113fdd4df62f4a229' for key 'clientid'\"ngithub.com/pingcap/dm/pkg/terror.(*Error).Delegate\"n
>>query-error task_3306
"errorSQL": "[tp: insert, sql: INSERT INTO `360sudi`.`client` (`id`,`clientid`,`toid`,`updatetime`) VALUES (?,?,?,?);, args: [4102315138 62ba2a78af090ab1337c6b62f8dedfc8b6338007cdeb5a7ea4e7f4b4c23e3e0c 3250350869 2019-11-21 19:54:56], key: 4102312246, ddls: []      

用自增id的主鍵查:

[helei@db01 ~]$ curl http://192.168.1.1:10080/mvcc/key/360/client/4102315138
{
 "key": "7480000000000011945F7280000000F4845C82",
 "region_id": 2278436,
 "value": {
  "info": {
   "writes": [
    {
     "type": 3,
     "start_ts": 412703076417274370,
     "commit_ts": 412703076417274370
    }
   ]
  }
 }
}      

根據查詢出來的 commit-ts 使用 pd-ctl tso 指令看下在下遊已經存在的記錄送出的時間

[root@tidb helei]# /data1/tidb-ansible-3.0.5/resources/bin/pd-ctl -i -u http://192.168.1.2:2379     
» tso
Usage: tso <timestamp>
» tso 412703076417274327
system:  2019-11-21 19:54:57.124 +0800 CST
logic:  471
» tso 412702903507091560
system:  2019-11-21 19:43:57.524 +0800 CST
logic:  104
»        

查找 dm-worker 日志中跟這個記錄相關的内容

-binlog.000020, 185499141), relay-binlog-gtid = "]
[2019/11/21 19:54:47.710 +08:00] [INFO] [syncer.go:2004] ["binlog replication progress"] [task=task_3306] [unit="binlog replication"] ["total binlog size"=378940822] ["last binlog size"=378533105] ["cost time"=30] [bytes/Second=13590] ["unsynced binlog size"=0] ["estimate time to catch up"=0]
[2019/11/21 19:54:47.710 +08:00] [INFO] [syncer.go:2029] ["binlog replication status"] [task=task_3306] [unit="binlog replication"] [total_events=1528442] [total_tps=1455] [tps=40] [master_position="(3306-binlog.000020, 185616633)"] [master_gtid=] [checkpoint="(3306-binlog|000001.000020, 185616633)(flushed (3306-binlog|000001.000020, 185484711))"]
[2019/11/21 19:54:57.157 +08:00] [ERROR] [db.go:269] ["execute statements failed after retry"] [task=task_3306] [unit="binlog replication"] [queries="[INSERT INTO `360sudi`.`client` (`id`,`clientid`,`toid`,`updatetime`) VALUES (?,?,?,?);]"] [arguments="[[4102315138 62ba2a78af090ab1337c6b62f8dedfc8b6338007cdeb5a7ea4e7f4b4c23e3e0c 3250350869 2019-11-21 19:54:56]]"] [error="[code=10006:class=database:scope=not-set:level=high] execute statement failed: commit: Error 1062: Duplicate entry '62ba2a78af090ab1337c6b62f8dedfc8b6338007cdeb5a7ea4e7f4b4c23e3e0c' for key 'clientid'"]      

比較下第二個步驟查詢出來的時間和第三個步驟查詢出來的時間

第二個步驟 2019-11-21 19:54:57.124 +0800 CST

第三個步驟 2019/11/21 19:54:57.157 +08:00

第二個步驟2019-11-21 19:43:57.524的時間,在第三個步的worker日志裡未找到相關

我們嘗試過:

1.将dm下遊配置為單一tidb而非tidb的lvs,防止多個ip寫入ntp引起毫秒級的誤差(無果)

2.task檔案針對syncer的worker進行限制,隻讓一個syncer進行同步(無果)

3.業務變更為replace into,開始測試

改syncer worker count的時候,光rolling_update是沒用的,需要dm-ctl去stop/start task才可以生效

可以看到,15:25起,qps在syncer worker count配置為16後瞬間從256漲到4k

​​

DM運維踩坑實踐總結

​​

限制worker,庫裡也有多個而非一個程序\

​​

DM運維踩坑實踐總結

​​

而單個程序依然會報錯1062不說,還會導緻延遲不斷增加,之後我們又調整會16後,能看到快速追上上遊主庫日志

DM運維踩坑實踐總結

最終我們是通過業務更改replace into解決該問題

六、DM大批量導入調參

叢集穩定運作旗艦,有新資料要通過DM灌入,此時會影響已有叢集的穩定性

如下圖所示,能看到DM導入期間叢集響應出現延遲

​​

DM運維踩坑實踐總結

​​

我們通過如下參數從原值調到-新值規避了這一問題,但每個叢集場景和配置完全不同,适度謹慎調整,調整前應了解每一個參數的含義,如下僅做參考

raftstore:
apply-pool-size: 3-4
store-pool-size: 3-4

storage:
scheduler-worker-pool-size: 4-6

server:
grpc-concurrency: 4-6

rocksdb:
max-background-jobs: 8-10
max-sub-compactions: 1-2      
七、限制

版本限制:

  • 資料庫版本
  • 5.5 < MySQL 版本 < 8.0
  • MariaDB 版本 >= 10.1.2
  • 僅支援 TiDB parser 支援的 DDL 文法
  • 上下遊 sql_model 檢查
  • 上遊開啟 binlog,且 binlog_format=ROW

DM不支援的類型:

1)一次删除多個分區的操作則會報錯:

alter table dsp_group_media_report drop partition p202006 ,p202007 ;

2)drop含有索引的列操作會報錯

Alter table dsp_group drop column test_column;

DM-portal限制:

●在早期還沒有dm-portal自動化生成task時,我們都是自行編寫DM的task同步檔案

●後來有了dm-portal自動化生成工具,隻要圖形頁面點點點就可以了

DM運維踩坑實踐總結

但該工具目前有一個問題是,沒有全庫正則比對,幾遍你隻勾選一個庫,他底層是預設把每張表都給你配置一遍。這就會出現當上層MySQL新建立某張表的時候,下遊會被忽略掉,例如當你使用改表工具gh-ost或者pt-online-schema-change,你的臨時表都會被當做為不在白名單内而被忽略,這個問題使用者需要注意。我們也已經回報給了官方。且如文章第四節所示,已于1.0.5版本修複。

DM-worker清理配置:

[purge]
interval = 3600
expires = 7
remain-space = 15      

關于relay-log,預設是不清理的,就和mysql的expire_logs_days一樣,這塊可以通過dm-worker的配置檔案來進行配置,例如将expires配置為7,代表7天後删除:

#預設expires=0,即沒有過期時間,而remain-space=15意思是當磁盤隻剩于15G的時候開始嘗試清理,這種情況我們極少會碰到,是以這個清理方式其實基本上是用不到的。是以建議有需要删除過期relay-log的小夥伴,直接配置expires保留天數就可以了。

DM導入完成後,應該提供是否在完成後自動删除全備檔案的選項,可以預設不删,由使用者決定是否删除。

從使用者角度來說,全量備份目錄無論是全量一次性導入還是all增量同步,後續都不會再使用到。如果dm-worker和tikv混部,會導緻全備檔案占據大量磁盤空間,引起tikv region評分出現異常,導緻性能下降,這一點如果有相同的架構的朋友們需得注意。

預設排程政策是當磁盤剩餘的有效空間不足 40% ,處于中間态時則同時考慮資料量和剩餘空間兩個因素做權重和當作得分,當得分出現比較大的差異時,就會開始排程。

上一篇: 再談王垠