MYSQL 同步到ES 如何設計架構保持一致性

蓝胖子的编程梦發表於2024-03-21

簡單使用某個元件很容易,但是一旦要搬到生產上就要考慮各種各樣的異常,保證你方案的可靠性,可恢復性就是我們需要思考的問題。今天來聊聊我們部門在 MYSQL 同步到ES的方案設計。

在面對複雜條件查詢時,MYSQL往往顯得力不從心,一般公司的做法會透過將mysql中的資料同步到ES,之後的查詢就透過ES進行查詢,ES在面對多條件複雜查詢時,能較快的查詢出結果集。

在MYSQL資料 到ES中的資料同步 方案設計上,就有多種選擇,

1,最簡單的便是直接在業務程式碼中對資料庫進行修改,插入,刪除時,同步修改ES中的資料。 但這種方案也是最不可靠的一種設計。在寫入MYSQL後,業務服務當機了,ES資料就會丟失。如果寫入ES失敗,重試邏輯將會巢狀在業務程式碼中,業務程式碼複雜性增加了,並且如果一直失敗,要一直重試嗎?

所以,對於這種方案,直接pass掉了。

2,第二種同步方案則是業界用的比較多的同步方案,透過binlog進行同步,目前業界已經有比較成熟的模擬mysql從庫,拉取binlog的元件,例如阿里開源的canal。整個同步架構如下所示,canal元件充當mysql從庫的角色,將mysql的binlog拉取下來,由客戶端從canal拉取訊息進行消費,再由客戶端主動插入或者更新ES中的資料。

📢📢注意,這裡我用的是客戶端主動從cannal拉取binlog訊息的方式,實際上還可以透過讓cannal主動傳送binlog訊息到訊息佇列,然後client非同步消費kafka中的訊息。

image.png

所以,我們部門選擇了第二種同步方案,並且由於資料量並不大,也沒有接入kafka,直接採用客戶端從cannal側拉資料的方式。

canal 同步資料異常情況分析

接著我先來提幾個問題,我們可以思考🤔看看當前的架構設計是否能滿足資料同步的基本要求。

1,如果客戶端消費資料失敗,會造成資料丟失嗎?

2,如果cannal崩潰了,那麼會造成拉取的binlog沒有被消費而造成資料丟失的情況嗎?

3,canal會有重複推送消費訊息的情況嗎?

4,如果ES側暫時當機,要想不丟失資料,應該怎麼做?

以上是我針對同步資料時 圍繞這個設計方案的可靠性與可恢復 提出的幾個問題,我們針對它們來看看同步資料應該如何來做。

第一個問題客戶端如果從canal 拉取到了訊息,但是本地由於異常,或者當機 導致消費失敗了,可以做到不丟失資料。因為canal 對於訊息的消費模型提供了ACK機制,客戶端在拉取完一批訊息後,可以依次消費訊息,然後傳送對應訊息的ACK,如果消費失敗,或者本地當機,那麼下次拉取訊息的時候依然能夠拉取到沒有消費完的訊息。

第二個問題, cannal 如果異常崩潰,也是可以做到 訊息不丟失,canal在從資料庫拉取binlog時,會記錄拉取的日誌偏移量offset到記憶體,但是偏移量的持久化 其實是透過定時任務 考慮客戶端ACK位點後,才進行記錄的,可以選擇記錄到zookeeper或者本地檔案。所以如果canal當機了,那麼重啟後,會從zookeeper或者本地檔案中讀取客戶端最後ack的位點,然後從這個位置開始從資料庫拉取訊息。為了讓canal 快速恢復,還可以做canal叢集,讓叢集中始終有備節點。

第三個問題canal的確會有重複推送消費訊息的可能,正如第二問題說的那樣 偏移量的持久化是透過定時任務記錄的,所以存在客戶端消費了訊息,但是這個ack位點還沒有持久化的情況,如果這個時候canal 當機重啟了,那麼將會把客戶端消費過的訊息也再發一遍。所以客戶端消費訊息需要做冪等處理。

第四個問題, 如果ES側的資料寫入失敗了,或者ES直接當機,也是能夠做到ES當機恢復後,資料不丟失的,最簡單的方式其實是客戶端發現ES寫入失敗了,然後不ACK訊息,直接不斷重試,直到寫入成功為止,不過這種做法其實不太好,因為不ACK,那麼訊息會一直存到canal的記憶體裡,同時canal會不斷dump 資料庫的binlog日誌,又塞到記憶體裡等待被客戶端拉取消費,這樣造成的後果就是canal 的記憶體會越來越大,最終停止資料庫的同步操作。

一個比較好點的方式是,客戶端消費了canal的訊息,直接在本地將訊息儲存起來,比如寫入到磁碟檔案上,寫入成功後即可發起ACK,然後本地啟一個協程慢慢將磁碟檔案上的訊息更新到ES中,所以,最後選用了leveldb 對做本地檔案的寫入,leveldb寫入檔案的操作是很快的,這樣快速的ACK訊息對canal的記憶體壓力也會小很多。

綜上,我們部門的資料同步模型就變成了下面這樣,canal做了叢集,保證當機了還有其他節點可以繼續同步,客戶端則是消費訊息後將先把訊息寫入到本地,然後開啟定時任務寫入到ES,如果有錯誤的話,會一直重試,直到成功為止。

image.png

總結

最後,我來總結下,採用canal 去做MySQL 到ES的資料同步,我們的確是可以做到高可靠性的,但是要注意的canal的訊息消費是有可能出現重複訊息的,不過由於目前我們部門沒有對訊息進行統計的需求,僅僅是將資料進行更新或者插入,存在即更新,沒有即插入,所以是冪等,可以不用太過關注。

相關文章