天天看點

Sharding-Jdbc實作讀寫分離、分庫分表,妙!

程式員的成長之路

網際網路/程式員/技術/資料共享 

閱讀本文大概需要 15 分鐘。

ShardingSphere-Jdbc定位為輕量級Java架構,在Java的Jdbc層提供的額外服務。它使用用戶端直連資料庫,以jar包形式提供服務,可了解為增強版的Jdbc驅動,完全相容Jdbc和各種ORM架構

Sharding-Jdbc實作讀寫分離、分庫分表,妙!

1)建立主伺服器所需目錄

2)定義主伺服器配置檔案

3)建立并啟動mysql主服務

4)添加複制master資料的使用者reader,供從伺服器使用

5)建立從伺服器所需目錄,編輯配置檔案

6)建立并運作mysql從伺服器

7)在從伺服器上配置連接配接主伺服器的資訊

首先主伺服器上檢視<code>master_log_file</code>、<code>master_log_pos</code>兩個參數,然後切換到從伺服器上進行主伺服器的連接配接資訊的設定

主服務上執行:

docker檢視主伺服器容器的ip位址

從伺服器上執行:

8)從伺服器啟動I/O 線程和SQL線程

Slave_IO_Running: Yes,Slave_SQL_Running: Yes即表示啟動成功

1)redo log(重做日志)

InnoDB首先将redo log放入到redo log buffer,然後按一定頻率将其重新整理到redo log file

下列三種情況下會将redo log buffer重新整理到redo log file:

Master Thread每一秒将redo log buffer重新整理到redo log file

每個事務送出時會将redo log buffer重新整理到redo log file

當redo log緩沖池剩餘空間小于1/2時,會将redo log buffer重新整理到redo log file

MySQL裡常說的WAL技術,全稱是Write Ahead Log,即當事務送出時,先寫redo log,再修改頁。也就是說,當有一條記錄需要更新的時候,InnoDB會先把記錄寫到redo log裡面,并更新Buffer Pool的page,這個時候更新操作就算完成了

Buffer Pool是實體頁的緩存,對InnoDB的任何修改操作都會首先在Buffer Pool的page上進行,然後這樣的頁将被标記為髒頁并被放到專門的Flush List上,後續将由專門的刷髒線程階段性的将這些頁面寫入磁盤

InnoDB的redo log是固定大小的,比如可以配置為一組4個檔案,每個檔案的大小是1GB,循環使用,從頭開始寫,寫到末尾就又回到開頭循環寫(順序寫,節省了随機寫磁盤的IO消耗)

Sharding-Jdbc實作讀寫分離、分庫分表,妙!

Write Pos是目前記錄的位置,一邊寫一邊後移,寫到第3号檔案末尾後就回到0号檔案開頭。Check Point是目前要擦除的位置,也是往後推移并且循環的,擦除記錄前要把記錄更新到資料檔案

Write Pos和Check Point之間空着的部分,可以用來記錄新的操作。如果Write Pos追上Check Point,這時候不能再執行新的更新,需要停下來擦掉一些記錄,把Check Point推進一下

當資料庫發生當機時,資料庫不需要重做所有的日志,因為Check Point之前的頁都已經重新整理回磁盤,隻需對Check Point後的redo log進行恢複,進而縮短了恢複的時間

當緩沖池不夠用時,根據LRU算法會溢出最近最少使用的頁,若此頁為髒頁,那麼需要強制執行Check Point,将髒頁重新整理回磁盤

2)binlog(歸檔日志)

MySQL整體來看就有兩塊:一塊是Server層,主要做的是MySQL功能層面的事情;還有一塊是引擎層,負責存儲相關的具體事宜。redo log是InnoDB引擎特有的日志,而Server層也有自己的日志,稱為binlog

binlog記錄了對MySQL資料庫執行更改的所有操作,不包括SELECT和SHOW這類操作,主要作用是用于資料庫的主從複制及資料的增量恢複

使用mysqldump備份時,隻是對一段時間的資料進行全備,但是如果備份後突然發現資料庫伺服器故障,這個時候就要用到binlog的日志了

binlog格式有三種:STATEMENT,ROW,MIXED

STATEMENT模式:binlog裡面記錄的就是SQL語句的原文。優點是并不需要記錄每一行的資料變化,減少了binlog日志量,節約IO,提高性能。缺點是在某些情況下會導緻master-slave中的資料不一緻

ROW模式:不記錄每條SQL語句的上下文資訊,僅需記錄哪條資料被修改了,修改成什麼樣了,解決了STATEMENT模式下出現master-slave中的資料不一緻。缺點是會産生大量的日志,尤其是alter table的時候會讓日志暴漲

MIXED模式:以上兩種模式的混合使用,一般的複制使用STATEMENT模式儲存binlog,對于STATEMENT模式無法複制的操作使用ROW模式儲存binlog,MySQL會根據執行的SQL語句選擇日志儲存方式

3)redo log和binlog日志的不同

redo log是InnoDB引擎特有的;binlog是MySQL的Server層實作的,所有引擎都可以使用

redo log是實體日志,記錄的是在某個資料也上做了什麼修改;binlog是邏輯日志,記錄的是這個語句的原始邏輯,比如給ID=2這一行的c字段加1

redo log是循環寫的,空間固定會用完;binlog是可以追加寫入的,binlog檔案寫到一定大小後會切換到下一個,并不會覆寫以前的日志

4)兩階段送出

執行器和InnoDB引擎在執行這個update語句時的内部流程:

執行器先找到引擎取ID=2這一行。ID是主鍵,引擎直接用樹搜尋找到這一行。如果ID=2這一行所在的資料也本來就在記憶體中,就直接傳回給執行器;否則,需要先從磁盤讀入記憶體,然後再傳回

執行器拿到引擎給的行資料,把這個值加上1,得到新的一行資料,再調用引擎接口寫入這行新資料

引擎将這行新資料更新到記憶體中,同時将這個更新操作記錄到redo log裡面,此時redo log處于prepare狀态。然後告知執行器執行完成了,随時可以送出事務

執行器生成這個操作的binlog,并把binlog寫入磁盤

執行器調用引擎的送出事務接口,引擎把剛剛寫入的redo log改成送出狀态,更新完成

update語句的執行流程圖如下,圖中淺色框表示在InnoDB内部執行的,深色框表示是在執行器中執行的

Sharding-Jdbc實作讀寫分離、分庫分表,妙!

将redo log的寫入拆成了兩個步驟:prepare和commit,這就是兩階段送出

Sharding-Jdbc實作讀寫分離、分庫分表,妙!

從庫B和主庫A之間維持了一個長連接配接。主庫A内部有一個線程,專門用于服務從庫B的這個長連接配接。一個事務日志同步的完整過程如下:

在從庫B上通過change master指令,設定主庫A的IP、端口、使用者名、密碼,以及要從哪個位置開始請求binlog,這個位置包含檔案名和日志偏移量

在從庫B上執行start slave指令,這時從庫會啟動兩個線程,就是圖中的I/O線程和SQL線程。其中I/O線程負責與主庫建立連接配接

主庫A校驗完使用者名、密碼後,開始按照從庫B傳過來的位置,從本地讀取binlog,發給B

從庫B拿到binlog後,寫到本地檔案,稱為中繼日志

SQL線程讀取中繼日志,解析出日志裡的指令,并執行

由于多線程複制方案的引入,SQL線程演化成了多個線程

主從複制不是完全實時地進行同步,而是異步實時。這中間存在主從服務之間的執行延時,如果主伺服器的壓力很大,則可能導緻主從伺服器延時較大

1)、建立Springboot工程,引入相關依賴

2)、application.properties配置檔案

3)、建立t_user表

4)、定義Controller、Mapper、Entity

5)、驗證

啟動日志中三個資料源初始化成功:

Sharding-Jdbc實作讀寫分離、分庫分表,妙!

調用<code>http://localhost:8080/api/user/save</code>一直進入到ds1主節點

Sharding-Jdbc實作讀寫分離、分庫分表,妙!

調用<code>http://localhost:8080/api/user/findUsers</code>一直進入到ds2、ds3節點,并且輪詢進入

Sharding-Jdbc實作讀寫分離、分庫分表,妙!

水準拆分:同一個表的資料拆到不同的庫不同的表中。可以根據時間、地區或某個業務鍵次元,也可以通過hash進行拆分,最後通過路由通路到具體的資料。拆分後的每個表結構保持一緻

垂直拆分:就是把一個有很多字段的表給拆分成多個表,或者是多個庫上去。每個庫表的結構都不一樣,每個庫表都包含部分字段。一般來說,可以根據業務次元進行拆分,如訂單表可以拆分為訂單、訂單支援、訂單位址、訂單商品、訂單擴充等表;也可以,根據資料冷熱程度拆分,20%的熱點字段拆到一個表,80%的冷字段拆到另外一個表

Sharding-Jdbc實作讀寫分離、分庫分表,妙!

一般資料庫的拆分也是有一個過程的,一開始是單表,後面慢慢拆成多表。那麼我們就看下如何平滑的從MySQL單表過度到MySQL的分庫分表架構

利用MySQL+Canal做增量資料同步,利用分庫分表中間件,将資料路由到對應的新表中

利用分庫分表中間件,全量資料導入到對應的新表中

通過單表資料和分庫分表資料兩兩比較,更新不比對的資料到新表中

資料穩定後,将單表的配置切換到分庫分表配置上

Sharding-Jdbc實作讀寫分離、分庫分表,妙!

使用者資料根據訂單id%2拆分為2個表,分别是:t_order0和t_order1。他們的邏輯表名是:t_order

Sharding-Jdbc實作讀寫分離、分庫分表,妙!

多資料源相同表:

多資料源不同表:

單庫分表:

全部手動指定:

上面的配置通過user_id%2來決定具體資料源,通過order_id%2來決定具體表

<code>insert into t_order(user_id,order_id) values(2,3),user_id%2 = 0</code>使用資料源ds0,<code>order_id%2 = 1</code>使用t_order1,insert語句最終操作的是資料源ds0的t_order1表。

Sharding-Jdbc可以配置分布式主鍵生成政策。預設使用雪花算法(snowflake),生成64bit的長整型資料,也支援UUID的方式

需求:

對1000w的使用者資料進行分庫分表,對使用者表的資料進行分表和分庫的操作。根據年齡奇數存儲在t_user1,偶數t_user0,同時性别奇數存儲在ds1,偶數ds0

表結構:

兩個資料庫中都包含t_user0和t_user1兩張表

application.properties:

測試類:

官方文檔:

https://shardingsphere.apache.org/document/current/cn/overview/

視訊資料:

https://www.bilibili.com/video/BV1ei4y1K7dn

&lt;END&gt;