處理mysql 主從數據庫延遲 3

一 序言

在運維MySQL數據庫時,DBA會接收到比較多關於主備延時的報警:

check_ins_slave_lag (err_cnt:1)critical-slavelag on ins:3306=39438

相信 slave 延遲是MySQL dba 遇到的一個老生長談的問題了。我們先來分析一下slave延遲帶來的風險

  1. 異常情況下,主從HA無法切換。HA 軟件需要檢查數據的一致性,延遲時,主備不一致。
  2. 備庫複製hang會導致備份失敗(flush tables with read lock會900s超時)
  3. 以 slave 爲基準進行的備份,數據不是最新的,而是延遲。

本文主要探討如何解決 ,如何規避 slave 延遲的問題,接下來我們要分析一下導致備庫延遲的幾種原因。

二 slave延遲的場景以及解決方法

1 無主鍵、無索引或索引區分度不高

有如下特徵

a. show slave status 顯示position一直沒有變
b. show open tables 顯示某個表一直是 in_use 爲 1
c. show create table 查看錶結構可以看到無主鍵,或者無任何索引,或者索引區分度很差。

解決方法:

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,

2 主庫上有大事務,導致從庫延時

現象解析binlog 發現類似於下圖的情況看:

解決方法

事前防範,與開發溝通,增加緩存,異步寫入數據庫,減少業務直接對db的大事務寫入。
事中補救,調整數據庫中io相關的參數比如innodb_flush_log_at_trx_commit和sync_binlog  或者打開並行複製功能。  

3 主庫寫入頻繁,從庫壓力跟不上導致延時

此類原因的主要現象是數據庫的 IUD 操作非常多,slave由於sql_thread單線程的原因追不上主庫。

解決方法:

a 升級從庫的硬件配置,比如ssd,fio.

b 使用@丁奇的預熱工具-relay fetch
  在備庫sql線程執行更新之前,預先將相應的數據加載到內存中,並不能提高sql_thread線程執行sql的能力,
  也不能加快io_thread線程讀取日誌的速度。

c 使用多線程複製 阿里MySQL團隊實現的方案--基於行的並行複製。
該方案允許對同一張表進行修改的兩個事務並行執行,只要這兩個事務修改了表中的不同的行。
這個方案可以達到事務間更高的併發度,但是侷限是必須使用Row格式的binlog。
因爲只有使用Row格式的binlog纔可以知道一個事務所修改的行的範圍,而使用Statement格式的binlog只能知道修改的表對象。

4 大量myisam表,在備份的時候導致slave延遲

由於xtrabackup 工具備份到最後會執行flash tables with read lock ,對數據庫進行鎖表以便進行一致性備份,然後對於myisam表 鎖,會阻礙salve_sql_thread 停滯運行進而導致hang

該問題目前的比較好的解決方式是修改表結構爲innodb存儲引擎的表。

三 MySQL的改進

爲了解決複製延遲的問題,MySQL也在不遺餘力的解決主從複製的性能瓶頸,研發高效的複製算法。

1 基於組提交的並行複製

MySQL的複製機制大致原理是:slave 通過io_thread 將主庫的binlog拉到從庫並寫入relay log,由SQL THREAD 讀出來relay log並進行重放。當主庫寫入併發寫入壓力很大,也即N:1的情形,slave 就可能會出現延遲。MySQL 5.6 版本提供並行複製功能,slave複製相關的線程由io_thread,coordinator_thread,worker構成,其中:

  • coordinator_thread負責讀取 relay log,將讀取的binlog event以事務爲單位分發到各個 worker thread 進行執行,並在必要時執行binlog event,比如是DDL 或者跨庫事務的時候。

     

  • worker_thread:執行分配到的binlog event,各個線程之間互不影響,具體worker_thread 的個數由slave_parallel_workers決定。
    需要注意的是 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打上一個相同的last_committed標籤,同一last_committed的事務在備庫是可以同時執行的,因此大大簡化了並行複製的邏輯,並打破了相同DB不能並行執行的限制。備庫在執行時,具有同一last_committed的事務在備庫可以並行的執行,互不干擾,也不需要綁定信息,後一批last_committed的事務需要等待前一批相同last_committed的事務執行完後纔可以執行。這樣的實現方式大大提高了slave應用relaylog的速度。

mysql> show variables like 'slave_parallel%';
+------------------------+----------+
| Variable_name | Value |
+------------------------+----------+
| slave_parallel_type | DATABASE |#默認是DATABASE 模式的,需要調整或者在my.cnf中配置
| 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)

核心參數:

binlog_group_commit_sync_no_delay_count:一組裏面有多少事物才提交,單位 (ms)
binlog_group_commit_sync_delay:等待多少時間後才進行組提交

3.2 基於寫集合的並行複製

其實從 MySQL5.7.22就提供基於write set的複製優化策略。WriteSet並行複製的思想是:不同事務的不同記錄不重疊,則都可在從機上並行回放,可以看到並行的力度從組提交細化爲記錄級。不想看官方文檔的話,大家可以看看姜老師的文章 https://mp.weixin.qq.com/s/oj-DzpR-hZRMMziq2_0rYg

廢話不多說,直接上性能壓測圖:



四 總結

slave延遲的原因可以歸結爲slave apply binlog的速度跟不上主庫寫入的速度,如何解決複製延遲呢?其實也是如何提高MySQL寫速度的問題。從目前的硬件和軟件的發展來看,硬件存儲由之前的HDD機械硬盤發展到現在的SSD,PCI-E SSD,再到NVM Express(NVMe),IO性能一直在提升。MySQL的主從複製也從單線程複製到不同算法的並行複製(基於庫,事務,行),應用binlog的速度也越來越快。

本文歸納從幾個常見的複製延遲場景,有可能還不完整,也歡迎大家留言討論。

五 拓展閱讀

[1] 一種MySQL主從同步加速方案-改進

https://dinglin.iteye.com/blog/1187154

[2] MySQL多線程同步MySQL-Transfer介紹(已經不在維護)

https://dinglin.iteye.com/blog/1581877

[3] MySQL 並行複製演進及 MySQL 8.0 中基於 WriteSet 的優化

https://www.cnblogs.com/DataArt/p/10240093.html

[4] 速度提升5~10倍,基於WRITESET的MySQL並行複製 https://mp.weixin.qq.com/s/oj-DzpR-hZRMMziq2_0rYg

[5] https://mysqlhighavailability.com/improving-the-parallel-applier-with-writeset-based-dependency-tracking/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章