近期負責的mysql mycat項目中接了一個需求,添加設置session級別的innodb_lock_wait_timeout,通過一番代碼修改能夠作到基本支持業務對for update的使用,可是到了測試環境發現出現個別sql會出現異常。mysql
具體現象是當在A表加for update排他鎖時,業務經過mycat請求mysql查詢加鎖的表,會報超時異常。可是在B表加for update鎖時,業務發起請求會卡住,並無報超時異常,可是mycat日誌顯示數據庫正常返回1205,且正常返回了。git
後面通過對比排查,發現時mycat源碼bug(github上已被修復,咱們使用的版本比較低),當數據庫返回的包過大時,mycat會申請直接內存buffer用於存儲數據,mycat使用完buffer回收Buffer時報空指針異常,致使線程掛掉,業務端接不到後續包因此卡住。github
本覺得問題就這樣結束,可是後續覆盤時發現,兩個sql均時返回一樣的報錯信息,爲何一個數據量大,一個數據量小呢?sql
根據for update的原理,咱們先查看下兩張表的索引:數據庫
A表:session
B表:測試
再分析了下兩條sql的執行計劃:線程
A sql:3d
B sql:指針
兩條sql都是以row_id爲條件,對比發現問題貌似出在索引上,A表row索引,B表使用的是聯合索引。A sql執行計劃是const常量級別的,Bsql 則是ref,即B sql是經過索引部分前綴,先找到符合多個條件的行,以後在肯定惟一行的。
聯合索引本質也是一個B+tree,不一樣的是聯合索引鍵值數量不是1,而是大於等於二,當B sql執行時,會先去根據第一個鍵檢索B+tree,找到的數據可能並非一條,因此數據庫會先回一部分正常返回包,當檢索到被鎖的數據後,纔會回error包。
再看mycat接收到的包(爲了精簡只打印了相關包類型):
A sql :
B sql:
由此發現,B sql比A sql多收到一個fied包,因此B sql返回的數據要大於A sql,致使報錯。
以後又將B SQL改造,把其複合索引的兩個條件都加上後執行計劃和mycat日誌以下:
可見當type爲const即根據索引找到的數據惟一時,mysql會馬上回Error包,可是若不惟一則可能會先回正常的result Set包,以後報錯後再回error包。
因而可知,工做中碰到的問題都是知識點的延申,通過本次問題的解決,在此記錄相關知識點爲:mysql複合索引的查找順序
mysql協議返回包時error包不必定是馬上返