日常 Bug 排查 - 讀從庫沒有原子性?

前言

日常 Bug 排查系列都是一些簡單 Bug 排查。問題雖小,但經常遇到,瞭解這些問題,會讓我們少走點彎路,提升效率。說不定有些問題你遇到過哦:)

Bug 現場

業務開發同學突然問了筆者一個問題,從庫讀會不會沒有原子性?我下意識的反應怎麼可能,只要是遵守 MySQL 主從 Replication 協議的原子性至少是能夠保證的。但他們遇到了一個比較詭異的現象。如下圖所示:

這麼一看確實像從庫沒有保證原子性。但這個明顯有違背筆者的常識,這個問題背後肯定還有其它的因素沒有挖掘到。

數據庫拓撲

於是筆者看了看這個庫的拓撲,是一主兩從的結構。如下圖所示:

真相大白

看到這個拓撲的那一刻筆者立馬反應過來,是踩了一個主從延遲變種的坑。由於請求 B 的兩條 select 是不在事務內的,而且都是 select。這兩很有可能路由到兩個不同的從庫,而這兩個從庫的主從延遲是不一樣的。例如一個 100ms,一個 200ms。那麼落到 100ms 從庫的那條 sql 就會查到請求 A 的提交,而 200ms 從庫的那條 sql 查不到。以致與錯誤的認爲從庫不保證原子性!

應該怎麼做

遇到這種情況,其實我們所需要做的只是在某次請求中穩定的路由到某個特定的從庫上面,這樣就能保證原子性 (要麼能查到,要麼都查不到)。

如上圖所示,一般在第一次請求之後,在 threadLocal 中打上相關粘性標籤 (SlaveA),那麼在這次線程請求中。後來的從庫 select 都走 SlaveA 即可。這個選擇邏輯可以通過重載數據源 DataSource 的 getConnection 邏輯來實現。

總結

主從延遲是個非常常見的問題。最常見的是主庫寫入後讀從庫沒有相應的數據,當然也有本文描述的這種看上去” 不符合原子性” 的變種。看似違背常識的背後可能有其它的隱變量 (多從庫不同延遲)。多挖掘一點問題現場的上下文信息就很容易揪出問題的根因。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/HUBi5Q4VouDP2hLEpzzCxw