日常Bug排查系列都是一些簡單Bug排查。問題雖小,但經常遇到,瞭解這些問題,會讓我們少走點彎路,提升效率。說不定有些問題你遇到過哦:) Bug現場 業務開發同學突然問了筆者一個問題,從庫讀會不會沒有原子性?我下意識的反應怎麼可能,只要是遵守MySQL主從Replication協議的原子性至少是能夠 ...
日常Bug排查系列都是一些簡單Bug排查。問題雖小,但經常遇到,瞭解這些問題,會讓我們少走點彎路,提升效率。說不定有些問題你遇到過哦:)
Bug現場
業務開發同學突然問了筆者一個問題,從庫讀會不會沒有原子性?我下意識的反應怎麼可能,只要是遵守MySQL主從Replication協議的原子性至少是能夠保證的。但他們遇到了一個比較詭異的現象。如下圖所示:
這麼一看確實像從庫沒有保證原子性。但這個明顯有違背筆者的常識,這個問題背後肯定還有其它的因素沒有挖掘到。
資料庫拓撲
於是筆者看了看這個庫的拓撲,是一主兩從的結構。如下圖所示:
真相大白
看到這個拓撲的那一刻筆者立馬反應過來,是踩了一個主從延遲變種的坑。由於請求B的兩條select是不在事務內的,而且都是select。這兩很有可能路由到兩個不同的從庫,而這兩個從庫的主從延遲是不一樣的。例如一個100ms,一個200ms。那麼落到100ms從庫的那條sql就會查到請求A的提交,而200ms從庫的那條sql查不到。以致與錯誤的認為從庫不保證原子性!
應該怎麼做
遇到這種情況,其實我們所需要做的只是在某次請求中穩定的路由到某個特定的從庫上面,這樣就能保證原子性(要麼能查到,要麼都查不到)。
如上圖所示,一般在第一次請求之後,在threadLocal中打上相關粘性標簽(SlaveA),那麼在這次線程請求中。後來的從庫select都走SlaveA即可。這個選擇邏輯可以通過重載數據源DataSource的getConnection邏輯來實現。
總結
主從延遲是個非常常見的問題。最常見的是主庫寫入後讀從庫沒有相應的數據,當然也有本文描述的這種看上去”不符合原子性”的變種。看似違背常識的背後可能有其它的隱變數(多從庫不同延遲)。多挖掘一點問題現場的上下文信息就很容易揪出問題的根因。