作者:莫善
某網際網路公司高階 DBA。
本文來源:原創投稿
*愛可生開源社群出品,原創內容未經授權不得隨意使用,轉載請聯絡小編並註明來源。
一、前言
線上tidb叢集都是2.1.[5,7,8,17],因版本太低,面臨諸多問題,比如管理難度大,熱點問題,執行計劃失效,效能瓶頸,其他已知/未知且無法解決的問題,現在需要升級至4.0.13版本。在調研後發現,如果原地升級將需要多次升級【2.1--> 3.0 --> 4.0】,擔心原地升級遇到不可逆的故障,更擔心的是解決不掉而影響業務,所以經過測試和評估,最終採用資料遷移的方式進行升級。
因為使用2.1版本的使用者本身比較少,更別提升級了,所以可參考的遷移升級文件幾乎沒有,在升級中遇到了很多問題,也踩了很多坑,本文整理了升級操作流程,並標記每個步驟容易遇到什麼問題及解決方案,權當經驗交流,避坑指南。本文所有內容/操作命令僅供參考。
- 因5.0基於MySQL 8.0協議,擔心和業務不相容,也因為5.0+的小版本都還比較小,擔心穩定性,所以就不考慮了,當時4.0.13是4.0最新的版本,就選了這個版本。
- 已經有24套tidb叢集完成了從2.1到4.0.13的升級。
二、環境介紹
1、舊叢集環境介紹
- 已有的元件
角色 | 數量 | 埠 |
---|---|---|
pd | 3 | 5017 |
tidb | 3 | 4000 |
tikv | 3 | 20117 |
alertmanager | 1 | 9093 |
prometheus | 1 | 9100 |
grafana | 1 | 3000 |
vip | 192.168.1.100 | 4000 |
dns | old.tdb.com | 4000 |
- 未列舉的元件表示未啟用該元件,因歷史原因,叢集並沒有啟用pump元件。
- 埠規劃也沒什麼規律
- 預計增加的元件
角色 | 數量 | 埠 |
---|---|---|
pump | 3 | 23001 |
drainer | 1 | 24001 |
2、舊叢集訪問資訊
dns | old.tdb.com | ||
---|---|---|---|
vip | 192.168.1.100:4000 | rs : 192.168.1.1:4000 192.168.1.2:4000 192.168.1.3:4000 |
3、新叢集環境介紹
角色 | 數量 | 埠 |
---|---|---|
pd | 3 | 13002 |
tidb | 3 | 15002 |
tikv | 3 | 17002 |
ticdc | 3 | 33002 |
alertmanager | 1 | 21002 |
prometheus | 1 | 19002 |
grafana | 1 | 20002 |
vip | 192.168.1.100 | 15002 |
dns | new.tdb.com | 15002 |
- 埠採用2+3的格式,前兩位是元件編號,後三位表示叢集編號。即後三位一樣的表示同一個叢集,前兩位一樣表示同一個元件。
4、新叢集訪問資訊
dns | new.tdb.com | ||
---|---|---|---|
vip | 192.168.1.100:15002 | rs : 192.168.1.1:15002 192.168.1.2:15002 192.168.1.3:15002 |
三、流程介紹
- 1、dba 列印當前連線tidb的ip列表讓主業務方確認是否存在非本業務的ip。確保所有使用該叢集的業務都參與進來。
- 2、dba 跟業務確認是否有重連機制。(開啟binlog需要重啟tidb元件)。
- 3、dba 開啟binlog,這步需要滾動重啟tidb元件,需要跟業務協商一個時間視窗。
- 4、dba 部署4.0環境並匯入全量資料。
- 5、dba 同步增量資料。
- 6、dba 校驗新舊叢集資料一致性。
- 7、dba 交付新環境,提供新的域名 + 埠。
- 8、dba 提供只讀賬戶,業務測試,驗證業務場景(僅限讀,不能寫)。
- 9、dba 同步許可權。
- 10、切換流量。
四、升級操作
1、列印舊叢集訪問列表
ansible # /opt/soft/mysql57/bin/mysql -u root -h old.tdb1 -P 4000 -ppassword
mysql> select distinct host from information_schema.processlist
ansible # /opt/soft/mysql57/bin/mysql -u root -h old.tdb2 -P 4000 -ppassword
mysql> select distinct host from information_schema.processlist
ansible # /opt/soft/mysql57/bin/mysql -u root -h old.tdb3 -P 4000 -ppassword
mysql> select distinct host from information_schema.processlist
登入所有tidb節點,每個節點的輸出結果追加到一個檔案,然後排序去重進行統計客戶端ip
2、確認是否有重連機制
略
3、開啟binlog並全量備份
這步操作在ansible管理機執行
(1)編輯配置檔案
ansible # vim /path/github/tidb-ansible-2.1.8/inventory.ini
新增pump元件的監控
[monitored_servers] monitor-pump1 ansible_host=xxxx deploy_dir=/path/tidb-data/pump-23001 monitor-pump2 ansible_host=xxxx deploy_dir=/path/tidb-data/pump-23001 monitor-pump3 ansible_host=xxxx deploy_dir=/path/tidb-data/pump-23001
- 新增pump元件
[pump_servers]
#下面三個是pump元件的機器, 如果啟用pump元件還需要開啟 enable_binlog = True
pump1 ansible_host=xxxx deploy_dir=/path/tidb-data/pump-23001
pump2 ansible_host=xxxx deploy_dir=/path/tidb-data/pump-23001
pump3 ansible_host=xxxx deploy_dir=/path/tidb-data/pump-23001
- pump埠設定及啟用binlog引數
## Global variables
[all:vars]
pump_port = 23001
## binlog trigger
enable_binlog = True
#enable_binlog = False
如果不設定enable_binlog = True,在部署pump的時候會被忽略。另外需要注意,在pump能提供服務前,不能重新載入tidb的配置並重啟,否則會導致業務寫操作失敗。
(2)編輯pump的配置
ansible # vim /path/github/tidb-ansible-2.1.8/conf/pump.yml
- 修改binlog儲存週期
global:
# a integer value to control expiry date of the binlog data, indicates for how long (in days) the binlog data would be stored.
# must bigger than 0
gc: 14
改成14天,避免全量資料匯入時間過長導致增量資料丟失(binlog被清理)。
(3)登入目標機器建立目錄
登入各個pump節點建立目錄及更改許可權
ansible # ssh pump1
pump1 # mkdir -p /path/tidb-data/pump-23001
pump1 # chown -R tidb. /path/tidb-data/pump-23001
ansible # ssh pump2
pump2 # mkdir -p /path/tidb-data/pump-23001
pump2 # chown -R tidb. /path/tidb-data/pump-23001
ansible # ssh pump3
pump3 # mkdir -p /path/tidb-data/pump-23001
pump3 # chown -R tidb. /path/tidb-data/pump-23001
(4)在ansible管理機部署pump及監控
ansible # ansible-playbook deploy.yml -l monitor-pump1,monitor-pump2,monitor-pump3,pump1,pump2,pump3 -i inventory.ini
(5)在ansible管理機啟動pump及監控
ansible # ansible-playbook start.yml -l monitor-pump1,monitor-pump2,monitor-pump3,pump1,pump2,pump3 -i inventory.ini
(6)登入tidb檢視pump是否部署完成
ansible # /opt/soft/mysql57/bin/mysql -u root -h old.tdb.com -P 4000 -ppassword
mysql> show pump status;
+------------+------------+--------+--------------------+---------------------+
| NodeID | Address | State | Max_Commit_Ts | Update_Time |
+------------+------------+--------+--------------------+---------------------+
| xxxx:23001 | xxxx:23001 | online | 427138948355850245 | 2021-08-20 04:42:57 |
| xxxx:23001 | xxxx:23001 | online | 427138948395171844 | 2021-08-20 04:42:57 |
| xxxx:23001 | xxxx:23001 | online | 427138948408279045 | 2021-08-20 04:42:57 |
+------------+------------+--------+--------------------+---------------------+
3 rows in set (0.00 sec)
mysql>
需要注意,<font color='red'>2.1.6之前的版本不支援這個查詢操作,需要通過binlogctl 進行檢視pump的狀態,如下示例。</font>
ansible # /path/binlogctl -pd-urls=http://pd_host:pd_port -cmd pumps
INFO[0000] pump: {NodeID: xxxx:23001, Addr: xxxx:23001, State: online, MaxCommitTS: 432180280017551379, UpdateTime: 2021-08-20 04:45:57 +0800 CST}
INFO[0000] pump: {NodeID: xxxx:23001, Addr: xxxx:23001, State: online, MaxCommitTS: 432180280004444167, UpdateTime: 2022-03-30 18:45:14 +0800 CST}
INFO[0000] pump: {NodeID: xxxx:23001, Addr: xxxx:23001, State: online, MaxCommitTS: 432180280017551372, UpdateTime: 2022-03-30 18:45:14 +0800 CST}
(7)在ansible管理機滾動重啟tidb節點
<font color='red'>執行這個操作前,一定要確保pump元件正常執行。</font>
ansible # ansible-playbook rolling_update.yml -t tidb -i inventory.ini
需要注意的是,這個操作可能會出現ansible啟動或者關閉動作失敗(一直卡著直到超時),如果碰到這種情況,可以登入到目標機器手動進行啟動或者停止。參考命令如下:
- 啟動 cd /path/tidb/scripts && sudo -u tidb bash start_tidb.sh
- 停止 cd /path/tidb/scripts && sudo -u tidb bash stop_tidb.sh
(8)登入tidb檢查binlog是否已經開啟
ansible # /opt/soft/mysql57/bin/mysql -u root -h old.tdb.com -P 4000 -ppassword
mysql> show variables like 'log_bin';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_bin | 1 |
+---------------+-------+
1 row in set (0.01 sec)
- 需要注意,<font color='red'>2.1.6之前的版本 log_bin 恆等於 0</font>,就是說即便enable_binlog = True,通過show variables like 'log_bin';查出來的也是0,但是pump會記錄binlog。
- 建議挨個tidb都檢查一遍。
(9)在ansible管理機更新監控
ansible # ansible-playbook rolling_update_monitor.yml -t prometheus -i inventory.ini
(10)建立全量備份
ansible # /path/mydumper -u user -p pass -h old.tdb.com -P 4000 -t 2 -F 32 --skip-tz-utc -o /backup_path/4000 -B db_name
備份需要注意:
- 工具獲取 https://docs.pingcap.com/zh/t...
- 在業務低峰進行備份,否則可能會出現網路卡打滿的情況(尤其是tidb是萬兆網路卡,tikv是千兆網路卡的架構)
- 可能會因為gc時間過短導致備份失敗(通過調整gc時間解決)
- 可能因為tidb分配的記憶體過小導致備份失敗(通過調整tidb記憶體解決)
- 備份完成後建議檢查一下建表語句的檔案,是否存在非法時間格式("0000-00-00"),如果存在在匯入新叢集的時候會報錯,需要跟業務溝通一下變更預設值。
- mydumper不支援限流備份,可以通過備份到磁碟效能很差的機器或者cfs這種網路儲存,在一定程度上實現了限流備份。
4、部署4.0環境並匯入全量資料
悲觀事務模型需要關注一下,<font color='red'>4.0雖然支援悲觀事務模型,而且新建叢集預設也是開啟狀態,但是要想一個操作用到悲觀鎖,還是有一定的限定條件的,即非autocommit 的事務。具體請參考這個文章的【6.2.3.2部分】</font> https://book.tidb.io/session1...
(1)安裝tiup
ansible # curl --proto '=https' --tlsv1.2 -sSf https://tiup-mirrors.pingcap.com/install.sh | sh
ansible # . /root/.bash_profile
ansible # tiup --version
ansible # tiup update --self
(2)準備拓撲檔案
ansible # vim topology-oltp-xxx.yaml
global:
user: "tidb"
ssh_port: 22
deploy_dir: "/tidb-deploy"
data_dir: "/tidb-data"
monitored:
node_exporter_port: 11000
blackbox_exporter_port: 12000
pd_servers:
- host: 10.0.1.4
- host: 10.0.1.5
- host: 10.0.1.6
tidb_servers:
- host: 10.0.1.1
- host: 10.0.1.2
- host: 10.0.1.3
tikv_servers:
- host: 10.0.1.7
- host: 10.0.1.8
- host: 10.0.1.9
cdc_servers:
- host: 10.0.1.7
- host: 10.0.1.8
- host: 10.0.1.9
monitoring_servers:
- host: 10.0.1.10
grafana_servers:
- host: 10.0.1.10
alertmanager_servers:
- host: 10.0.1.10
以上是官方提供的配置模板,請根據實際情況修改。
- 建議部署ticdc(pump),避免需要回滾的時候可追溯增量資料。
- 建議每個元件單獨一臺機器。
(3)檢查tiup 管理機到各個節點的ssh通道是否正常
略
(4)部署叢集
ansible # tiup cluster check tidb-oltp-xxx-v4.0.13 v4.0.13 topology-oltp-xxx.yaml
ansible # tiup cluster deploy tidb-oltp-xxx-v4.0.13 v4.0.13 topology-oltp-xxx.yaml
ansible # tiup cluster start tidb-oltp-xxx-v4.0.13
ansible # tiup cluster display tidb-oltp-xxx-v4.0.13
- check可能會報很多異常,可以根據提示進行修復,很多異常也可以忽略。請參考 https://docs.pingcap.com/zh/t...
(5)許可權維護
ansible # /opt/soft/mysql57/bin/mysql -u root -h new.tdb.com -P 15002 -p
mysql> create user if not exists root@"192.168.1.%" IDENTIFIED BY 'password';
mysql> GRANT ALL PRIVILEGES ON *.* TO 'root'@'192.168.1.%' WITH GRANT OPTION;
mysql> GRANT ALL PRIVILEGES ON *.* TO 'root'@'pd1' WITH GRANT OPTION;
mysql> GRANT ALL PRIVILEGES ON *.* TO 'root'@'pd2' WITH GRANT OPTION;
mysql> GRANT ALL PRIVILEGES ON *.* TO 'root'@'pd3' WITH GRANT OPTION;
- 這裡用空密碼就能登入。
- 這裡需要加上pd節點的授權,而且要求是root使用者(還是給all許可權吧,測試發現給select許可權不行,沒做更細緻的許可權測試),否則dashboard不能正常使用。
ansible # /opt/soft/mysql57/bin/mysql -u root -h new.tdb.com -P 15002 -ppassword
mysql> drop user if exists root@"%";
建議刪除root@'%'這個空密碼使用者。
(6)匯入全量資料
ansible # /path/loader -d /backup_path/4000 -h new.tdb.com -u user -p password -P 15002 -t 2 -status-addr ":9299"
恢復需要注意:
- 工具獲取 https://docs.pingcap.com/zh/t...
- 建議在業務低峰進行恢復
- 可能會因為表情符導致loader失敗,如果遇到,可以試試Dumpling
- 多個loader任務的場景,建議避開預設埠,否則可能會因為埠衝突導致失敗
5、同步增量資料
這步操作在ansible管理機執行
(1)在備份機獲取備份點位(本例使用ansible管理機進行備份)
從備份目錄檢視metadata檔案
ansible # cd /backup_path/xxx
ansible # cat metadata
Started dump at: 2021-08-29 15:34:30
SHOW MASTER STATUS:
Log: tidb-binlog
Pos: 425971435565482001
GTID:
Finished dump at: 2021-08-29 15:34:33
ansible #
(2)修改配置檔案
ansible # vim /path/github/tidb-ansible-2.1.8/inventory.ini
- 新增drainer元件的監控
[monitored_servers]
monitor-drainer1 ansible_host=xxxx deploy_dir=/path/tidb-data/drainer-24001
- 新增drainer元件
[drainer_servers]
drainer1 ansible_host=xxxx deploy_dir=/path/tidb-data/drainer-24001 initial_commit_ts="425971435565482001"
- drainer埠設定
## Global variables
[all:vars]
drainer_port = 24001
(3)準備drainer的配置檔案
ansible # vim /path/github/tidb-ansible-2.1.8/conf/drainer1_drainer.toml
配置檔名命名規則為【別名_drainer.toml】,否則部署時無法找到自定義配置檔案。
# drainer Configuration.
# the interval time (in seconds) of detect pumps' status
detect-interval = 10
# syncer Configuration.
[syncer]
# disable sync these schema
ignore-schemas = "INFORMATION_SCHEMA,PERFORMANCE_SCHEMA,mysql"
# number of binlog events in a transaction batch
txn-batch = 2000
# work count to execute binlogs
worker-count = 32
disable-dispatch = false
# safe mode will split update to delete and insert
safe-mode = false
# downstream storage, equal to --dest-db-type
# valid values are "mysql", "pb", "tidb", "flash", "kafka"
db-type = "tidb"
# the downstream MySQL protocol database
[syncer.to]
host = "new.tdb.com"
user = "user"
password = "xxxx"
port = 15002
txn-batch 和 worker-count的配置在配置檔案預設值應該是1,建議根據實際情況改大點,如果太小可能出現增量資料一直追不上的情況。
(4)部署drainer及監控
ansible # ansible-playbook deploy_drainer.yml -i inventory.ini -l drainer1
ansible # ansible-playbook deploy.yml -i inventory.ini -l monitor-drainer1
(5)登入新叢集的tidb,給drainer節點授權
ansible # /opt/soft/mysql57/bin/mysql -u root -h new.tdb.com -P 15002 -ppassword
mysql> create user if not exists user@"drainer_host" IDENTIFIED BY 'xxxx';
mysql> GRANT ALL PRIVILEGES ON *.* TO 'user'@'drainer_host';
注意:新叢集(4.0)要給drainer所在的主機授權,否則啟動drainer將報錯,為了演示方便,這裡直接給了所有許可權
(6)啟動drainer及監控
啟動drainer前建議先確定一下目標庫是否已經存在tidb_binlog庫,如果存在,且又需要從備份的點位開始增量同步,這種情況需要手動刪除一下,要不然drainer會從checkpoint開始同步資料。(一般出現在匯入全量失敗後需要重新匯入全量,然後忘記清理tidb_binlog庫)
ansible # ansible-playbook start_drainer.yml -i inventory.ini -l drainer1
ansible # ansible-playbook start.yml -i inventory.ini -l monitor-drainer1
(7)登入tidb檢查drainer狀態
ansible # /opt/soft/mysql57/bin/mysql -u root -h old.tdb.com -P 4000 -ppassword
mysql> show drainer status;
+------------+------------+--------+--------------------+---------------------+
| NodeID | Address | State | Max_Commit_Ts | Update_Time |
+------------+------------+--------+--------------------+---------------------+
| xxxx:24001 | xxxx:24001 | online | 431972431138127904 | 2021-08-25 16:42:57 |
+------------+------------+--------+--------------------+---------------------+
1 rows in set (0.00 sec)
mysql>
需要注意,<font color='red'>2.1.6之前的版本不支援這個查詢操作,需要通過binlogctl 進行檢視drainer的狀態,如下示例。</font>
ansible # /path/binlogctl -pd-urls=http://pd_host:pd_port -cmd drainers
INFO[0000] drainer: {NodeID: xxxx:24001, Addr: xxxx:24001, State: online, MaxCommitTS: 432180589478543384, UpdateTime: 2021-08-25 16:45:57 +0800 CST}
(8)更新監控
ansible # ansible-playbook rolling_update_monitor.yml -t prometheus -i inventory.ini
(9)登入grafana進行檢視同步進度
注意:如果同步落後比較大,可以在alertmanager將drainer的告警先禁用
6、校驗新舊叢集資料一致性
(1)下載工具
ansible # git clone https://gitee.com/mo-shan/check_data_for_mysql.git
ansible # cd check_data_for_mysql
(2)修改配置
- 編輯配置檔案
ansible # cd /path/check_data_for_mysql
ansible # vim conf/check.conf
mysql_user="xxxx"
mysql_passwd="xxxx"
mysql_port1="6666"
mysql_port2="6666"
mysql_host1="192.168.1.1"
mysql_host2="192.168.1.2"
max_count=10000
threads=5
max_threads_running=30
mysql_path="/opt/soft/mysql57/bin/mysql"
log_partition="/dev/sda3"
log_par_size="10"
skip_check_table=""
skip_check_db="INFORMATION_SCHEMA,METRICS_SCHEMA,PERFORMANCE_SCHEMA,mysql,sys,tidb_binlog,test,tidb_loader,dm_meta" #不建議改
請結合實際情況根據註釋提示進行相關配置
- 修改工作路徑
ansible # sed -i 's#^work_dir=.*#work_dir=\"/check_data_for_mysql_path\"#g' start.sh #將這裡的check_data_for_mysql_path改成check_data_for_mysql的家目錄的絕對路徑
(3)測試用例
- 每次執行校驗任務的時候<font color='red'>強制要清空log目錄,所以請做好校驗結果的備份</font>
- 執行校驗任務的時候<font color='red'>強烈建議開啟screen</font>
- 有網路卡監控需求,<font color='red'>執行監控指令碼時也強烈建議單獨開啟screen進行監控 </font>
第一步:先開啟一個screen監控網路
ansible # screen -S check_net_4000
ansible # bash manager.sh -a start
[ 2022-01-18 11:55:34 ] [ 1000 Mb/s ] [ RX : 2 MB/S ] [ TX : 2 MB/S ]
[ 2022-01-18 11:55:35 ] [ 1000 Mb/s ] [ RX : 2 MB/S ] [ TX : 4 MB/S ]
[ 2022-01-18 11:55:36 ] [ 1000 Mb/s ] [ RX : 2 MB/S ] [ TX : 2 MB/S ]
[ 2022-01-18 11:55:37 ] [ 1000 Mb/s ] [ RX : 2 MB/S ] [ TX : 3 MB/S ]
[ 2022-01-18 11:55:38 ] [ 1000 Mb/s ] [ RX : 1 MB/S ] [ TX : 2 MB/S ]
[ 2022-01-18 11:55:39 ] [ 1000 Mb/s ] [ RX : 1 MB/S ] [ TX : 2 MB/S ]
[ 2022-01-18 11:55:41 ] [ 1000 Mb/s ] [ RX : 1 MB/S ] [ TX : 2 MB/S ]
[ 2022-01-18 11:55:42 ] [ 1000 Mb/s ] [ RX : 2 MB/S ] [ TX : 8 MB/S ]
第二步:新開啟一個screen執行校驗任務
ansible # screen -S check_data_4000
ansible # bash start.sh -d dba -t dbatest1 -f true
[ 2022-01-17 20:32:19 ] [ 成功 ] [ 192.168.1.1 ] [ start.sh/start.sh ] [ f_prepare:130 ] [ 本次資料一致性檢查開始 ]
[ 2022-01-17 20:32:19 ] [ 警告 ] [ 192.168.1.1 ] [ start.sh/start.sh ] [ f_main:185 ] [ 本次資料一致性檢查將檢查如下庫 : [dba] ]
[ 2022-01-17 20:32:19 ] [ 成功 ] [ 192.168.1.1 ] [ start.sh/start.sh ] [ f_main:203 ] [ 正在檢查dba庫 ]
[ 2022-01-17 20:32:19 ] [ 成功 ] [ 192.168.1.1 ] [ func/f_check_diff_for_mysql.sh ] [ f_check_diff_for_mysql:249 ] [ dba.dbatest1 ] [ 表結構一致 ]
[ 2022-01-17 20:32:19 ] [ 成功 ] [ 192.168.1.1 ] [ func/f_check_diff_for_mysql.sh ] [ f_check_diff_for_mysql:491 ] [ dba.dbatest1 ] [ 1,1 ] [ 00 d 00 h 00 m 00 s ] [ 9.09%, (0:0)/1 ] [ 資料一致 ]
[ 2022-01-17 20:32:19 ] [ 成功 ] [ 192.168.1.1 ] [ func/f_check_diff_for_mysql.sh ] [ f_check_diff_for_mysql:491 ] [ dba.dbatest1 ] [ 2,11 ] [ 00 d 00 h 00 m 00 s ] [ 100.00%, (0:0)/1 ] [ 資料一致 ]
[ 2022-01-17 20:32:19 ] [ 成功 ] [ 192.168.1.1 ] [ func/f_check_diff_for_mysql.sh ] [ f_check_diff_for_mysql:504 ] [ dba.dbatest1 ] [ 檢查完畢 ]
[ 2022-01-17 20:32:19 ] [ 成功 ] [ 192.168.1.1 ] [ start.sh/start.sh ] [ f_main:242 ] [ 本次資料一致性檢查完成 ] [ 通過 ]
ansible #
檢查結束後會提示檢查通過,否則就是檢查不通過。
- 工具實現邏輯請參考 https://mp.weixin.qq.com/s/PP...
7、交付新環境
dba提供新的域名和埠給業務,這裡給業務提供一個只讀賬戶即可。
ansible # /opt/soft/mysql57/bin/mysql -u root -h new.tdb.com -P 15002 -ppassword
mysql> create user if not exists read_only@"host" IDENTIFIED BY 'xxxx';
mysql> GRANT SELECT ON *.* TO 'read_only'@'host';
需要注意的是,交付新環境前先不要同步許可權表(mysql.user)。
8、業務驗證
請業務充分驗證。
9、同步許可權表
tidb2.1和4.0的許可權表結構不一致,所以沒法通過匯出匯入的方式同步許可權,另外經過測試使用pt工具也是不行的,下面提供一個同步許可權的指令碼,2.1到4.0版本測試有效,其他版本尚未測試。
#!/bin/bash
port=4000
mysql_comm="/opt/soft/mysql57/bin/mysql -u root -h old.tdb.com -P ${port} -ppassword"
for user in $(${mysql_comm} -NBe "select concat(user,'@','\"',host,'\"',':::',password) from mysql.user;" 2>/dev/null)
do
user_tmp="$(awk -F::: '{print $1}' <<< "${user}")"
pass_tmp="$(awk -F::: '{print $2}' <<< "${user}")"
create_user="create user if not exists ${user_tmp} IDENTIFIED BY PASSWORD '${pass_tmp}';"
drop_user="drop user if exists ${user_tmp};"
grep -q "^root@" <<< "${user_tmp}" && {
grant_user="$(${mysql_comm} -NBe "show grants for ${user_tmp}" 2>/dev/null|sed 's/$/ WITH GRANT OPTION;/g')"
} || {
grant_user="$(${mysql_comm} -NBe "show grants for ${user_tmp}" 2>/dev/null|sed 's/$/;/g')"
echo "${drop_user}"
}
echo "${create_user}"
echo "${grant_user}"
done
該指令碼會將舊叢集的許可權打出來,確認無誤後可以寫到新叢集。
ansible # bash show_grant.sh | /opt/soft/mysql57/bin/mysql -u root -h new.tdb.com -P 15002 -ppassword
- 許可權同步以後,<font color='red'>請業務不要做授權操作</font>,如需授權新主機或新建使用者,找dba協助。
- 非必要<font color='red'>也請業務不要再做ddl操作</font>,如有需求也請dba協助。
10、切換流量
- 業務切流量前,建議將新叢集的tidb挨個重啟一遍,釋放掉auto_increment快取,重啟完畢後需要檢查drainer任務的狀態及延遲,等沒延遲再聯絡業務進行切換。
- 如果不重啟,切到新叢集后<font color='red'>自增列主鍵可能會報大量【Duplicate entry '' for key 'PRIMARY'】。</font>
這個流程其實很簡單,直接將新叢集的tidb主機替換到vip原來 rs列表即可,或者新申請一個vip,將原來的域名解析到新vip。但因歷史問題,原來的域名和tidb埠都不符合管理規範,所以需要業務通過新的域名/埠訪問tidb。
需要注意:將域名解析到新的vip,這種僅對新進來的連線起作用。
鑑於環境的特殊性,dba提供了兩種方案實現讓業務通過新的域名埠訪問tidb。
不管採用哪種方案,在切流量以後都不建議馬上下掉舊域名。推薦的做法是刪除舊域名對應的vip的rs列表,將新叢集的tidb節點掛到舊域名對應的vip的rs列表(需要注意新tidb埠跟舊vip埠可能不一致),這樣做是避免了業務漏切的情況,觀察幾天dns日誌,確認沒業務使用舊域名後再下掉。
(1)野蠻方案
業務直接修改連線資訊,使用 new.tdb.com:15002 來連線tidb。
因可能存在多個業務使用該庫,而且每個服務可能有多臺業務機器,做不到所有服務同一時刻都切到新庫,所以會出現下面幾種情況:
1)寫新庫,讀舊庫會讀不到,因為新庫跟舊庫沒有同步鏈路。
2)寫舊庫,讀新庫,可能會讀不到,因為舊庫跟新庫之間存在延遲。
避免不了雙寫,可能會導致下面的問題。
- A. 更新同一行資料的兩個連線執行的時間極短(小於舊庫到新庫的同步延遲)。兩個連線是分別在舊庫/新庫執行,這時候該行資料的最終狀態不是以誰最後執行為準。比如說,先在舊庫執行了【update t set name = 1 where id = 2;】,然後在新庫執行【update t set name = 2 where id = 2;】,理論上這個資料的記錄最終應該是name=2,但是考慮到新庫到舊庫的同步有延遲,這個資料就可能會被舊庫的資料覆蓋變成name=1。如果反過來,先寫新庫,再寫舊庫,這種情況對資料沒影響。
- B. 業務的兩個連線在新庫舊庫分別插入同一行資料(主鍵一樣或者唯一鍵一樣的資料),如果先寫新庫,再寫舊庫,這樣在業務端都會提交成功,但是會導致舊庫到新庫的同步失敗,因為舊庫寫入的資料同步到新庫就會報主鍵衝突(唯一鍵衝突),這時候就需要dba人工干預進行修復。如果反過來,先寫舊庫再寫新庫(不考慮舊庫到新庫的延遲),這時候寫新庫的會話就會報錯,這種情況對資料沒影響。
針對上述的情況,<font color='red'>需要業務充分評估。如果不能接受,可以建議業務使用下面的平滑方案,這樣影響面較小。</font>
(2)平滑方案
業務繼續使用 old.tdb.com:4000 這個來連線tidb。
dba需要將新叢集的tidb加到舊叢集的vip的rs列表,但是為了避免同時往新舊叢集寫資料,所以應該先將vip的rs先下掉,然後再將新叢集的tidb ip加到vip rs列表。
這裡涉及兩個動作:
- 將舊叢集的vip的rs列表清空(下線rs),這裡建議主動釋放連線(重啟/關閉舊叢集的tidb),要不然可能會出現下掉rs後(具體需要看vip的實現機制),連線不會釋放。
- 將新叢集的tidb的ip加到舊叢集vip 的rs列表。
這兩個操作需要跟業務確認好,因為下掉rs再重新加入有個時間差(預計30s之內),<font color='red'>這過程叢集不可用。</font>
完成上述操作後,舊叢集的訪問資訊會變成如下表:
dns | old.tdb.com | ||
---|---|---|---|
vip | 192.168.1.100:4000 | rs : 192.168.1.1:15002 192.168.1.2:15002 192.168.1.3:15002 |
這時候業務需要挨個更新業務程式碼的配置,將舊域名和埠替換成新域名和埠(需要將 old.tdb.com:4000 替換成 new.tdb.com:15002 ),這時候再修改配置重啟業務影響面會比較小。
五、寫在最後
本文件僅做經驗分享,避坑指南,因使用場景各異,各自環境也不同,在遷移過程中還可能碰上其他問題。<font color='red'>如有線上環境操作需求,請在測試環境充分測試。</font>