《Mysql - 讀寫分離有哪些坑?》


一:讀寫分離

  - 概念

    -  讀寫分離的主要目標就是分攤主庫的壓力

 

  - 基本架構

    -     - 

 

二:兩種讀寫分離的架構特點

  - 客戶端直連方案

    - 因為少了一層 proxy 轉發,所以查詢性能稍微好一點兒,並且整體架構簡單,排查問題更方便。

    - 但是這種方案,由於要了解后端部署細節,所以在出現主備切換、庫遷移等操作的時候,客戶端都會感知到,並且需要調整數據庫連接信息

    - 你可能會覺得這樣客戶端也太麻煩了,信息大量冗余,架構很丑。

    - 其實也未必,一般采用這樣的架構,一定會伴隨一個負責管理后端的組件,比如 Zookeeper,盡量讓業務端只專注於業務邏輯開發。

 

  - proxy 方案

    - 帶 proxy 的架構,對客戶端比較友好客戶端不需要關注后端細節,連接維護、后端信息維護等工作,都是由 proxy 完成的。

    - 但這樣的話,對后端維護團隊的要求會更高。而且,proxy 也需要有高可用架構。

    - 因此,帶 proxy 架構的整體就相對比較復雜。 

 

三:什么是“過期讀” ?

  -   “在從庫上會讀到系統的一個過期狀態”的現象,暫且稱之為“過期讀”。

 

四:處理 “過期讀” 的方案?

  - 強走主庫方案

  - sleep 方案;

  - 判斷主備無延遲方案;

  - 配合 semi-sync 方案;

  - 等主庫位點方案;

  - 等 GTID 方案。 

 

五:強走主庫方案

  - 原理

    - 強制走主庫方案其實就是,將查詢請求做分類

    - 對於必須要拿到最新結果的請求,強制將其發到主庫上。

      - 比如,在一個交易平台上,賣家發布商品以后,馬上要返回主頁面,看商品是否發布成功。

      - 那么,這個請求需要拿到最新的結果,就必須走主庫。

    - 對於可以讀到舊數據的請求,才將其發到從庫上。

      -  在這個交易平台上,買家來逛商鋪頁面,就算晚幾秒看到最新發布的商品,也是可以接受的。那么,這類請求就可以走從庫。

 

  - 問題

    - 這個方案最大的問題在於,有時候你會碰到“所有查詢都不能是過期讀”的需求,比如一些金融類的業務。

    - 這樣的話,你就要放棄讀寫分離,所有讀寫壓力都在主庫,等同於放棄了擴展性。

 

六:sleep 方案

  - 原理

    - 主庫更新后,讀從庫之前先 sleep 一下。具體的方案就是,類似於執行一條 select sleep(1) 命令。

    - 這個方案的假設是,大多數情況下主備延遲在 1 秒之內,做一個 sleep 可以有很大概率拿到最新的數據。

      - 如如在客戶端在下單完成后做1s的loading,其實等於變相的等待了從庫1s。

 

  - 問題

    - sleep 方案確實解決了一定場景下的過期讀問題。

    - 但,從嚴格意義上來說,這個方案存在的問題就是不精確。

    - 這個不精確包含了兩層意思:

      - 如果這個查詢請求本來 0.5 秒就可以在從庫上拿到正確結果,也會等 1 秒;

      - 如果延遲超過 1 秒,還是會出現過期讀。 

 

七:判斷主備無延遲方案

  - 原理

    - 通過 show slave status;結果

    - 

 

  - 對比  seconds_behind_master 判斷主備無延遲(精度S)

    - 每次從庫執行查詢請求前,先判斷 seconds_behind_master 是否已經等於 0。

    - 如果還不等於 0 ,那就必須等到這個參數變為 0 才能執行查詢請求。

 

  - 對比 點位 判斷主備無延遲

    -  Master_Log_File 和 Read_Master_Log_Pos,表示的是讀到的主庫的最新位點;

    -  Relay_Master_Log_File 和 Exec_Master_Log_Pos,表示的是備庫執行的最新位點。

    -  如果

      - Master_Log_File == Relay_Master_Log_File

      - Read_Master_Log_Pos == Exec_Master_Log_Pos

      - 這兩組值完全相同,就表示接收到的日志已經同步完成。

 

  - 對比  GTID 判斷主備無延遲

    - Auto_Position=1 ,表示這對主備關系使用了 GTID 協議。

    - Retrieved_Gtid_Set,是備庫收到的所有日志的 GTID 集合;

    - Executed_Gtid_Set,是備庫所有已經執行完成的 GTID 集合。

    - 如果這兩個集合相同,也表示備庫接收到的日志都已經同步完成。 

 

  - 小結

    - 雖然等待無延遲是可以解決問題,但是可能存在主備一直不一致,導致備庫無法讀取的問題。

    - 對比位點和對比 GTID 這兩種方法,都要比判斷 seconds_behind_master 是否為 0 更准確。

    - 這幾種辦法並沒有達到 “精確” 的程度,可能存在  主庫已經執行,但是還沒有發送給備庫的情況,導致過期讀。(通過 semi-sync 解決)

 

八: 等主庫點位方案 / 等GTID方案

  - 這兩個方案都是 在備庫執行,等待一定時間,如果在時間內 主庫點位 / GTID 同步,則在備庫執行,否則到主庫執行。

 

九:小結

  - 這幾種方案中,有的方案看上去是做了妥協,有的方案看上去不那么靠譜兒,但都是有實際應用場景的,你需要根據業務需求選擇。

  - 即使是最后等待位點和等待 GTID 這兩個方案,雖然看上去比較靠譜兒,但仍然存在需要權衡的情況。

    - 如果所有的從庫都延遲,那么請求就會全部落到主庫上,這時候會不會由於壓力突然增大,把主庫打掛了呢?

  - 其實,在實際應用中,這幾個方案是可以混合使用的。

  - 比如,先在客戶端對請求做分類,區分哪些請求可以接受過期讀,而哪些請求完全不能接受過期讀;然后,對於不能接受過期讀的語句,再使用等 GTID 或等位點的方案。

  - 但話說回來,過期讀在本質上是由一寫多讀導致的。

  - 在實際應用中,可能會有別的不需要等待就可以水平擴展的數據庫方案,但這往往是用犧牲寫性能換來的,也就是需要在讀性能和寫性能中取權衡。 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM