使用shardingsphere實(shí)現(xiàn)mysql數(shù)據(jù)庫分片方式
在大數(shù)據(jù)時(shí)代,隨著業(yè)務(wù)數(shù)據(jù)量的不斷增長,單一的數(shù)據(jù)庫往往難以承載大規(guī)模的數(shù)據(jù)處理需求。數(shù)據(jù)庫分片(Sharding)是一種有效的數(shù)據(jù)庫擴(kuò)展技術(shù),通過將數(shù)據(jù)分布到多個(gè)數(shù)據(jù)庫實(shí)例上,提高系統(tǒng)的性能和可擴(kuò)展性。
ShardingSphere 是一款開源的分布式數(shù)據(jù)庫中間件,可以幫助我們輕松實(shí)現(xiàn)數(shù)據(jù)庫分片。
本文的目的是介紹如何快速上手使用 ShardingSphere 來實(shí)現(xiàn) MySQL 數(shù)據(jù)庫分片。
一、ShardingSphere 簡介
ShardingSphere 是 Apache 基金會下的一個(gè)開源項(xiàng)目,提供分布式數(shù)據(jù)庫中間件解決方案。ShardingSphere 已經(jīng)在2020年4月16日從 Apache 孵化器畢業(yè),成為 Apache 頂級項(xiàng)目。其主要功能包括數(shù)據(jù)分片(Sharding)、讀寫分離、分布式事務(wù)以及數(shù)據(jù)加密等。ShardingSphere 主要由三個(gè)核心組件組成:
- Sharding-JDBC:輕量級的 Java 框架,直接集成在應(yīng)用程序中,提供數(shù)據(jù)庫分片、讀寫分離等功能。需要在
spring boot中集成,編寫相關(guān)的配置。如果分片策略用默認(rèn)的4種,那可以只改配置就好了。如果分片策略很特殊,可以通過實(shí)現(xiàn)抽象類,寫自定義的方法進(jìn)行分片分庫。 - Sharding-Proxy:獨(dú)立部署的數(shù)據(jù)庫代理,支持所有兼容
MySQL、PostgreSQL協(xié)議的客戶端。原來程序連接到這個(gè)代理就可以實(shí)現(xiàn)分片和分庫。程序不需要任何改變。 - Sharding-Sidecar(Plan):云原生環(huán)境下的數(shù)據(jù)庫代理,與
Kubernetes等平臺集成。
1.1 對比
| Sharding-JDBC | Sharding-Proxy | Sharding-Sidecar | |
|---|---|---|---|
| 數(shù)據(jù)庫 | 任意 | MySQL/PostgreSQL | MySQL/PostgreSQL |
| 連接消耗數(shù) | 高 | 低 | 高 |
| 異構(gòu)語言 | 僅Java | 任意 | 任意 |
| 性能 | 損耗低 | 損耗略高 | 損耗低 |
| 無中心化 | 是 | 否 | 是 |
| 靜態(tài)入口 | 無 | 有 | 無 |
1.2 核心概念
分庫分表中最重要的核心概念有兩個(gè),即路由鍵和分片算法,這兩個(gè)將決定數(shù)據(jù)分片的位置,先稍微解釋一下這兩個(gè)概念:
- 路由鍵:也被稱為分片鍵,也就是作為數(shù)據(jù)分片的基準(zhǔn)字段,可以是一個(gè)或多個(gè)字段組成。
- 分片算法:基于路由鍵做一定邏輯處理,從而計(jì)算出一個(gè)最終節(jié)點(diǎn)位置的算法。
舉個(gè)例子來感受一下,好比按 user_id 將用戶表數(shù)據(jù)分片,每八百萬條數(shù)據(jù)劃分一張表,那在這里,user_id 就是路由鍵,而按user_id做范圍判斷則屬于分片算法,一張表中的所有數(shù)據(jù)都會依據(jù)這兩個(gè)基礎(chǔ),后續(xù)對所有的讀寫SQL進(jìn)行改寫,從而定位到具體的庫、表位置。

在Sharding-Sphere這套技術(shù)中,無論是JDBC還是Proxy產(chǎn)品,工作的流程都遵循上述這個(gè)原則,里面除開上面介紹的路由鍵和分片算法的概念外,還有邏輯表、真實(shí)表、數(shù)據(jù)節(jié)點(diǎn)這三個(gè)概念:
- 邏輯表:提供給應(yīng)用程序操作的表名,程序可以像操作原本的單表一樣,靈活的操作邏輯表。
- 真實(shí)表:在各個(gè)數(shù)據(jù)庫節(jié)點(diǎn)上真實(shí)存在的物理表,但表名一般都會和邏輯表存在偏差。
- 數(shù)據(jù)節(jié)點(diǎn):主要是用于定位具體真實(shí)表的庫表名稱,如DB1.tb_user1、DB2.tb_user2…
- 均勻分布:指一張表的數(shù)量在每個(gè)數(shù)據(jù)源中都是一致的。
- 自定義分布:指一張表在每個(gè)數(shù)據(jù)源中,具體的數(shù)量由自己來定義,上圖就是一種自定義分布。
以 Java 程序?yàn)槔?,編寫業(yè)務(wù)代碼時(shí)寫的 SQL 語句,會直接基于邏輯表進(jìn)行操作,邏輯表并不是一種真實(shí)存在的表結(jié)構(gòu),而是提供給 Sharding-Sphere 使用的,當(dāng) Sharding-Sphere 接收到一條操作某張邏輯表的 SQL語句時(shí),它會根據(jù)已配置好的路由鍵和分片算法,對相應(yīng)的 SQL語句進(jìn)行解析,然后計(jì)算出 SQL要落入的數(shù)據(jù)節(jié)點(diǎn),最后再將語句發(fā)給具體的真實(shí)表上處理即可。
1.3 Sharding-Sphere中的表概念
除開上述的一些核心概念外,在Sharding-Sphere中為了解決某些問題,同時(shí)還有一些表概念,如廣播表、綁定表、單表、動態(tài)表等,接著簡單介紹一下這些概念。
1.3.1 綁定表
一般的項(xiàng)目基本上都會存在主外鍵,雖然不會在表上添加主外鍵約束,但也會從邏輯上保障主外鍵關(guān)系,但經(jīng)過水平分庫后,所有的讀寫操作會基于路由鍵去分片。
那此時(shí)以訂單表、訂單詳情表為例,假設(shè)用戶選擇了三個(gè)購物車的商品提交訂單,此時(shí)會產(chǎn)生
- 一條訂單記錄
- 以及對應(yīng)的三條訂單詳情記錄。
假設(shè)商品庫有兩個(gè)水平節(jié)點(diǎn),兩個(gè)商品庫都具備訂單表、訂單詳情表,而訂單表的路由鍵是 order_id,訂單詳情表的路由鍵是 order_info_id ,數(shù)據(jù)分片的路由算法為取模,那此時(shí)數(shù)據(jù)的落庫情況為:
- 訂單詳情2數(shù)據(jù)落入 DB0 商品庫中。
- 訂單詳情1、詳情3數(shù)據(jù)落入 DB1 商品庫中。
但此時(shí)訂單表和訂單詳情表的數(shù)據(jù)是存在外鍵關(guān)系的,如果按照上述的路由方式去對數(shù)據(jù)做分片,最終就會導(dǎo)致通過 order_id=1 的訂單 ID 查詢訂單詳情時(shí),只能在 DB1 中查詢到兩條訂單詳情數(shù)據(jù),這個(gè)問題顯然是業(yè)務(wù)上無法接受的。通常我們會將這兩張表配置為綁定表,以確保它們在同一個(gè)分片中,從而優(yōu)化訂單相關(guān)的聯(lián)表查詢操作。
1.3.2 廣播表
廣播表是指在所有分片數(shù)據(jù)源中都存在的一張表,且數(shù)據(jù)內(nèi)容完全相同。這種表通常用于存儲一些公共的、全局性的數(shù)據(jù),比如系統(tǒng)配置、國家代碼等。由于廣播表的數(shù)據(jù)是全局一致的,當(dāng)有更新操作時(shí),需要將變更同步到所有的分片數(shù)據(jù)源中。
- 在不同的庫需要數(shù)據(jù)的表中冗余字段,把常用的字段放到需要要數(shù)據(jù)的表中,避免跨庫連表。
- 選擇同步數(shù)據(jù),通過廣播表/網(wǎng)絡(luò)表/全局表將對應(yīng)的表數(shù)據(jù)直接完全同步一份到相應(yīng)庫中。
- 在設(shè)計(jì)庫表拆分時(shí)創(chuàng)建 ER 綁定表,具備主外鍵的表放在一個(gè)庫,保證數(shù)據(jù)落到同一數(shù)據(jù)庫
- Java 系統(tǒng)中組裝數(shù)據(jù),通過調(diào)用對方服務(wù)接口的形式獲取數(shù)據(jù),然后在程序中組裝后返回
這四種方案都能夠解決需要跨庫 Join 的問題,但第二種方案提到了廣播表/網(wǎng)絡(luò)表/全局表似乎之前沒聽說過對嘛,這其實(shí)是分庫分表中的名詞。場景:假設(shè)有一個(gè) t_country 表,存儲國家和地區(qū)的信息,這些信息在整個(gè)系統(tǒng)中是統(tǒng)一的,因此可以將 t_country 作為廣播表,在所有數(shù)據(jù)庫實(shí)例中都保存一份副本。
但往往垂直分庫的場景中,第四種方案是最常用的,因?yàn)榉謳旆直淼捻?xiàng)目中,Java 業(yè)務(wù)系統(tǒng)那邊也絕對采用了分布式架構(gòu),因此通過調(diào)用對端 API 接口來獲取數(shù)據(jù),是分布式系統(tǒng)最為常見的一種現(xiàn)象。
1.3.3 單表
單表的含義比較簡單,并非所有的表都需要做分庫分表操作,所以當(dāng)一張表的數(shù)據(jù)無需分片到多個(gè)數(shù)據(jù)源中時(shí),就可將其配置為單表,這樣所有的讀寫操作最終都會落入這一張單表中處理。
二、ShardingSphere-JDBC
Sharding-JDBC是 ShardingSphere 的第一個(gè)產(chǎn)品,也是 ShardingSphere 的前身。 它定位為輕量級 Java 框架,在 Java 的 JDBC層提供的額外服務(wù)。它使用客戶端直連數(shù)據(jù)庫,以 jar 包形式提供服務(wù),無需額外部署和依賴,可理解為增強(qiáng)版的 JDBC 驅(qū)動,完全兼容 JDBC 和各種 ORM 框架。
Apache ShardingSphere-JDBC 可以通過Java 和 YAML 這 2 種方式進(jìn)行配置,開發(fā)者可根據(jù)場景選擇適合的配置方式。
原理
Sharding-JDBC 中的路由結(jié)果是通過分片字段和分片方法來確定的,如果查詢條件中有 id 字段的情況還好,查詢將會落到某個(gè)具體的分片
如果查詢沒有分片的字段,會向所有的db或者是表都會查詢一遍,讓后封裝結(jié)果集給客戶端。

接下來會搭建一個(gè)簡單的SpringBoot+MyBatis項(xiàng)目,結(jié)合Sharding-Sphere-JDBC實(shí)現(xiàn)水平分庫~
spring boot整合
<!-- 分表分庫依賴 -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.2.1</version>
</dependency>
數(shù)據(jù)庫
接著先在數(shù)據(jù)庫中創(chuàng)建db_sharding_01、db_sharding_02兩個(gè)庫,我這里用偽集群的方式搭建水平庫,畢竟線上只需要把數(shù)據(jù)庫地址改為不同的機(jī)器IP即可
接著分別再在兩個(gè)水平庫中,創(chuàng)建用戶表、訂單表、訂單詳情表、商品表(兩張),這四張表是接下來用于測試的表,SQL如下:
drop table if exists `user_info`; create table `user_info` ( `user_id` bigint not null comment '用戶id', `user_name` varchar(255) comment '用戶姓名', `user_sex` varchar(255) comment '用戶性別', `user_age` int(8) not null comment '用戶年齡', primary key (`user_id`) using btree ) engine = InnoDB character set = utf8 collate = utf8_general_ci row_format = compact; drop table if exists `shoping_00`; create table `shoping_00` ( `shoping_id` bigint not null comment '商品id', `shoping_name` varchar(255) comment '商品名稱', `shoping_price` int(8) not null comment '商品價(jià)格', primary key (`shoping_id`) using btree ) engine = InnoDB character set = utf8 collate = utf8_general_ci row_format = compact; drop table if exists `shoping_01`; create table `shoping_01` ( `shoping_id` bigint not null comment '商品id', `shoping_name` varchar(255) comment '商品名稱', `shoping_price` int(8) not null comment '商品價(jià)格', primary key (`shoping_id`) using btree ) engine = InnoDB character set = utf8 collate = utf8_general_ci row_format = compact; drop table if exists `order`; create table `order` ( `order_id` bigint not null comment '訂單號', `order_price` int(8) not null comment '訂單總金額', `user_id` bigint not null comment '用戶id', primary key (`order_id`) using btree ) engine = InnoDB character set = utf8 collate = utf8_general_ci row_format = compact; drop table if exists `order_info`; create table `order_info` ( `order_info_id` bigint not null comment '訂單詳情號', `order_id` bigint not null comment '訂單號', `shoping_name` varchar(255) comment '商品名稱', `shoping_price` int(8) not null comment '商品價(jià)格', primary key (`order_info_id`) using btree, index `key_order_id`(`order_id`) using btree ) engine = InnoDB character set = utf8 collate = utf8_general_ci row_format = compact;
分庫分表的核心配置
Sharding-Sphere的所有產(chǎn)品對業(yè)務(wù)代碼都是零侵入的,無論是Sharding-JDBC也好,Sharding-Proxy也罷,都不需要更改業(yè)務(wù)代碼,這也就意味著大家在分庫分表環(huán)境下做業(yè)務(wù)開發(fā)時(shí),可以像傳統(tǒng)的單庫開發(fā)一樣輕松,Sharding-Sphere中最主要的是對配置文件的更改,Sharding-JDBC主要修改application.properties/yml文件,Sharding-Proxy主要修改自身的配置文件。
多數(shù)據(jù)源配置
spring:
shardingsphere:
mode:
type: Standalone
repository:
type: JDBC
datasource:
names: ds0,ds1
ds0:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: 「數(shù)據(jù)庫節(jié)點(diǎn)1的地址」
username: 「數(shù)據(jù)庫節(jié)點(diǎn)1的賬號」
password: 「數(shù)據(jù)庫節(jié)點(diǎn)1的密碼」
ds1:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: 「數(shù)據(jù)庫節(jié)點(diǎn)2的地址」
username: 「數(shù)據(jù)庫節(jié)點(diǎn)1的賬號」
password: 「數(shù)據(jù)庫節(jié)點(diǎn)1的密碼」
上述這組配置中,需要通過names配置多個(gè)數(shù)據(jù)源的別名,接著需要為每個(gè)別名配置對應(yīng)的數(shù)據(jù)源信息,按照上述方式編寫好配置文件后,則表示完成了多數(shù)據(jù)源的配置。
多數(shù)據(jù)源可用性測試
為了確保多數(shù)據(jù)源的可用性,接著先簡單配置一張表:
spring:
shardingsphere:
props:
sql-show: true
rules:
sharding:
tables:
shoping:
actual-data-nodes: ds0.shoping_00
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Linux下MYSQL 5.7 找回root密碼的問題(親測可用)
這篇文章主要介紹了Linux下MYSQL 5.7 找回root密碼的問題(親測可用),通過 --skip-grant-tables 找回,新增完成后,:wq 保存退出,重啟mysqld服務(wù),具體內(nèi)容詳情跟隨小編一起看看吧2021-10-10
如何選擇合適的MySQL日期時(shí)間類型來存儲你的時(shí)間
這篇文章主要介紹了如何選擇合適的MySQL日期時(shí)間類型來存儲你的時(shí)間,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2020-08-08
mysql數(shù)據(jù)類型和字段屬性原理與用法詳解
這篇文章主要介紹了mysql數(shù)據(jù)類型和字段屬性,結(jié)合實(shí)例形式分析了mysql數(shù)據(jù)類型和字段屬性基本概念、原理、分類、用法及操作注意事項(xiàng),需要的朋友可以參考下2020-04-04
MySQL復(fù)制出錯 Last_SQL_Errno:1146的解決方法
這篇文章主要介紹了MySQL復(fù)制出錯 Last_SQL_Errno:1146的解決方法,需要的朋友可以參考下2016-07-07
MySQL將多行數(shù)據(jù)轉(zhuǎn)換為一行數(shù)據(jù)的實(shí)現(xiàn)示例
在MySQL中,GROUP_CONCAT函數(shù)可以將多個(gè)記錄的列值連接成一個(gè)字符串,適用于將多行數(shù)據(jù)合并為單行顯示,本文就來詳細(xì)的介紹一下,感興趣的可以了解一下2024-09-09
給MySQL表中的字段設(shè)置默認(rèn)值的兩種方法
在MySQL中,我們可以為表的字段設(shè)置默認(rèn)值,以確保在插入新記錄時(shí),如果沒有為該字段指定值,將使用默認(rèn)值,要為MySQL表中的字段設(shè)置默認(rèn)值,我們可以在創(chuàng)建表時(shí)或者在已存在的表上使用ALTER TABLE語句進(jìn)行修改,下面將展示兩種設(shè)置默認(rèn)值的方法,需要的朋友可以參考下2023-11-11
MySQL和連接相關(guān)的timeout 的詳細(xì)整理
這篇文章主要介紹了MySQL和連接相關(guān)的timeout 的詳細(xì)整理的相關(guān)資料,本文主要總結(jié)下和連接有關(guān)的timeout,需要的朋友可以參考下2017-08-08
Mysql5.7.17 winx64.zip解壓縮版安裝配置圖文教程
這篇文章主要介紹了Mysql5.7.17 winx64.zip解壓縮版安裝配置圖文教程,需要的朋友可以參考下2018-03-03

