數據的讀寫分離——主從同步 分析

前述

最近在研究redis,redis中提到了主從同步,而我常用的mysql數據庫中也有主從同步的概念。
故而這次對數據的主從同步進行了了解

爲何要讀寫分離?

訪問量大、訪問量大、訪問量大

我們現在很多技術解決方案,都是爲了解決:隨着用戶量增長,訪問量越來越大,而原本的服務架構不足以支撐如此的訪問量

而往往讀操作是越大部分。我們以往單數據庫,一個數據庫負責了所有讀寫操作

所以就提出了一種解決方案——讀寫分離——由master(主數據庫)負責數據的寫操作,而slave(從數據庫,可多臺)只負責讀操作

爲何要主從同步?

在出現了讀寫分離的數據庫架構設計後,便引出一個問題,slave既然負責讀操作,那數據哪裏來?

自然是同步master內的數據。

所以我們便將slave從master同步數據,稱之爲主從同步

高併發場景下,如何實現數據庫主從同步?

如何主從同步?

先大致說下同步原理:在master上,主從同步事件會被寫到特殊的log文件中(binary-log);在slave機器上,slave讀取主從同步事件,並根據讀取的事件變化,在slave庫上做相應的更改

先說說主從同步事件的形式

  1. statement:會將對數據庫操作的sql語句寫入到binlog中。
  2. row:會將每一條數據的變化寫入到binlog中。
  3. mixed:statement與row的混合。Mysql決定什麼時候寫statement格式的,什麼時候寫row格式的binlog。

在同步時執行操作

  1. master數據發生改變,改變事件(DDM、DML)會依次寫入到binlog文件
  2. master會創建一條binlog dump線程,負責與slave聯繫,當binlog發生變化,dump線程通知slave,並將binlog信息發送給slave
  3. slave在開啓主從同步後,會創建兩條線程:
    1. I/O線程,負責與master的dump聯繫,接收binlog,並寫入到本地的relaylog
    2. SQL線程,讀取relaylog,根據relaylog操作slave數據庫

主從同步引出的問題

延時——主從未同步

既然slave負責讀,當master寫操作後,在slave同步時,這中間必然存在時間間隔

那在這時間間隔中就存在了變數:

  1. 在slave同步時,用戶去slave請求最新數據,卻沒有——主從不同步
  2. 在master寫操作完成後成功返回,dump開始通知slave,但在這期間,如果master發生了crash,那slave便丟失了本次的寫操作
  3. 在master寫操作完成正在返回,且dump已通知slave,但這時,master發生了cash,那麼主數據庫回滾,但slave卻保存了這次寫操作
  4. ...

這該如何?

幾種主從同步解決方案

1.異步同步

也是mysql默認的同步方式。

master在執行完客戶端提交的事務後會立即將結果返給給客戶端,並不關心從庫是否已經接收並處理,

但這樣就會發生上面所說的第二中情況

2.全同步

顧名思義:

就是master完成寫操作事物後不立刻返回,等!等所有slave完成從同步,再返回事物。

這樣確實可以保證主從數據一致,但問題也是很明顯的

每次master的寫操作時間增大!如果一次寫操作的數據量巨大,那....。如果在執行某個slave出現的select鎖競爭,又要等待,甚至slave出現了死鎖...

所以全同步雖然保證了主從同步一致性,但代價也是非常巨大,要慎重

3.半同步

介於異步與全同步之間。master寫操作完成後事物不立即返回,至少等待一個slave接收到並寫到relay log中,才返回事物。

但也造成了一定的延遲,至少是一個tcp/ip往返時間,所以此方式最好在低延遲環境中執行

其他解決方案

中間件

高併發場景下,如何實現數據庫主從同步?

所有讀寫操作走中間件,由中間件,寫操作路由到master,讀操作路由到slave
如本次請求爲寫操作,在中間件記錄該寫操作的key,在主從同步時間段,有讀操作請求中間件內的該key,則讓本次讀操作master,主從同步時間段完成後,讀請求走slave

相關的中間件有:

  • canal:是阿里巴巴旗下的一款開源項目,純Java開發,基於數據庫增量日誌解析,提供增量數據訂閱&消費,目前主要支持了MySQL
  • otter:也是阿里開源的一個分佈式數據庫同步系統,尤其是在跨機房數據庫同步方面,有很強大的功能。它是基於數據庫增量日誌解析,實時將數據同步到本機房或跨機房的mysql/oracle數據庫

區別:

  • otter目前嵌入式依賴canal,部署爲同一個jvm,目前設計爲不產生Relay Log。
  • otter目前允許自定義同步邏輯,解決各類需求

這樣便可保證主從同步,且效率還可以

但中間件嘛,數據庫成本也擺在那了

緩存記錄寫key法

高併發場景下,如何實現數據庫主從同步?

其作用與思路與緩存中間件很類似

寫流程:

1)要發生寫操作,將key記錄在cache裏,並設置“主從同步時間”的cache超時時間

2)然後master執行寫操作,並執行主從同步

讀流程:

1)先到緩存查找key(key設置了失效時間)

2)如有數據,則緩存命中,說明該key剛發生過寫操作,此時需要將請求路由到主庫讀最新的數據。

3)如果緩存沒有命中,說明這個key上近期沒有發生過寫操作,此時將請求路由到從庫,繼續讀寫分離。

優點:相對數據庫中間件,自然成本較低

缺點:多了一層緩存中間件,那在業務實現時,就要考慮多一些了

總結

以上就是我對讀寫分離——主從同步的瞭解與思考,當然是借鑑了其他文章,以前對數據庫的讀寫分離處於只知其名的階段

本次還是因爲要系統瞭解redis知識體系,所以對主從同步進行了較爲深入的瞭解

希望多多交流,如有疑惑與不足請及時留言交流

 

知識總要有體系的去學習,這樣便可以讓自己在腦子裏建模,對該知識有個系統的認識,也更便於理解。知識它是什麼,爲什麼有它,它來解決什麼技術問題。

 

參考文檔:
https://www.toutiao.com/a6762107909095555592/
https://www.toutiao.com/a6699310878136730115/
https://www.toutiao.com/a6754000358118261251/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章