一文淺談“讀寫分離”技術

ITPUB社群發表於2023-02-01

讀寫分離,作為一種常用的資料庫訪問最佳化手段,得到廣泛的應用。本文嘗試從讀寫分離的技術實現、適用場景及典型產品等角度,闡述這一技術的整體現狀。

1. 讀寫分離:概述

1).何為讀寫分離

讀寫分離,從字面理解就是將對資料庫的讀操作與寫操作分離的一種最佳化手段。其最早起源於網際網路快速發展時期,面對海量使用者訪問問題,透過這一技術來解決資料庫效能瓶頸問題。目前已經成為非常常見的一種資料庫訪問最佳化技術。

2).讀寫分離好處

  • 提高訪問效能

    透過引入讀寫分離技術,將之前集中於單點的訪問壓力,分散到更多節點。即可利用更多的資源,支撐業務系統,可有效提升整體訪問效能。

  • 提高穩定性

    透過將讀取與寫入操作的分離,可有效規避由於異常操作所帶來的風險。常見如一個大查詢語句,因訪問資料規模巨大佔用大量CPU資源。透過承載端分離,可避免影響更為重要的寫入操作。

  • 提高資源利用率

    為了更好地保護資料,資料庫系統通常採用多副本技術冗餘保護資料,但其備用副本如無法提供業務訪問,將是一種資源浪費,而讀寫分離可有效利用只讀副本,提升整體資源利用率。

  • 提高可用性

    透過引入更多節點來承載讀寫操作,結合負載均衡與高可用探查技術,可避免單點故障引發可用性問題。

  • 提高訪問效率

    透過利用不同節點分別承載讀取與寫入,還可緩解因為鎖帶來的爭用問題,提高單節點的訪問效率。

  • 更大最佳化空間

    針對讀取操作的特殊性,可透過分離後的獨立資源採取特有的最佳化技術,進一步提升訪問效率。

2. 讀寫分離:技術實現

1).常用方案

目前業界流行的讀寫分離方案,通常都是基於上述主從模式的資料庫架構,透過引入資料訪問代理層,來實現訪問動作的讀寫分離。引入資料訪問代理的好處是源程式不需要做任何改動就可以實現讀寫分離,壞處是由於多了一層中介軟體做中轉代理,效能上會有所下降,資料訪問代理也容易成為效能瓶頸,並且還存在一定維護成本。還有另一種方式,是將資料訪問代理層前置到應用側,透過SDK方式與應用整合在一起,可避免獨立一層所帶來的效能損耗和維護成本高的問題。但這種方式對開發語言有一定要求,存在適用性問題。

2).技術要點

讀寫分離功能的好與不好,主要是在易用性和靈活度問題。前者是關心如何讓業務開發像操作單個主庫一樣,無需過多關注主從讀寫分離的細節,只需要做好相應讀寫配置後,就無需考慮寫主讀從的細節。後者是解決使用者多變的業務場景和拓撲變化,並可實現自動適應。這其中是需要解決一系列技術問題,如下面這些常見的問題。

❖ 判斷讀寫操作

如何判斷讀寫操作,是讀寫分離面臨的首要問題。判斷方式可大致分為自動和手動兩種,前者是透過顯式的方式由使用者來指定;後者則是自動進行判斷,使用者無需關心。這兩種判斷方式往往是互補的,可配合來使用。下面是常見判斷邏輯及處理:

  • 基於不同埠連線

該實現方式就讀寫分離功能而言不是太好,因為此方式與應用自己實現沒有明顯差別,只是將直接連線不同資料庫的邏輯變成了連線中介軟體伺服器的不同埠,並沒有對應用系統開發帶來實質性的簡化工作。

  • 基於SQL匹配

採用正規表示式匹配是比較容易實現的方案,可以無需應用的修改,只需要在中介軟體新增正則匹配的規則,即可將讀、寫分發的邏輯在中介軟體完成。讀寫分離的效果,取決於中介軟體的正則匹配規則的編寫質量。

  • 基於Hint

應用系統傳送SQL時,可以新增Hint,顯示的告訴中介軟體想要將該SQL傳送到何處。中介軟體解析特定規則的Hint,即可實現對帶有不同Hint的語句分發到不同的資料庫節點。

  • 基於語法解析

當中介軟體獲取到應用傳送的SQL字串時,對其進行完整的語法解析,可以最大程度的獲取SQL字串中的資訊,例如型別、操作物件等。基於語法的判斷,就能夠自動針對不同語句型別進行讀寫分發,可以最大限度的減少應用的適配工作。

使用語法解析是相對來說較為友好的方式,無需開發人員感知即可實現讀寫操作分離。但這其中存在難點,就是如何準確判斷出只讀操作存在一定困難,例如使用函式、儲存過程、觸發器或諸如“SELECT ... FOR UPDATE”類的操作。此時,是需要引入輔助機制進行判斷,可採取配置名單方式來輔助分析;或者透過Hint、API的方式強制指定走寫庫或讀庫。除此之外,還有些命令也需要規範是否可在備庫執行,如COPY、SHOW、SET、BEGIN...END等。

❖ 如何處理事務

事務類操作,往往意味著資料變化,在讀寫分離中如何處理呢?通常有兩種思路,一種是簡單粗暴方式,將所有事務及關聯操作全部傳送到主機;一種是更為精確的處理,即分析事務內的語句序列,將事務中先寫後讀的物件進行關聯,一起傳送到主機,確保資料正確,而把和寫操作無關的讀操作,進行拆分,傳送到備機執行。後一種處理方式能最大限度的利用讀寫分離,當然需要解決物件前後關係這一問題。

❖ 解決主備延遲

基於副本方式的延遲是常見的,也是讀寫分離在設計之初就需考慮的問題。其通常的處理思路可以有多種:

  • 強制讀寫走主庫

這類解決方案最簡單粗暴,也是實際工作中最常用的方案。透過對主備節點延遲情況的判斷,來決定如何是走主庫還是備庫。通常可將延遲判斷封裝在中間層,前端應用可不感知,只需配置延遲閾值即可,當超過這一閾值就自動走主庫。如下次訪問時延遲低於閾值,可重新走備庫。當然,這一方式無疑會加大對主庫的壓力。

  • 輪轉和重試備庫

當在備庫讀取不到最新資料時,另一種思路多讀取幾次或者嘗試讀取其他備庫。這裡面的核心是對讀取最新資料的判斷,通常需要在應用開發時有所考慮才可。同時還需要制定退化方案,在何種情況下退化到讀取主庫。

  • 結合快取解決

如延遲是常態,很難短期內解決,透過引入快取可達到立竿見影的效果。其原理是在資料寫入主庫時,同步或非同步寫入快取,應用讀取時優先讀取快取,失效時才讀取資料庫。這種方案因引入快取元件稍顯複雜,需解決快取與資料庫同步更新及失效問題;同時對應用側有一定影響,需感知到快取。比較好的處理方式是都封裝在中間層,透過它來統一處理訪問邏輯。

  • 資料庫最佳化

最後一種就是儘量避免出現延遲,常見對資料庫有些可最佳化的措施。例如儘量減少在主節點上執行大事務操作、減少主庫索引進而減小寫入開銷、主備庫採用不同儲存引擎提升效率等等。當然這些方案只能起到一定作用,無法完全避免延遲問題。

❖ 靈活負載策略

針對多個讀庫,讀寫分離元件還需提供靈活的負載均衡策略,常見的如隨機、輪詢、權重等等。這其中有幾個特殊情況需要考慮:

  • QoS

不同讀庫的服務能力有所差異下,其能提供的服務保障不同,需在讀寫分離中提供例如權重的配置,進行干預。當然,更好的方式是提供服務質量評估機制,可根據各讀庫的服務能力進行分配。

  • 位置感知

針對多AZ、多Region的情況,不同讀庫承載的角色不同,有的只作為備選主庫不承擔讀、有的作為遠端災備等,因此在讀寫分離中希望能感知到這些資訊,有所區別對待。往往可透過設定標籤的方式解決,根據不同標籤設定不同策略。

❖ 解決讀一致性

在讀寫分離中,當存在多個讀庫下,會因為延遲不同,出現讀取不一致的情況。即路由到不同的讀庫,讀取的資料鮮活度不同。這對於前端應用會造成一定困擾,解決的方法可採用會話粘性的策略,針對同一會話路由到同一讀庫,避免出現讀不一致。

❖ 拓撲結構感知

如果讀寫分離訪問的資料叢集拓撲發生變化,例如主備發生切換,寫操作要到新的主庫;亦或是增加了備庫數量,流量可以打到新備庫等,這些都是需要讀寫分離元件感知到底層資料庫拓撲的變化。這裡的難點在於幾個方面:

  • 準確感知變化

當出現網路等原因,底層發生變化,可能讀寫分離元件沒有探查到;或者探查本身就出現問題,沒有發生變化而誤認為發生變化。此時就會出現兩張拓撲結構,一個實際結構,一是讀寫分離元件感知到的結構。這一問題,一方面可透過引入共識機制,增加多方判斷解決;一方面也可透過與高可用元件互動減少誤判。

  • 感知時效問題

當發生拓撲變化後,從發生變化到被讀寫分離元件感知是需要時間的,過短會導致資料庫探查壓力大;過長會影響整體恢復時間,這其中需要有個取捨。建議將這一能力開放給使用者,由使用者根據自身業務進行決策。同時也可與高可用元件互動,將拓撲變化資訊儘快推送到讀寫分離元件,變被動探查為主動感知,提高時效性。

  • 人為干預能力

除因故障等原因發生的拓撲變化外,有時還需人工干預讀寫分離。如發生機器維護、資料庫升級等情況下,可提前透過人工手段,從拓撲結構中摘除相關節點,做到更加平順。

❖ 個性化訴求

除了上述要點外,還有些使用者個性化的需求。如某個資料庫使用者的訪問只走主庫,某類應用的訪問只走主庫等,這類需求比較分散,比較好的處理方式是提供一定的指令碼擴充套件能力,類似lua擴充套件Nginx的方式。

3. 讀寫分離:最佳實踐

1).資料庫最佳化手段對比

讀寫分離技術,是一種有效的資料庫訪問最佳化手段,但不是唯一。隨著業務增長,達到一定規模後,提升資料庫承載能力可以有多種方式,從大的分類來看可分為業務層最佳化、架構層最佳化、訪問層最佳化與資料庫最佳化幾個方面。

  • 業務層-垂直拆分

    最為徹底的最佳化手段,在業務層就做了拆分,投入較高,但取得效果往往也比較可觀。

  • 架構層-快取/搜尋

    透過引入快取、搜尋等技術,減輕對資料庫壓力,讓資料庫專注於有價值操作。這種方式需要一定改造工作量,取得收益取決於業務對資料的要求而定。

  • 訪問層-讀寫分離

    簡單快速的最佳化方式,可快速提升效能,針對部分場景效果明顯。

  • 訪問層-分庫分表

    分庫分表方式,原理上是採取“大化小”的策略,但對於SQL相容性有較高要求,會存在一定業務改造工作量。預期收益效果看規模和業務對資料要求而定。

  • 資料庫-垂直拆分

    對現有資料庫根據業務進行拆分,難易程度及投入成本取決於之前架構設計,難點在於拆分後的資料互動。預期收益不很明確。

  • 資料庫-垂直擴充套件

    對資料庫升級是快速見效的措施,對應用幾乎無影響,但需一定的成本投入及升級所需的中斷服務的時間。取得收益存在上限瓶頸,預期中等。

  • 資料庫-水平擴充套件

    對分庫分表類似,但通常初始投入較大,對應用存在一定侵入性。

從上述對比可見,讀寫分離,可以說是對應用侵入最小,也最容易實現的最佳化手段。相對投入不到,就可取得一定效果。特別是對於大量讀請求和少量寫請求的業務場景,會有不錯的效果。

2).讀寫分離適用場景

讀寫分離是一種簡單有效的最佳化方式,但不是萬能,其有著明顯的適用場景特徵。

  • 讀多寫少

當單機資料庫不能支援業務的讀寫規模,就可以考慮讀寫分離。但需要考慮兩者的比例,如果寫操作比例大於讀操作,那麼大量寫操作都在主庫進行,讀寫分離達不到預期降低主庫壓力的作用。一般來說,兩者讀寫比越大,效果越好。當然還需考慮寫規模不能也不能高於單機資料庫支援規模。

  • 讀有限擴充套件

針對承載讀的規模超大的情況,也需慎重。透過讀寫分離是可以實現一定程度讀操作的橫向擴充套件,但不是無限的,受限於資料庫複製的效率與成本,其存在擴充套件上限。對於大規模的可綜合考慮快取、資料拆分等多種手段。

  • 允許延遲

針對主備方式難免存在延遲,因此對於延遲很敏感的操作不適於此方案。

  • 非複雜查詢

採用讀寫分離能在一定程度上解決查詢效率問題,但針對複雜查詢試圖透過這一方式去解決不是一個好的思路。這類訴求建議透過搜尋引擎、OLAP等技術去解決。

4. 讀寫分離:典型產品

業內有很多讀寫分離方案,一類是採用中介軟體思路開發,以開源產品為主;一類是資料庫產品,內建讀寫分離功能。下面簡單介紹下主要的產品:

1).MySQL-Proxy

MySQL-Proxy是MySQL官方提供的MySQL中介軟體服務。MySQL-Proxy實際上是在客戶端請求與MySQLServer之間建立了一個連線池。所有客戶端請求都是發向MySQL-Proxy,然後經由MySQL-Proxy進行相應的分析,判斷出是讀操作還是寫操作,分發至對應的MySQLServer上。對於多節點Slave叢集,也可以起做到負載均衡的效果。

# ./mysql-proxy --daemon --log-level=debug --user=mysql --keepalive --log-file=/var/log/mysql-proxy.log --plugins="proxy" --proxy-backend-addresses="192.168.1.5:3306" --proxy-read---proxy-lua-script="/root/soft/mysql-proxy/rw-splitting.lua" --plugins=admin --admin-username="admin" --admin-password="admin" --admin-lua-script="/root/soft/mysql-proxy/lib/mysql-proxy/lua/admin.lua"

其中proxy-backend-addresses是master伺服器,proxy-read-only-backend-addresses是slave伺服器。

2).Apache ShardingSphere

Apache ShardingSphere 是一款開源的資料庫中介軟體產品,並在Apache基金會畢業,可以說是非常成熟的開源專案。其產品內建了豐富的功能,包括讀寫分離能力,具體包括:

  • 支援動態、靜態讀寫分離能力,支援自動拓撲感知與人工設定。

  • 支援多種資料庫(如MySQL、PG、openGauss等)及多種架構(如MySQL 主從、MGR等)

  • 支援多端接入(Driver、Proxy),可滿足低時延場景

  • 支援語法解析自動判斷或 Hint 方式手工指定

  • 支援包括熔斷等能力的人工干預手段,可適應多種場景

  • 支援類SQL的管理配置方式,支援熱載入配置

  • 支援豐富的負載均衡演算法,如下圖

3).MyCAT

MyCAT 是一款開源的資料庫中介軟體產品。讀寫分離功能透過配置檔案完成,如下

balance,讀寫分離策略

  • 0,不開啟讀寫分離機制,所有讀操作發到當前可用writeHost上

  • 1,全部readHost與stand by writeHost參與select語句負載均衡

  • 2,所有讀操作都隨機在writeHost、readhost上分發

  • 3,所有讀請求隨機分發到readhost

writeType,寫模式

  • 0,所有的操作傳送到配置的第一個writehost

  • 1,隨機傳送到配置的所有writehost

  • 2,不執行寫操作

switchType,切換模式

  • -1,表示不自動切換

  • 1,預設值,表示自動切換

  • 2,基於MySQL主從同步的狀態決定是否切換

  • 3,基於MySQL galary cluster的切換機制

4).阿里雲-RDS資料庫代理(以RDS PG為例)

資料庫代理是阿里雲資料庫RDS提供的一款安全、穩定、高效能,對應用完全透明的資料庫中間層服務。資料庫代理是位於資料庫服務端和應用服務端之間的網路代理服務,代理服務端代替應用服務端資料庫傳送和接受所有資料庫請求,進而可以在代理服務層上實現比如讀寫分離、連線池、端對端加密、防閃斷等附加功能。透過資料庫代理使用者只需要透過一個連結地址即可實現讀寫分離架構,讀寫屬性和只讀屬性的多樣化選擇滿足了不同業務場景。

資料庫代理支援了PostgreSQL的協議,並且具備對使用者的請求連線認證許可權的能力。代理中的路由策略是核心:可以透過hint中指定固定的例項節點來轉發流量;也能夠將事務內寫操作之前的讀請求轉發到只讀例項,降低主例項負載;路由策略還可以根據使用者自定義的只讀模式或讀寫模式對請求進行不同的分發執行。健康巡檢模組週期性感知讀寫分離架構的拓撲變化情況,在例項節點不健康或者超過延遲閾值,會自動把讀請求路由到其他的只讀例項上。

5).OceanBase

OceanBase 資料庫天然支援讀寫分離的功能,即透過 OBProxy 代理服務和修改 OBServer 的配置即可實現業務的讀寫分離策略。OceanBase 資料庫在讀取資料時,提供了兩種一致性級別:強一致性和弱一致性。

  • 強一致性是指請求路由給主副本讀取最新資料;

  • 弱一致性是指請求優先路由給備副本,不要求讀取最新資料。

透過應用側為執行的 SQL 新增 SQL Hint 來顯性開啟弱一致性讀就可以實現基於註釋的讀寫分離功能,同時也衍生出如下三種常用的讀寫分離策略:

6).KunlunBase

KunlunBase是一個開源、高效能的分散式關聯式資料庫,支援混合負載、PB級資料量管理並提供毫秒延遲的新一代資料庫解決方案。

KunlunBase 的讀寫分離在計算層的遠端查詢最佳化器內實現的,當使用者的SQL同時滿足如下條件:

  • 當前SQL型別為select;

  • SQL中不包含使用者自定義函式,除非當前事務為只讀事務;

  • 如果不在事務中(autocommit=on),則允許讀寫分離;

  • 如果語句在顯式事務中,則要滿足:

- 如果在只讀事務中,則允許讀寫分離;

- 如果在讀寫事務中,則該事務未更新過資料;

遠端查詢最佳化器就會將相應的SQL 執行計劃下發到從備機的節點上執行。KunlunServer 會根據以下規則選擇傳送select語句到目標儲存叢集的哪個備機節點:

  • 根據節點權重值選擇 (ro_weight)

  • 根據網路延遲(ping)

  • 根據主從副本的資料一致性延遲(latency)

來自 “ 韓鋒頻道 ”, 原文作者:韓鋒頻道;原文連結:https://mp.weixin.qq.com/s/gQZ5MQs9S4WFoNQtpcd-Pw,如有侵權,請聯絡管理員刪除。

相關文章