日常Bug排查-MVCC和for update混用導致讀數據不一致
日常Bug排查-MVCC和for update混用導致讀數據不一致
前言
日常Bug排查系列都是一些簡單Bug的排查。筆者將在這里介紹一些排查Bug的簡單技巧,同時順便積累素材。
Bug現場
又是喜聞樂見的讀數據不一致的問題。這次的問題是這樣,業務在一個事務中更新A和B兩個表的兩個數據。但是在另一個事務中只看到了A的更新,而B依舊是更新之前的值。說好的原子性感覺又被打破了。如下圖所示:

思路
在將這兩個請求的SQL按照時序畫出來的時候,筆者立馬就明白了相關問題所在。核心就在于數據庫是RR隔離級別的,同時業務在查詢A的時候使用的是Select for update,在查詢B的時候使用的是普通的Select。這么使用的原因可能是覺得所有的查詢都需要先查A再查B,那么只需要對A加鎖就行,減少了數據庫鎖的數量。
但是,這里是有一個問題的,就是對B表的查詢用的是普通的Select,也就是使用了MySQL的MVCC機制。而MySQL MVCC的默認創建時刻就是事務的第一個不帶for update的普通Select(具體原理見筆者的博客https://my.oschina.net/alchemystar/blog/1927425)。那么我們就可以從上面的SQL順序可以看到,在事務1開始之前就已經創建了視圖,此時的視圖是A1和B1。那么由于RR,查詢B表的普通Select看到的自然是B1,而select for update不走MVCC,于是看到的是A2。如下圖所示:

解決方案
讓業務對B表的查詢也用Select for update即可,相比于不一致增加的一點非熱點行鎖的性能可以忽略不計。
總結
MVCC和數據庫鎖兩者采用了不同的機制,如果不清楚其中的原理可能會導致不一致的現象出現。同時,在這次的問題中業務對于B表不用鎖這樣的優化實際上是一個負優化。這再次提醒我們,不要過早優化!


浙公網安備 33010602011771號