MySQL rpl_semi_sync_master_timeout相關的一件BUG
1.背景
部署基於MySQL原生複製的HA系統時,發現在半同步模式下,半同步複製降級為非同步複製的逾時時間如果設得很長,會嚴重影響效能高,這是個很奇怪的現象。
2.現象
組合不同參數,用sysbench做壓力測試。
| sysbench --db-driver=mysql --mysql-db=test2 --mysql-host=srdsdevapp69 --mysql-table-engine=innodb --oltp-table-size=5000000 --num-threads=10 --max-time=10 --max-requests=0 --oltp-test-mode=complex --oltp-read-only=off --test=/opt/sysbench-0.5/sysbench/tests/db/insert.lua run |
結果如下:
| rpl_semi_sync_master_enabled |
rpl_semi_sync_master_timeout |
qps |
備忘 |
| on |
21474836480 |
13.99 |
約248天 |
| 2147483648 |
196.3 |
約24.8天 |
| 214748364 |
1251.67 |
約2.5天 |
| 86400000 |
2146.96 |
1天 |
| 43200000 |
3211.17 |
12小時 |
| 21600000 |
3583.02 |
6小時 |
| 10000 |
3637.16 |
10秒(預設值) |
| off |
- |
8926.76 |
|
3. 原因
從上面的表不難看出,當rpl_semi_sync_master_timeout很大時,每個查詢的執行時間和rpl_semi_sync_master_timeout成正比。
為什麼會出現這麼奇葩的事?翻開MySQL的代碼,立刻真相大白!
plugin\semisync\semisync_master.cc:
- #define TIME_THOUSAND 1000
- #define TIME_MILLION 1000000
- #define TIME_BILLION 1000000000
- ...
- int ReplSemiSyncMaster::commitTrx(const char* trx_wait_binlog_name,
- my_off_t trx_wait_binlog_pos)
- {
- ...
- unsigned long long diff_nsecs =
- start_ts.tv_nsec + (unsigned long long)wait_timeout_ * TIME_MILLION;
- abstime.tv_sec = start_ts.tv_sec;
- while (diff_nsecs >= TIME_BILLION)//這個while迴圈是罪魁禍首!!!
- {
- abstime.tv_sec++;
- diff_nsecs -= TIME_BILLION;
- }
- abstime.tv_nsec = diff_nsecs;
- ...
- }
上面有個while迴圈,迴圈次數等於rpl_semi_sync_master_timeout對應的秒數,也就是說,如果設定成300天的話,要迴圈25920000次,不慢才怪!
4. 修複
把那段代碼中的while替換等價的寫法後,問題解決。測出的qps在3700左右,和rpl_semi_sync_master_timeout無關。
4.1 修改代碼
plugin\semisync\semisync_master.cc:
- # diff plugin/semisync/semisync_master.cc plugin/semisync/semisync_master.cc_bak
- 687,688c687,688
- < start_ts.tv_nsec + ((unsigned long long)wait_timeout_ % TIME_THOUSAND) * TIME_MILLION;
- < abstime.tv_sec = start_ts.tv_sec + (unsigned long long)wait_timeout_ / TIME_THOUSAND;
- ---
- > start_ts.tv_nsec + (unsigned long long)wait_timeout_ * TIME_MILLION;
- > abstime.tv_sec = start_ts.tv_sec;
4.2 編譯
cmake -DCMAKE_INSTALL_PREFIX=/usr/local/mysql -DMYSQL_DATADIR=/usr/local/mysql/data -DSYSCONFDIR=/etc -DWITH_MYISAM_STORAGE_ENGINE=1 -DWITH_INNOBASE_STORAGE_ENGINE=1 -DWITH_MEMORY_STORAGE_ENGINE=1 -DWITH_READLINE=1 -DMYSQL_UNIX_ADDR=/var/lib/mysql/mysql.sock -DMYSQL_TCP_PORT=3306 -DENABLED_LOCAL_INFILE=1 -DWITH_PARTITION_STORAGE_ENGINE=1 -DEXTRA_CHARSETS=all -DDEFAULT_CHARSET=utf8 -DDEFAULT_COLLATION=utf8_general_ci -DWITH_FAST_MUTEXES=1make
註:上面的編譯選項填的比較隨意,從網上隨便抄了後再改的,只求編譯通過。
4.3 拷貝半同步外掛程式
chown mysql:mysql ./plugin/semisync/semisync_master.socp -rf ./plugin/semisync/semisync_master.so /usr/local/mysql/lib/mysql/plugin/semisync_master.socp -rf ./plugin/semisync/semisync_master.so /usr/local/mysql/lib/plugin/semisync_master.so
4.4重啟MySQL5. 補充
該Bug已報告到MySQL官方網站,http://bugs.mysql.com/80651