天天看點

資料庫讀寫分離、資料一緻性的沖突、msql主從複制

本文隻作為知識點掃盲,并不做任何深入,單純記錄相關知識點,以備後期檢索

文中相關内容已标明轉載位址

面對資料一緻性問題的選擇:

觀點一:https://www.cnblogs.com/lice-blog/p/11569748.html

讀寫分離: 為保證資料庫資料的一緻性,我們要求所有對于資料庫的更新操作都是針對主資料庫的,但是讀操作是可以針對從資料庫來進行。大多數站點的資料庫讀操作比寫操作更加密集,而且查詢條件相對複雜,資料庫的大部分性能消耗在查詢操作上了。

主從複制資料是異步完成的,這就導緻主從資料庫中的資料有一定的延遲,在讀寫分離的設計中必須要考慮這一點。

1. 适當放棄一緻性:在一些實時性要求不高的場合,我們适當放棄一緻性要求。這樣就可以充分利用多種手段來提高系統吞吐量,例如頁面緩存、分布式資料緩存、資料庫讀寫分離等等。

以部落格為例,使用者登入後發表了一篇文章,他需要馬上看到自己的文章,但是對于其它使用者來講可以允許延遲一段時間(1分鐘/5分鐘/30分鐘),不會造成什麼問題。這時對于目前使用者就需要讀主資料庫,對于其他通路量更大的外部使用者就可以讀從資料庫。

2. 可以通過程式控制,将強一緻性要求的功能(比如存錢、取錢)的讀寫操作均指向主資料庫,或者将寫操作采用“雙寫”的方式實作;而弱一緻性(最終一緻性)要求的功能(比如更新微網誌(寫)、金融查詢賬戶(讀))實作讀寫分離。

總結:要使用讀寫分離來實作系統吞吐量的提升就要從業務上想辦法降低一緻性的要求。對必須要有一緻性的功能是無法進行讀寫分離的,可以采用多庫不區分讀寫以及memcache緩存技術來實作。

其他方案:

1、利用用戶端緩存,減少對資料庫的反複讀取,當使用者讀取一次評論之後便将評論資訊寫入本地cookie,當使用者在一段時間内不停重新整理頁面的時候,就不讓他再讀資料庫了直接去cookie裡面取資料。添加到資料庫資料成功的同時也傳回資料告訴用戶端也添加到cookie裡面去。這樣使用者就知道他自己成功評論了,而且不論他怎麼刷都傷不到伺服器。 

2、利用緩存伺服器的緩存,減少對資料庫的反複讀取,和cookie差不多,隻不過是存在了伺服器的記憶體裡面。這樣比讀資料庫快,但是需要注意這種情況下,如果使用者玩命的刷,伺服器還是很傷。就算是讀記憶體還是得讀伺服器的東西。

3、沒必要将所有的評論都放在資料庫裡,如果評論太多太久遠的沒有意義的就删了吧。

4、關于同一時間并發的評論,直接先不寫資料庫,先全寫到記憶體裡去合并數量,然後按照資料庫能接受的節奏,寫進資料庫。其實這個也是治标不治本,真正的洪流來了,怎麼優化都沒用。直接封IP吧。 

最後的大招: 

告訴使用者他的資料已經送出,但是伺服器更新需要一定的時間,請不要着急等30秒後重新整理看看。這招最簡單,根本就不用什麼程式。

觀點二:https://www.cnblogs.com/lenther2002/p/5021474.html

處理一個資料庫死鎖的異常時候,其中一個建議就是使用 NOLOCK 或者 READPAST,對于非銀行等嚴格要求事務的行業,搜尋記錄中出現或者不出現某條記錄,都是在可容忍範圍内,是以碰到死鎖,應該首先考慮,我們業務邏輯是否能容忍出現或者不出現某些記錄,而不是尋求對雙方都加鎖條件下如何解鎖的問題。

NOLOCK 和 READPAST 都是處理查詢、插入、删除等操作時候,如何應對鎖住的資料記錄。但是這時候一定要注意NOLOCK 和 READPAST的局限性,确認你的業務邏輯可以容忍這些記錄的出現或者不出現:  簡單來說:

NOLOCK 可能把沒有送出事務的資料也顯示出來.

READPAST 會把被鎖住的行不顯示出來 

不使用 NOLOCK 和 READPAST ,在 Select 操作時候則有可能報錯誤:事務(程序 ID **)與另一個程序被死鎖在 鎖 資源上,并且已被選作死鎖犧牲品。

https://www.cnblogs.com/bigox/p/11530540.html

MySQL主從複制(異步複制與半同步複制)

1.MySQl主從複制

  • 原理:将主伺服器的binlog日志複制到從伺服器上執行一遍,達到主從資料的一緻狀态。
  • 過程:從庫開啟一個I/O線程,向主庫請求Binlog日志。主節點開啟一個binlog dump線程,檢查自己的二進制日志,并發送給從節點;從庫将接收到的資料儲存到中繼日志(Relay log)中,另外開啟一個SQL線程,把Relay中的操作在自身機器上執行一遍
  • 優點:
    • 作為備用資料庫,并且不影響業務
    • 可做讀寫分離,一般是一個寫庫,一個或多個讀庫,分布在不同的伺服器上,充分發揮伺服器和資料庫的性能,但要保證資料的一緻性

2.主從複制的日志格式

這裡的日志格式就是指二進制日志的三種格式:基于語句statement的複制、基于行row的複制、基于語句和行(mix)的複制。其中基于row的複制方式更能保證主從庫資料的一緻性,但日志量較大,在設定時考慮磁盤的空間問題

show variables like ‘%binlog%format%’;    #檢視目前使用的binlog的格式
set binlog_format = ‘row’;                #設定格式,這種方法隻在目前session生效
set global binlog_format = ‘row’;       #在全局下設定binlog格式,會影響所有的Session
           

3. 複制架構

3.1、一主多從架構

在主庫的請求壓力非常大時,可通過配置一主多從複制架構實作讀寫分離,把大量對實時性要求不是很高的請求通過負載均衡分發到多個從庫上去讀取資料,降低主庫的讀取壓力。而且在主庫出現當機時,可将一個從庫切換為主庫繼續提供服務(主備切換)

3.2、多級複制架構

因為每個從庫在主庫上都會有一個獨立的Binlog Dump線程來推送binlog日志,是以随着從庫數量的增加,主庫的IO壓力和網絡壓力也會随之增加,這時,多級複制架構應運而生。

多級複制架構隻是在一主多從的基礎上,在主庫和各個從庫之間增加了一個二級主庫Master2,這個二級主庫僅僅用來将一級主庫推送給它的BInlog日志再推送給各個從庫,以此來減輕一級主庫的推送壓力。

但它的缺點就是Binlog日志要經過兩次複制才能到達從庫,增加了複制的延時。

我們可以通過在二級從庫上應用Blackhol存儲引擎(黑洞引擎)來解決這一問題,降低多級複制的延時。

“黑洞引擎”就是寫入Blackhole表中資料并不會寫到磁盤上,是以這個Blackhole表永遠是個空表,對資料的插入/更新/删除操作僅在Binlog中記錄,并複制到從庫中去。

3.3、雙主複制/Dual Master架構

雙主複制架構适用于需要進行主從切換的場景

在隻有一個主庫的架構下,當主庫當機後,将其中一個從庫切換為主庫繼續提供服務。原來的主庫就沒有資料來源了,那麼當這個新的主庫接收到新的資料時,原來的主庫卻沒有同步,是以他們的資料差異越來越大,那麼原來的主庫就無法成為主從複制環境中的一員了。當原來的主庫恢複正常後,需要重新将其添加進複制環境中去。

那為了避免重複添加主庫的問題,雙主複制應運而生。兩個資料庫互為主從,當主庫當機恢複後,由于它還是原來從庫(現在主庫)的從機,是以它還是會複制新的主庫上的資料。那麼無論主庫的角色怎麼切換,原來的主庫都不會脫離複制環境。

4.複制方式

MySQL的主從複制有兩種複制方式,分别是異步複制和半同步複制

4.1異步複制

1、邏輯上

MySQL預設的複制即是異步的,主庫在執行完用戶端送出的事務後會立即将結果返給給用戶端,并不關心從庫是否已經接收并處理,這樣就會有一個問題,主如果crash掉了,此時主上已經送出的事務可能并沒有傳到從庫上,如果此時,強行将從提升為主,可能導緻新主上的資料不完整。

2、技術上

主庫将事務 Binlog 事件寫入到 Binlog 檔案中,此時主庫隻會通知一下 Dump 線程發送這些新的 Binlog,然後主庫就會繼續處理送出操作,而此時不會保證這些 Binlog 傳到任何一個從庫節點上。

4.2全同步複制

基本不會考慮該模式

1、邏輯上

指當主庫執行完一個事務,所有的從庫都執行了該事務才傳回給用戶端。因為需要等待所有從庫執行完該事務才能傳回,是以全同步複制的性能必然會收到嚴重的影響。

2、技術上

當主庫送出事務之後,所有的從庫節點必須收到、APPLY并且送出這些事務,然後主庫線程才能繼續做後續操作。但缺點是,主庫完成一個事務的時間會被拉長,性能降低。

4.3半同步複制

1、邏輯上

是介于全同步複制與全異步複制之間的一種,主庫隻需要等待至少一個從庫節點收到并且 Flush Binlog 到 Relay Log 檔案即可,主庫不需要等待所有從庫給主庫回報。同時,這裡隻是一個收到的回報,而不是已經完全完成并且送出的回報,如此,節省了很多時間。

2、技術上

介于異步複制和全同步複制之間,主庫在執行完用戶端送出的事務後不是立刻傳回給用戶端,而是等待至少一個從庫接收到并寫到relay log中才傳回給用戶端。相對于異步複制,半同步複制提高了資料的安全性,同時它也造成了一定程度的延遲,這個延遲最少是一個TCP/IP往返的時間。是以,半同步複制最好在低延時的網絡中使用。

mysql具體如何配置可參考

https://www.cnblogs.com/zero-gg/p/9057092.html

mqspl半同步存在的一些問題

https://www.cnblogs.com/jichunhu/p/5825801.html

https://www.jianshu.com/p/5387bf6b6dec

資料不一緻沖突産生的原因:從資料庫同步資料的同時可能給業務方傳回的是舊的資料,即所謂的髒讀現象

異步複制:

資料庫讀寫分離、資料一緻性的沖突、msql主從複制

【1】系統先對Master-DB進行了一個寫操作,寫主庫,主庫寫入完畢,傳回成功,并不關心從庫是否已同步更新;

【2】很短的時間内并發進行了一個讀操作,讀從庫,此時主從同步沒有完成,故讀取到了一個舊資料(“舊”是個相對概念,使用者已經收到寫入成功的消息,那麼原資料就相對而言成了舊資料);

【3】主從同步完成。

該模式其他的問題在于假如在寫主庫發生之後,主從同步完成之前發生了主備切換,那麼新主庫中就會丢失掉之前的寫事務

資料一緻性沖突的解決方案:

方案一、讀寫都在主庫

 顯然,這是一種最簡單的方法,讀寫全落在主庫上,必然不會帶來不一緻問題(針對一些強一緻性要求的資料可以這麼做,但是一些弱一緻性需求的資料則不需要)。如圖所示:

資料庫讀寫分離、資料一緻性的沖突、msql主從複制

方案二、semi-sync(半同步複制)

之是以會讀取到舊資料,關鍵在于主從同步需要一個時間段,而讀取請求可能剛好就發生在同步階段。為了讀取到最新的資料,需要等主從同步完成之後,主庫上的寫請求再傳回。示意圖如圖所示:

資料庫讀寫分離、資料一緻性的沖突、msql主從複制

【1】系統先對DB-master進行了一個寫操作,寫主庫;

【2】等主從同步完成,寫主庫的請求才傳回成功;

【3】讀從庫,讀到最新的資料(如果讀請求先完成,寫請求後完成,讀取到的是“當時”最新的資料,這就不算是髒資料。始終保證讀到的是當時的最新資料)

顯然帶來的後果就是主庫的寫請求時延會增加,吞吐量會降低。

方案三、資料庫中間件

借助中間件的路由作用,對服務層的讀寫請求進行分發,進而避免出現不一緻問題。示意圖如圖所示:

資料庫讀寫分離、資料一緻性的沖突、msql主從複制

【1】所有的讀寫都走資料庫中間件,通常情況下,寫請求路由到主庫,讀請求路由到從庫;

【2】記錄所有路由到主庫的key,在經驗主從同步時間視窗(根據經驗設定一個允許同步時間)内(假設是500ms),如果有讀請求通路中間件,此時有可能從庫還是舊資料,就把這個key上的讀請求路由到主庫;

【3】經驗主從同步時間過完後,對應key的讀請求繼續路由到從庫

中間件帶來的好處就是能保證資料的絕對一緻性,但同時也帶來成本上升的問題。

方案四、利用緩存

 原理同方案三類似,當寫請求發生時,

資料庫讀寫分離、資料一緻性的沖突、msql主從複制

【1】将某個庫上的某個key要發生寫操作,記錄在cache裡,并設定“經驗主從同步時間”的cache逾時時間;

【2】修改資料庫

而讀取請求發生的時候:

資料庫讀寫分離、資料一緻性的沖突、msql主從複制

從圖中可以看出:

1)先到cache裡檢視,對應庫的對應key有沒有相關資料;

2)如果cache hit,有相關資料,說明這個key上剛發生過寫操作,此時需要将請求路由到主庫讀最新的資料;

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

顯然,利用緩存,減少了中間件帶來的成本問題,但多了一個Cache元件,并且讀寫資料庫多了一步Cache操作,操作相對其他稍較繁瑣。

繼續閱讀