前言
我們知道窟她,MySQL有一個老問題陈症,當表上無主鍵時,那么對于在該表上做的DML震糖,如果是以ROW模式復制录肯,則每一個行記錄前鏡像在備庫都可能產(chǎn)生一次全表掃描(或者二級索引掃描),大多數(shù)情況下吊说,這種開銷都是非常不可接受的论咏,并且產(chǎn)生大量的延遲。
在MySQL5.6中提供了一個新的參數(shù):slave_rows_search_algorithms, 可以部分解決無主鍵表導致的復制延遲問題颁井,其基本思路是對于在一個ROWS EVENT中的所有前鏡像收集起來厅贪,然后在一次掃描全表時,判斷HASH中的每一條記錄進行更新雅宾。
以上摘自印風大神的文章养涮,具體效果和使用方式可以查看文章:https://yq.aliyun.com/articles/41058。
本文不是要繼續(xù)討論 slave_rows_search_algorithms 的原理秀又,而是在使用?slave_rows_search_algorithms 參數(shù)時遇到一個坑单寂,撲朔迷離的地方在于這不像一個bug贬芥,這又是一個bug吐辙,最后官方的操作更是讓人看不懂...
問題描述
row-based replication,主從數(shù)據(jù)一致情況下蘸劈,slave sql 線程報錯 Can't find record昏苏。
如何復現(xiàn)
相關配置參數(shù):
slave_rows_search_algorithms = 'INDEX_SCAN,HASH_SCAN'
binlog_format = ROW
在主庫上:
1. CREATE TABLE t1 ( A INT UNIQUE KEY, B INT ); insert into t1 values (1,2);
2. replace into t1 values (1,3),(1,4);
3. 然后從庫就會報錯了
4. 在從庫上:set global slave_rows_search_algorithms='INDEX_SCAN,TABLE_SCAN'; start slave;? 問題解決
或者用如下方法避免:
1. 將 UNIQUE KEY 調整為 PRIMARY KEY;
2. 將 replace into tt.t1 values (1,3),(1,4); 調整為 replace into tt.t1 values (1,3);replace into tt.t1 values (1,4);
分析過程
1. 查看 slave_rows_search_algorithms 參數(shù)定義:
The default value is?INDEX_SCAN,TABLE_SCAN, which means that all searches that can use indexes do use them, and searches without any indexes use table scans.
To use hashing for any searches that do not use a primary or unique key, set?INDEX_SCAN,HASH_SCAN. Specifying?INDEX_SCAN,HASH_SCANhas the same effect as specifying?INDEX_SCAN,TABLE_SCAN,HASH_SCAN, which is allowed.
Do not use the combination?TABLE_SCAN,HASH_SCAN. This setting forces hashing for all searches. It has no advantage over?INDEX_SCAN,HASH_SCAN, and it can lead to?“record not found”?errors or duplicate key errors in the case of a single event containing multiple updates to the same row, or updates that are order-dependent.
根據(jù)手冊描述,可以理解為:
1. 從庫定位數(shù)據(jù)可選項有?INDEX_SCAN贤惯、HASH_SCAN洼专、TABLE_SCAN,優(yōu)先級是依次遞減的孵构。也就是說如果有主鍵屁商,走?INDEX_SCAN,沒主鍵則根據(jù)設置走?HASH_SCAN?還是?TABLE_SCAN(而且如果兩者都配了颈墅,則優(yōu)先?HASH_SCAN)蜡镶;
2. 無主鍵設置?INDEX_SCAN,HASH_SCAN,可以提升從庫回訪效率恤筛,降低延遲官还;
3. 如果走了?HASH_SCAN,當對同一行數(shù)據(jù)同時更新多次時毒坛,會導致無法找到行記錄望伦。
看到這里,手冊已經(jīng)說明原因了煎殷。但是矛盾的地方在于手冊有推薦使用?INDEX_SCAN,HASH_SCAN?的意思屯伞,并且 8.0.2 版本開始的默認值已經(jīng)修改為:INDEX_SCAN,HASH_SCAN。
2. 針對上面的疑問提交SR
Oracle 工程師回應這是 HASH_SCAN 的 bug豪直,修復到了 MySQL8.0.17(還沒有正式發(fā)布):
It happens when one row is updated twice within an Update_rows_log_event. HASH_SCAN will put both rows in a hash map. Then it iterates over the rows of the table, looks up each row in the hash. If any row is found, it applies the update. Since it only makes one lookup per row, it will miss the second update. In the end, it checks that all rows in the hash were applied and generates an error otherwise. This is what is seen in the bug report.
等等愕掏,為什么只修復到 8.0 而沒在 5.7 修復?再根據(jù)之前就有的疑問顶伞,我來腦補一波:
1. 文檔寫了在某些情況 HASH_SCAN 有這個問題(吐槽:HASH_SCAN 就是優(yōu)化無主鍵情況從庫復制效率的饵撑,但是無主鍵且對同一行數(shù)據(jù)同時更新多次時 HASH_SCAN 又會導致從庫無法找到記錄,那我用還是不用呢唆貌?黑人問號.gif)滑潘,所以暫時我先假設“這不是個 bug”;
2. Oracle 工程師反手甩給我一個 bug 報告锨咙,啪啪打臉语卤,官方承認這就是一個 bug;
3. 官方好像也認為有點不對勁酪刀,一拍腦袋粹舵,咱們只在 8.0 修復,把鍋甩給 “8.0.2 的默認值修改成了 HASH_SCAN”骂倘。
這個迷惑行為可以用一句經(jīng)典總結:it's not a bug,it's a feature!
解決方案
1. 給表添加主鍵(規(guī)范必須有主鍵才是王道)眼滤;
2. 修改參數(shù) slave_rows_search_algorithms='INDEX_SCAN,TABLE_SCAN';
3. 最符合初衷的做法是升級到 8.0.17历涝,可惜這步子對于絕大多數(shù)生產(chǎn)環(huán)境來說都太大了诅需。