天天看點

高性能(二)三、讀寫分離和分庫分表

三、讀寫分離和分庫分表

1.讀寫分離

1.1 概述

将資料庫的讀寫操作分散到不同的資料庫節點上

高性能(二)三、讀寫分離和分庫分表
  • 通常一主多從一台主資料庫負責寫,多台從資料庫負責讀。
  • 主庫和從庫之間會進行資料同步,以保證從庫中資料的準确性。

1.2 問題及解決

1.2.1 問題

主從同步延遲

主庫寫完資料後同步到從庫之前主從資料不一緻

1.2.2 解決

(1)強制将讀請求路由到主庫處理

  • 适合将必須擷取最新資料的讀請求都交給主庫處理
  • 方案:Sharding-JDBC

    通過Sharding-JDBC的HintManager分片鍵值管理器強制使用主庫

HintManager hintManager = HintManager.getInstance();
hintManager.setMasterRouteOnly();
// 繼續JDBC操作
           

(2)延遲讀取

  • 适合對資料比較敏感的場景,在寫請求後避免立即讀操作

    如:支付成功後跳轉到一個支付成功頁面,點選傳回後才傳回自己的賬戶。

1.3 如何實作讀寫分離

1.3.1 正常步驟

(1)部署一主多從資料庫
(2)主從複制

保證主從資料庫之間的資料是實時同步的

(3)主庫處理寫請求,從庫處理讀請求

1.3.2 項目實作方式

(1)代理方式
高性能(二)三、讀寫分離和分庫分表
  • 應用和資料中間加一個代理層,并處理應用程式所有的資料請求
  • 代理層負責讀寫請求,将他們路由到對應的資料庫中
  • 中間件

    MySQL Router(官方)、Atlas(基于 MySQL Proxy)、Maxscale、MyCat。

(2)元件方式

引入第三方元件處理讀寫請求(推薦)

如:sharding-jdbc,引入jar包即可使用,非常友善且節省很多運維成本

官方連結:

sharding-jdbc 關于讀寫分離的操作

1.4 主從複制原理

1.4.1 概述

  • MySQL binlog(binary log 即二進制日志檔案)主要記錄了MySQL資料庫中資料的所有變化(資料庫執行的所有 DDL 和 DML 語句)
  • 根據主庫的 MySQL binlog 日志就能夠将主庫的資料同步到從庫中。

備注: binlog 還能幫助我們實作資料恢複。

1.4.2 步驟

高性能(二)三、讀寫分離和分庫分表
(1)主庫将資料庫中變化寫入到binlog中
(2)從庫連結主庫
(3)從庫建立一個I/O線程向主庫請求更新的binlog
(4)主庫會建立一個binlog dump線程來發送binlog,從庫的I/O線程負責接收
(5)從庫的I/O線程将接收的binlog寫入到relay log中
(6)從庫的SQL線程讀取relay log同步到本地資料庫(執行SQL)

1.4.3 延伸

(1)阿裡開源canal

實作MySQL資料庫之間或與其他資料源如elasticsearch之間的資料同步,底層也是依賴binlog,原理模拟MySQL主從複制過程

(2)Redis也是通過主從複制實作讀寫分離

詳見我另一篇部落格,連結:

資料庫及緩存之Redis(一)

2.分庫分表

解決資料庫存儲資料量過大問題

2.1 分庫

2.1.1 概念

将資料庫中的資料分散 到不同的資料庫中

2.1.2 分類

(1)垂直分庫
  • 把單一資料庫按照業務劃分,不同業務使用不同的資料庫
  • 舉例

    将資料庫中的使用者表、訂單表、商品表分别單獨拆分為使用者資料庫、訂單資料庫、商品資料庫

高性能(二)三、讀寫分離和分庫分表
(2)水準分庫
  • 把同一個表按一定規則拆分到不同的資料庫中,每個庫可以位于不同的伺服器上,實作水準擴充,解決單表的存儲和性能瓶頸問題
  • 舉例

    訂單表資料量太大,訂單表水準切分後的2張表分别放在不同資料庫

高性能(二)三、讀寫分離和分庫分表

2.2 分表

對單表的資料進行拆分

高性能(二)三、讀寫分離和分庫分表

2.2.1 垂直拆分

(1)對資料列拆分,把一張列比較多的表拆分為多張表
(2)舉例

将使用者資訊表中一些單獨列抽出來作為一張表

2.2.2 水準拆分

(1)對資料行拆分,把一張行比較多的表拆分為多張表,解決單一表資料量過大的問題。
(2)舉例

将使用者資訊表拆分為多個使用者資訊表,避免單一表資料量過大造成性能下降

備注: 為了提升性能,通常會選擇拆分後的多張表放在不同資料庫中,即水準分表和水準分庫結合

2.3 分庫分表的場景

(1)單表的資料達到千萬級别以上,資料庫讀寫速度比較緩慢。
(2)資料庫中的資料占用的空間越來越大,備份時間越來越長。
(3)應用的并發量太大

2.4 常見的分片算法

分片算法主要解決了資料被水準分片之後,資料究竟該存放哪個表的問題。

2.4.1 哈希分片

求指定key(如id)的哈希,然後根據哈希值确定資料應被放置在哪個表中。

适合随機讀寫而不适合經常需要範圍查詢的場景

2.4.2 範圍分片

按照特性的範圍區間(如時間、ID區間)來配置設定資料

  • 适合經常進行範圍查找而不适合随機讀寫的場景

    因為資料未被分散容易出現熱點資料的問題

  • 舉例

    如将id 為 1~299999 的記錄分到第一個庫,300000~599999 的分到第二個庫。

2.4.3 地理位置分片

根據地理位置(城市、地域)來配置設定資料

很多 NOSQL資料庫都支援

2.4.4 融合算法

靈活組合多種分片算法

如将哈希分片和範圍分片組合

2.5 分庫分表問題

2.5.1 無法join操作

  • 同一個資料庫中的表分布在不同的資料庫中無法使用join操作
  • 需要在一個資料庫中查詢到一個資料再去另外一個資料庫彙總查詢對應的資料

2.5.2 事務問題

  • 同一個資料庫中的表分布在不同的資料庫中,單個操作涉及到多個資料庫,資料庫自帶的事務無法解決

2.5.3 分布式ID

  • 分庫後,資料遍布在不同伺服器上的資料庫中,資料庫的自增主鍵已經沒辦法滿足生成的主鍵唯一。
  • 需要引入分布式ID

2.5.4 其他

  • 需要更多的資料庫伺服器,成本上升了

2.6 分庫分表方案

ShardingSphere 項目

高性能(二)三、讀寫分離和分庫分表
(1)包括 Sharding-JDBC、Sharding-Proxy 和 Sharding-Sidecar,由京東數科巨佬維護
(2)功能完善

支援讀寫分離和分庫分表、分布式事務、資料庫治理等功能。

(3)生态體系完善

社群活躍,文檔完善,更新和釋出比較頻繁

入門可以看看下面這篇文章:

《芋道 Spring Boot 分庫分表入門》

2.7 分庫分表資料遷移

2.7.1 停機遷移

  • 系統使用的人數非常少的時候,如淩晨 2 點,挂公告系統要維護更新預計 1 小時。
  • 再寫個腳本老庫的資料寫到新庫中

2.7.2 雙寫方案

針對不能停機遷移場景

原理如下:

(1)對老庫的更新操作(增删改),同時也要寫入新庫(雙寫)
(2)還需要自己寫腳本将老庫中的資料和新庫的資料做比對
  • 在遷移過程,雙寫隻會讓被更新操作過的老庫中的資料同步到新庫
  • 如果新庫中沒有,那咱們就把資料插入到新庫
  • 如果新庫有,舊庫沒有,就把新庫對應的資料删除(備援資料清理)
(3)重複上一步的操作,直到老庫和新庫的資料一緻為止

備注:

項目中實施雙寫很麻煩很容易會出現問題,建議使用資料庫同步工具 Canal 做增量資料遷移(還是依賴 binlog,開發和維護成本較低)。

上一篇跳轉—高性能(一)

本篇文章主要參考連結如下:

參考連結1-JavaGuide

持續更新中…

随心所往,看見未來。Follow your heart,see light!

歡迎點贊、關注、留言,一起學習、交流!

繼續閱讀