一 序言mysql
在運維MySQL數據庫時,DBA會接收到比較多關於主備延時的報警:算法
check_ins_slave_lag (err_cnt:1)critical-slavelag on ins:3306=39438sql
相信slave延遲是MySQL DBA遇到的一個老生常談的問題了。咱們先來分析一下slave延遲帶來的風險:數據庫
本文主要探討如何解決 ,如何規避slave延遲的問題,接下來咱們要分析一下致使備庫延遲的幾種緣由。緩存
二 slave延遲的場景以及解決方法多線程
有以下特徵:併發
a. show slave status 顯示position一直沒有變
b. show open tables 顯示某個表一直是 in_use 爲 1
c. show create table 查看錶結構能夠看到無主鍵,或者無任何索引,或者索引區分度不好。app
解決方法:運維
a. 找到表區分度比較高的幾個字段, 可使用這個方法判斷:
select count(*) from xx;
select count(*) from (select distinct xx from xxx) t;
若是2個查詢count(*)的結果差很少,說明能夠對這些字段加索引
b. 備庫stop slave;
可能會執行比較久,由於須要回滾事務。
c. 備庫
set global slave_rows_search_algorithms='TABLE_SCAN,INDEX_SCAN,HASH_SCAN';
或者
set sql_log_bin=0;
alter table xx add key xx(xx);
d. 備庫start slave
若是是innodb,能夠經過show innodb status來查看 rows_inserted,updated,deleted,selected這幾個指標來判斷。
若是每秒修改的記錄數比較多,說明覆制正在以比較快的速度執行。異步
其實針對無索引的表,能夠直接調整從庫上的參數slave_rows_search_algorithms。
現象解析binlog發現相似於下圖的狀況看:
解決方法:
此類緣由的主要現象是數據庫的IUD操做很是多,slave因爲sql_thread單線程的緣由追不上主庫。
解決方法:
a 升級從庫的硬件配置,好比ssd,fio.
b 使用@丁奇的預熱工具-relay fetch
在備庫sql線程執行更新以前,預先將相應的數據加載到內存中,並不能提升sql_thread線程執行sql的能力,
也不能加快io_thread線程讀取日誌的速度。
c 使用多線程複製 阿里MySQL團隊實現的方案--基於行的並行複製。
該方案容許對同一張表進行修改的兩個事務並行執行,只要這兩個事務修改了表中的不一樣的行。
這個方案能夠達到事務間更高的併發度,可是侷限是必須使用 Row格式的binlog。
由於只有使用Row格式的binlog才能夠知道一個事務所修改的行的範圍,而使用Statement格式的binlog只能知道修改的表對象。
因爲xtrabackup工具有份到最後會執行flash tables with read lock ,對數據庫進行鎖表以便進行一致性備份,而後對於myisam表鎖,會阻礙salve_sql_thread停滯運行進而致使hang。
該問題目前的比較好的解決方式是修改表結構爲innodb存儲引擎的表。
三 MySQL的改進
爲了解決複製延遲的問題,MySQL也在竭盡全力的解決主從複製的性能瓶頸,研發高效的複製算法。
MySQL的複製機制大體原理是:slave經過io_thread 將主庫的binlog拉到從庫並寫入relay log,由SQL thread讀出來relay log並進行重放。當主庫寫入併發寫入壓力很大,也即N:1的情形,slave就可能會出現延遲。MySQL 5.6版本提供並行複製功能,slave複製相關的線程由io_thread,coordinator_thread,worker構成,其中:
須要注意的是dbname與worker線程的綁定信息在一個hash表中進行維護,hash表以entry爲單位,entry中記錄當前entry所表明的數據庫名,有多少個事務相關的已被分發,執行這些事務的worker thread等信息。
分配線程是以數據庫名進行分發的,當一個實例中只有一個數據庫的時候,不會對性能有提升,相反,因爲增長額外的操做,性能還會有一點回退。
MySQL 5.7版本提供基於組提交的並行複製,經過設置以下參數來啓用並行複製。
slave_parallel_workers>0
global.slave_parallel_type='LOGICAL_CLOCK'
即主庫在ordered_commit中的第二階段,將同一批commit的 binlog打上一個相同的seqno標籤,同一時間戳的事務在備庫是能夠同時執行的,所以大大簡化了並行複製的邏輯,並打破了相同DB不能並行執行的限制。備庫在執行時,具備同一seqno的事務在備庫能夠並行的執行,互不干擾,也不須要綁定信息,後一批seqno的事務須要等待前一批相同seqno的事務執行完後才能夠執行。這樣的實現方式大大提升了slave應用relaylog的速度。
#默認是DATABASE模式的,須要調整或者在my.cnf中配置
mysql> show variables like 'slave_parallel%';
+++
| Variable_name | Value |
+++
| slave_parallel_type | DATABASE |
| slave_parallel_workers | 4 |
+++
2 rows in set (0.00 sec)
mysql> STOP SLAVE SQL_THREAD;
Query OK, 0 rows affected (0.00 sec)
mysql> set global slave_parallel_type='LOGICAL_CLOCK';
Query OK, 0 rows affected (0.00 sec)
mysql> START SLAVE SQL_THREAD;
Query OK, 0 rows affected (0.01 sec)
啓用並行複製以後查看processlist,系統多了四個線程Waiting for an event from Coordinator (手機用戶推薦橫屏查看)
mysql> show processlist;
| Id | User | Host | db | Command | Time | State | Info |
| 9 | root | localhost:40270 | NULL | Query | 0 | starting | show processlist |
| 10 | system user | | NULL | Connect | 1697 | Waiting for master to send event | NULL |
| 31 | system user | | NULL | Connect | 5 | Slave has read all relay log; waiting for more updates | NULL |
| 32 | system user | | NULL | Connect | 5 | Waiting for an event from Coordinator | NULL |
| 33 | system user | | NULL | Connect | 5 | Waiting for an event from Coordinator | NULL |
| 34 | system user | | NULL | Connect | 5 | Waiting for an event from Coordinator | NULL |
| 35 | system user | | NULL | Connect | 5 | Waiting for an event from Coordinator | NULL |
7 rows in set (0.00 sec)
核心參數:
其實從MySQL5.7.22就提供基於write set的複製優化策略。WriteSet並行複製的思想是:不一樣事務的不一樣記錄不重疊,則均可在從機上並行回放,能夠看到並行的力度從組提交細化爲記錄級。不想看官方文檔的話,你們能夠看看姜老師的文章:
速度提高5~10倍,基於WRITESET的MySQL並行複製
廢話很少說,直接上性能壓測圖:
四 總結
slave延遲的緣由能夠歸結爲slave apply binlog的速度跟不上主庫寫入的速度,如何解決複製延遲呢?其實也是如何提升MySQL寫速度的問題。從目前的硬件和軟件的發展來看,硬件存儲由以前的HDD機械硬盤發展到如今的SSD,PCI-E SSD,再到NVM Express(NVMe),IO性能一直在提高。MySQL的主從複製也從單線程複製到不一樣算法的並行複製(基於庫,事務,行),應用binlog的速度也愈來愈快。
本文概括從幾個常見的複製延遲場景,有可能還不完整,也歡迎你們留言討論。
五 拓展閱讀
[1] 一種MySQL主從同步加速方案-改進
[2] MySQL多線程同步MySQL-Transfer介紹(已經不在維護)
[3] MySQL並行複製演進及MySQL 8.0中基於WriteSet的優化
[4] 速度提高5~10倍,基於WRITESET的MySQL並行複製
[5] https://mysqlhighavailability.com/improving-the-parallel-applier-with-writeset-based-dependency-tracking/