目錄
一. 在Docker中安裝ShardingSphere
二. 實踐:水平分片
2.1 應用場景
2.2 架構圖
2.3 服務器規劃
?2.4 創建server-user容器
2.5 創建server-order0和server-order1容器
2.6.日志配置
2.7 數據節點配置
2.8.測試數據節點
2.8.1.測試server_order0.t_order0節點
2.8.2.測試server_order0.t_order1節點
2.8.3.測試server_order1.t_order0節點
?2.8.4.測試server_order1.t_order1節點
2.8.5.完整配置?
2.6 水平分庫配置
2.6.1.行表達式分片算法
2.6.2.取模分片算法
2.7 水平分表配置
2.7.1.配置分表策略
2.7.2 HASH取模分片算法
2.7.3.測試?
2.8 分布式序列算法
2.8.1.分布式環境下主鍵的自動生成存在的問題
2.8.2 UUID
2.8.3? 雪花算法
2.9 多表關聯查詢?
2.9.1?配置策略
2.9.2.測試
2.9.3. 綁定表
2.11 廣播表
2.11.1.什么是廣播表
?2.11.2?配置廣播表
?
一. 在Docker中安裝ShardingSphere
?接下來我將講解ShardingSphere在Docker里面的安裝
創建Docker容器所需的映射目錄
我們必須提前進行創建
mkdir -p /bit/shardingsphere/proxy/conf
mkdir -p /bit/shardingsphere/proxy/ext-lib
mkdir -p /bit/shardingsphere/proxy/logs
?
創建Docker容器
這個 docker run
命令是用來安裝并啟動一個 ShardingSphere-Proxy 容器的。ShardingSphere-Proxy 是一個透明的數據庫代理,它允許你像連接普通 MySQL 服務器一樣連接它,但它背后可以管理多個數據庫實例,實現分庫分表、讀寫分離等高級功能。?
docker run -d \-p 3307:3307 \-v /bit/shardingsphere/proxy/conf:/opt/shardingsphere-proxy/conf \-v /bit/shardingsphere/proxy/ext-lib:/opt/shardingsphere-proxy/ext-lib \-v /bit/shardingsphere/proxy/logs:/opt/shardingsphere-proxy/logs \-e JVM_OPTS="-Xms256m -Xmx256m -Xmn128m" \--name ss-proxy \apache/shardingsphere-proxy:5.3.2
這些參數啥意思呢? 我們看看
docker run -d \
docker run
:Docker 命令,用于創建并啟動一個新的容器。-d
:表示以后臺(detached)模式運行容器。容器啟動后,命令行會立即返回,不會阻塞。
-p 3307:3307 \
-p
:用于映射端口。3307:3307
:將宿主機(Host)的?3307
?端口映射到容器(Container)內部的?3307
?端口。這意味著,外部應用程序(如你的應用代碼、MySQL 客戶端工具)需要連接宿主機的?3307
?端口來訪問 ShardingSphere-Proxy 服務。
-v /bit/shardingsphere/proxy/conf:/opt/shardingsphere-proxy/conf \
-v
:用于創建數據卷(Volume),實現宿主機目錄與容器目錄的綁定(掛載)。/bit/shardingsphere/proxy/conf:/opt/shardingsphere-proxy/conf
:將宿主機上的?/bit/shardingsphere/proxy/conf
?目錄掛載到容器內的?/opt/shardingsphere-proxy/conf
?目錄。這個目錄通常存放 ShardingSphere-Proxy 的核心配置文件,如?server.yaml
(服務配置)和?config-xxx.yaml
(數據分片/讀寫分離規則配置)。通過掛載,你可以在宿主機上直接修改配置文件,而無需進入容器內部,修改后重啟容器即可生效。
-v /bit/shardingsphere/proxy/ext-lib:/opt/shardingsphere-proxy/ext-lib \
/bit/shardingsphere/proxy/ext-lib:/opt/shardingsphere-proxy/ext-lib
:將宿主機上的?/bit/shardingsphere/proxy/ext-lib
?目錄掛載到容器內的?/opt/shardingsphere-proxy/ext-lib
?目錄。這個目錄用于存放用戶自定義的 JAR 包,例如:- 數據庫驅動(如果官方鏡像里沒有你需要的數據庫驅動,如 Oracle)。
- 自定義的分片算法類。
- 自定義的加密算法類。
- 自定義的分布式序列生成器。
-v /bit/shardingsphere/proxy/logs:/opt/shardingsphere-proxy/logs \
/bit/shardingsphere/proxy/logs:/opt/shardingsphere-proxy/logs
:將宿主機上的?/bit/shardingsphere/proxy/logs
?目錄掛載到容器內的?/opt/shardingsphere-proxy/logs
?目錄。ShardingSphere-Proxy 的運行日志(如?shardingsphere.log
)會輸出到這個目錄。掛載后,你可以在宿主機上方便地查看和管理日志文件,即使容器被刪除,日志也保留在宿主機上。
-e JVM_OPTS="-Xms256m -Xmx256m -Xmn128m" \
-e
:用于設置環境變量(Environment Variable)。JVM_OPTS="-Xms256m -Xmx256m -Xmn128m"
:將環境變量?JVM_OPTS
?的值設置為指定的 JVM 內存參數。當 ShardingSphere-Proxy 啟動時,會讀取這個環境變量,并將其作為 JVM 的啟動參數,從而控制其內存使用。
--name ss-proxy \
--name
:為啟動的容器指定一個名稱。ss-proxy
:這個容器的名稱被設置為?ss-proxy
。之后你可以使用?docker stop ss-proxy
,?docker start ss-proxy
,?docker logs ss-proxy
?等命令來管理這個容器,比使用容器的 ID 更方便。
apache/shardingsphere-proxy:5.3.2
apache/shardingsphere-proxy:5.3.2
:這是要運行的 Docker 鏡像的名稱和標簽。apache/shardingsphere-proxy
?是鏡像的名稱。5.3.2
?是鏡像的版本標簽(Tag)。Docker 會首先檢查本地是否有這個鏡像,如果沒有,會自動從 Docker Hub(或其他配置的鏡像倉庫)拉取這個版本的鏡像,然后使用它來創建并啟動容器。
廢話不多說,我們直接執行就OK。
然后我們來看看有沒有啟動成功啊?
# 查看是否啟動成功
docker ps -a
?我們發現沒有啟動成功,這個是因為docker容器沒有進行配置
修改配置文件
首先我們需要來到這個配置文件對應的?宿主機映射目錄里面,也就是/bit/shardingsphere/proxy/conf
cd ?/bit/shardingsphere/proxy/conf
我們就在這個目錄里面創建一個配置文件server.yaml
vim server.yaml
然后把下面這個粘貼進去即可?
# 運行模式配置
mode:type: Standalone # 單機模式(非集群部署)# 用戶權限配置
authority:users:- user: root@% # 用戶名格式:用戶名@訪問主機(%表示允許任意主機訪問)password: 123456 # 用戶密碼privilege:type: ALL_PERMITTED # 權限類型:授予該用戶所有操作權限# 系統屬性配置
props:sql-show: true # 是否在日志中顯示執行的SQL語句(true=顯示,false=隱藏)proxy-mysql-default-version: 8.0.42 # 代理對外暴露的MySQL默認版本號
?
我們保存退出。
上傳MySQL驅動
連接MySQL數據庫時,需要把MySQL驅動包放入宿主機映射擴展目錄/bit/shardingsphere/proxy/ext-lib中
cd /bit/shardingsphere/proxy/ext-lib
現在我們可以上一小節下載的?MySQL驅動驅動直接復制到這里即可。
當然,大家執行下面這個命令也是可以的
wget https://repo1.maven.org/maven2/com/mysql/mysql-connector-j/8.0.33/mysql-connector-j-8.0.33.jar
啟動
啟動Docker容器,運行ShardingSphere
# 重新啟動容器
docker start ss-proxy
# 查看狀態,啟動成功
docker ps
?
沒有一點問題
測試連接
通過客戶端工具連接
# 指定主機和端口號進行連接,成功
mysql -uroot -p -h127.0.0.1 -P3307
進入Docker容器
# 進入容器
docker exec -it ss-proxy env LANG=C.UTF-8 /bin/bash
?如果我們想要遠程登陸這個服務器的話,需要去云服務器里面的防火墻和安全組里面開放這個3307端口。
二. 實踐:水平分片
2.1 應用場景
隨著系統持續運行,各業務模塊積累的數據量不斷攀升。在垂直分庫架構下,雖然不同業務的表分散在不同數據庫中,但單個數據表內的記錄仍持續增長。當表數據量超過數千萬行時,底層存儲引擎使用的B+樹索引層級會逐漸加深。樹高每增加一層,數據查詢就需要多一次磁盤I/O操作,導致查詢延遲顯著上升。此時,垂直分庫已無法解決單表數據膨脹帶來的性能衰減。
為突破這一瓶頸,需要引入水平分片機制。其核心思路是將單張邏輯表的數據,按預設規則(如用戶ID哈希、時間范圍等)橫向拆分到多個結構相同的物理分片表中。每個分片表只保存原表的部分數據子集,從而將海量數據分散存儲。
這種拆分帶來兩個關鍵優勢:首先,單個分片表的數據規模大幅縮減,有效控制了B+樹的高度。當樹高穩定在3層以內時,查詢操作通常只需1-3次磁盤I/O,響應時間保持在毫秒級。其次,不同分片表可部署在不同物理節點上,查詢負載被天然分散。用戶請求會根據分片規則自動路由到特定分片,避免單一節點過載。
更重要的是,水平分片使系統獲得彈性擴展能力。當數據持續增長時,只需增加新的分片節點即可線性提升存儲容量和吞吐量,無需重構整體架構。這種設計既保障了海量數據場景下的穩定性能,也為后續業務擴展預留了技術空間。
2.2 架構圖
在前面垂直分庫的時候,我們是采用下面這個架構圖
- server-user服務器中存儲t_user表中的數據 ?
- server-order服務器中存儲t_order表中的數據
然而隨著業務的發展,server-order 節點中 t_order 表的存儲壓力不斷增大,這時就需要對 t_order 表進行橫向拆分,也就是水平分片。我們先采用水平分庫,將t_order表里面的數據拆分成torder0和t_order1兩部分,分別存放到server_order0和server_order1兩臺機器上
然而server-order 節點中 t_order 表的存儲壓力成指數級增長,上圖的水平分庫已經不能滿足需求了,我們需要在水平分庫的基礎之上再將torder0和t_order1進行水平分片。
分庫策略:為了讓請求正確路由到指定的數據庫中
分表策略:為了讓請求在正確的分片表中操作數據?
2.3 服務器規劃
由原來的一臺服務器中的一張表,變成兩臺服務器中的四張表,分擔數據存儲壓力
- 訂單服務器:容器名 server-order0,端口號:63310
- 訂單服務器:容器名 server-order1,端口號:63311
?2.4 創建server-user容器
創建Docker容器
docker run -d \-p 53310:3306 \-v /bit/mysql/user/conf:/etc/mysql/conf.d \-v /bit/mysql/user/mysql:/var/lib/mysql \-e MYSQL_ROOT_PASSWORD=123456 \--name server-user \mysql:8.0.42
關鍵參數解釋:
-d
:后臺運行容器(detached 模式)。-p 53310:3306
:將主機的 53310 端口映射到容器的 3306 端口(MySQL 默認端口),外部通過?主機IP:53310
?訪問數據庫。-v /bit/mysql/user/conf:/etc/mysql/conf.d
:掛載本地配置目錄到容器,用于自定義 MySQL 配置(如?my.cnf
)。-v /bit/mysql/user/mysql:/var/lib/mysql
:掛載本地數據目錄到容器,實現數據持久化(防止容器刪除后數據丟失)。-e MYSQL_ROOT_PASSWORD=123456
:設置 MySQL 的?root
?用戶密碼。--name server-user
:為容器指定名稱(便于后續管理)。mysql:8.0.42
:使用的 MySQL 鏡像版本。
?
# 查看容器狀態
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b5051d4517c4 mysql:8.0.42 "docker-entrypoint.s…" 4 seconds ago Up 3 seconds 33060/tcp, 0.0.0.0:53310->3306/tcp, [::]:53310->3306/tcp server-user
進入Docker容器
# 進入Docker容器,env LANG=C.UTF-8避免中文亂碼問題
docker exec -it server-user env LANG=C.UTF-8 /bin/bash# 運行Mysql客戶端
mysql -uroot -p
# 修改root用戶密碼
SET PASSWORD = '123456';
創建數據庫
使用客戶端登錄數據庫并執行SQL
-- 創建數據庫
CREATE DATABASE IF NOT EXISTS user_db CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;-- 使用 user_db 數據庫作為當前操作上下文
USE user_db;CREATE TABLE IF NOT EXISTS t_user (id BIGINT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(20)
);
測試遠程登陸
接下來我們將測試一下遠程登陸
注意:如果你的宿主機是云服務器的話,請記得去云服務器官網開放防火墻端口和設置安全組
mysql -h150.158.139.86 -P53310 -uroot -p
2.5 創建server-order0和server-order1容器
創建Docker容器
創建兩臺Docker,容器名端口號分別為 server-order0:63310 和 server-order1:63311
# 創建server-order0,注意修改相應的容器名、端口號和映射路徑docker run -d \
-p 63310:3306 \
-v /bit/mysql/order0/conf:/etc/mysql/conf.d \
-v /bit/mysql/order0/mysql:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--name server-order0 \
--restart always \
mysql:8.0.42# 創建server-order1,注意修改相應的容器名、端口號和映射路徑
docker run -d \
-p 63311:3306 \
-v /bit/mysql/order1/conf:/etc/mysql/conf.d \
-v /bit/mysql/order1/mysql:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--name server-order1 \
--restart always \
mysql:8.0.42
?
查看是否啟動成功
docker ps
root@VM-16-14-ubuntu:/bit/shardingsphere/proxy/conf# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d9e2e351fb8a mysql:8.0.42 "docker-entrypoint.s…" 2 seconds ago Up 1 second 33060/tcp, 0.0.0.0:63311->3306/tcp, [::]:63311->3306/tcp server-order1
d2d5c33d7ca2 mysql:8.0.42 "docker-entrypoint.s…" 3 minutes ago Up 3 second 33060/tcp, 0.0.0.0:63310->3306/tcp, [::]:63310->3306/tcp server-order0
接下來我們將使用客戶端分別登錄 server-order0 和 server-order1 并創建數據庫
水平分片后由于數據分布在不同的數據節點,所以主鍵值不能依賴自增值,生成策略由業務層實現,插入記錄時指定一個主鍵值。
進入Docker容器?server-order0,創建t_order1和
# 進入Docker容器,env LANG=C.UTF-8避免中文亂碼問題
docker exec -it server-order0 env LANG=C.UTF-8 /bin/bash# 運行Mysql客戶端
mysql -uroot -p# 修改root用戶密碼
SET PASSWORD = '123456';
?然后我們需要創建torder0和torder1的表的結構
-- 創建訂單數據庫(使用UTF8字符集和現代排序規則)
CREATE DATABASE IF NOT EXISTS order_db CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;-- 切換到訂單數據庫
USE order_db;-- 創建分片表0(用于存儲用戶ID為偶數的訂單)
CREATE TABLE IF NOT EXISTS t_order0 (id BIGINT PRIMARY KEY, -- 主鍵IDorder_no VARCHAR(30) COMMENT '訂單號', -- 訂單唯一編號amount DECIMAL(12, 2) COMMENT '訂單金額', -- 訂單金額(最大999億)user_id BIGINT COMMENT '用戶編號' -- 關聯用戶ID
);-- 創建分片表1(用于存儲用戶ID為奇數的訂單)
CREATE TABLE IF NOT EXISTS t_order1 (id BIGINT PRIMARY KEY, -- 主鍵IDorder_no VARCHAR(30) COMMENT '訂單號', -- 訂單唯一編號amount DECIMAL(12, 2) COMMENT '訂單金額', -- 訂單金額(最大999億)user_id BIGINT COMMENT '用戶編號' -- 關聯用戶ID
);
?
進入Docker容器?server-order1,創建對應的表
# 進入Docker容器,env LANG=C.UTF-8避免中文亂碼問題
docker exec -it server-order1 env LANG=C.UTF-8 /bin/bash# 運行Mysql客戶端
mysql -uroot -p# 修改root用戶密碼
SET PASSWORD = '123456';
?然后我們需要創建torder0和torder1的表的結構
-- 創建訂單數據庫(使用UTF8字符集和現代排序規則)
CREATE DATABASE IF NOT EXISTS order_db CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;-- 切換到訂單數據庫
USE order_db;-- 創建分片表0(用于存儲用戶ID為偶數的訂單)
CREATE TABLE IF NOT EXISTS t_order0 (id BIGINT PRIMARY KEY, -- 主鍵IDorder_no VARCHAR(30) COMMENT '訂單號', -- 訂單唯一編號amount DECIMAL(12, 2) COMMENT '訂單金額', -- 訂單金額(最大999億)user_id BIGINT COMMENT '用戶編號' -- 關聯用戶ID
);-- 創建分片表1(用于存儲用戶ID為奇數的訂單)
CREATE TABLE IF NOT EXISTS t_order1 (id BIGINT PRIMARY KEY, -- 主鍵IDorder_no VARCHAR(30) COMMENT '訂單號', -- 訂單唯一編號amount DECIMAL(12, 2) COMMENT '訂單金額', -- 訂單金額(最大999億)user_id BIGINT COMMENT '用戶編號' -- 關聯用戶ID
);
?
測試遠程連接
接下來我們將測試一下遠程連接
注意:如果你的宿主機是云服務器的話,那么就一定要記得去云服務器官網開放防火墻端口號并且去開放安全組端口號
mysql -h150.158.139.86 -P63310 -uroot -p
mysql -h150.158.139.86 -P63311 -uroot -p
?
2.6.日志配置
?首先我們需要來到這個配置文件對應的?宿主機映射目錄里面,也就是/bit/shardingsphere/proxy/conf
cd /bit/shardingsphere/proxy/conf
接下來我們將創建一個文件
vim logback.xml
并編寫以下內容
<?xml version="1.0"?>
<configuration><!-- 日志輸入到文件 --><appender name="SHARDING_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><!-- 日志路徑 --><file>./logs/shardingsphere.log</file><encoder><!-- 日志輸入的樣式 --><pattern>[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern></encoder><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><fileNamePattern>shardingsphere.%d{yyyy-MM-dd}.%i.log</fileNamePattern><timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><maxFileSize>100MB</maxFileSize></timeBasedFileNamingAndTriggeringPolicy></rollingPolicy></appender><root level="INFO"><appender-ref ref="SHARDING_FILE" /></root></configuration>
?
我們保存退出一下 。
2.7 數據節點配置
數據節點指的是某個數據源下的某張具體的分片表
數據節點配置與可以稱為標準分片表配置
數據分片需要配置的文件是: conf/config-sharding.yaml,對應我們宿主機的目錄就是/bit/shardingsphere/proxy/conf/config-sharding.yaml。
首先我們先來到我們的/bit/shardingsphere/proxy/conf/,然后創建一個叫config-sharding.yaml的文件
cd /bit/shardingsphere/proxy/conf/
vim config-sharding.yaml
配置數據源
配置 server_user、server_order0、server_order1 三個數據源
databaseName: bit_sharding_dbdataSources:server_user:url: jdbc:mysql://150.158.139.86:53310/user_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1server_order0:url: jdbc:mysql://150.158.139.86:63310/order_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1server_order1:url: jdbc:mysql://150.158.139.86:63311/order_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1
配置數據節點
每個 t_order 表都是一個數據節點,當前 t_order 表分布如下:
- server_order0.t_order0
- server_order0.t_order1
- server_order1.t_order0
- server_order1.t_order1
rules:
- !SHARDINGtables:t_user: # 邏輯表名actualDataNodes: server_user.t_user # 由數據源名 + 表名組成t_order: # 邏輯表名actualDataNodes: server_order0.t_order0,server_order0.t_order1,server_order1.t_order0,server_order1.t_order1
我們在想?:server_order0.t_order0,server_order0.t_order1,server_order1.t_order0,server_order1.t_order1能不能再簡化一下,我們可以去ShardingSphere官網看看核心概念 :: ShardingSphere
?我們現在就去修改rules節點,使用行表達式對 t_order 表進行水平分庫配置
rules:
- !SHARDINGtables:t_user: # 邏輯表名actualDataNodes: server_user.t_user # 由數據源名 + 表名組成t_order: # 邏輯表名actualDataNodes: server_order${0..1}.t_order${0..1}
所有對邏輯表?t_user
?的操作直接路由到?server_user
?數據源(即?user_db
?數據庫)
所有對邏輯表t_order的操作路由到下面四個節點
- server_order0.t_order0
- server_order0.t_order1
- server_order1.t_order0
- server_order1.t_order1
完整配置文件內容
databaseName: bit_sharding_dbdataSources:server_user:url: jdbc:mysql://150.158.139.86:53310/user_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1server_order0:url: jdbc:mysql://150.158.139.86:63310/order_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1server_order1:url: jdbc:mysql://150.158.139.86:63311/order_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1rules:
- !SHARDINGtables:t_user: # 邏輯表名actualDataNodes: server_user.t_user # 由數據源名 + 表名組成t_order: # 邏輯表名actualDataNodes: server_order${0..1}.t_order${0..1}
?我們把這個內容寫入/bit/shardingsphere/proxy/conf/config-sharding.yaml.
保存退出
2.8.測試數據節點
我們在上面的配置文件里面寫了
rules:
- !SHARDINGtables:t_user: # 邏輯表名actualDataNodes: server_user.t_user # 由數據源名 + 表名組成t_order: # 邏輯表名actualDataNodes: server_order${0..1}.t_order${0..1}
每個 t_order 表都是一個數據節點,當前 t_order 表分布如下:
- server_order0.t_order0
- server_order0.t_order1
- server_order1.t_order0
- server_order1.t_order1
事實上呢?
我們一般是不會直接像上面2.7里面的配置文件那樣子直接寫actualDataNodes: server_order${0..1}.t_order${0..1}的,我們得先一個一個數據節點進行測試,等每個數據節點都測試成功了,我們再寫actualDataNodes: server_order${0..1}.t_order0${0..1}的
2.8.1.測試server_order0.t_order0節點
?首先我們先來到我們的/bit/shardingsphere/proxy/conf/,然后創建一個叫config-sharding.yaml的文件
cd /bit/shardingsphere/proxy/conf/
vim config-sharding.yaml
我們把我們把config-sharding.yaml里面的一部分內容修改成下面這樣子
……
rules:
- !SHARDINGtables:t_user: # 邏輯表名actualDataNodes: server_user.t_user # 由數據源名 + 表名組成t_order: # 邏輯表名actualDataNodes: server_order0.t_order0
然后保存退出?,我們重啟Docker容器
docker restart ss-proxy
docker ps
運行結果如下
root@VM-16-14-ubuntu:/bit/shardingsphere/proxy/conf# docker restart ss-proxy
ss-proxy
root@VM-16-14-ubuntu:/bit/shardingsphere/proxy/conf# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d9e2e351fb8a mysql:8.0.42 "docker-entrypoint.s…" 15 hours ago Up 15 hours 33060/tcp, 0.0.0.0:63311->3306/tcp, [::]:63311->3306/tcp server-order1
d2d5c33d7ca2 mysql:8.0.42 "docker-entrypoint.s…" 16 hours ago Up 16 hours 33060/tcp, 0.0.0.0:63310->3306/tcp, [::]:63310->3306/tcp server-order0
b5051d4517c4 mysql:8.0.42 "docker-entrypoint.s…" 22 hours ago Up 2 hours 33060/tcp, 0.0.0.0:53310->3306/tcp, [::]:53310->3306/tcp server-user
efc3b3c7dabc apache/shardingsphere-proxy:5.3.2 "/bin/sh -c '${LOCAL…" 41 hours ago Up 6 seconds 0.0.0.0:3307->3307/tcp, [::]:3307->3307/tcp ss-proxy
接著我們打開實時日志
tail -f /bit/shardingsphere/proxy/logs/shardingsphere.log
?然后我們去登陸
mysql -h127.0.0.1 -P3307 -uroot -p
我們接著執行下面這個?
?注意:如果顯示的是下面這個情況,不要太慌張,這個是ShardingSphere-Proxy5.3.2的bug,不影響我們的!!
然后我們執行下面這個語句
-- 插入記錄并在server_order0.t_order0節點中查看是否插入成功
insert into t_order (id, order_no, user_id, amount) values (1, 'BIT001', 1, 20.00);
我們看看日志中的結果
[INFO ] 2025-08-01 08:26:34.003 [ShardingSphere-Command-2] INFO ?ShardingSphere-SQL - Logic SQL: insert into t_order (id, order_no, user_id, amount) values (1, 'BIT001', 1, 20.00)
[INFO ] 2025-08-01 08:26:34.003 [ShardingSphere-Command-2] INFO ?ShardingSphere-SQL - Actual SQL: server_order0 ::: insert into t_order0 (id, order_no, user_id, amount) values (1, 'BIT001', 1, 20.00)
可見這條插入到邏輯表t_order的數據插入到server_order0里的t_order0表了。與我們配置的一模一樣。
2.8.2.測試server_order0.t_order1節點
??首先我們先來到我們的/bit/shardingsphere/proxy/conf/,然后創建一個叫config-sharding.yaml的文件
cd /bit/shardingsphere/proxy/conf/
vim config-sharding.yaml
我們把我們把config-sharding.yaml里面的一部分內容修改成下面這樣子
……
rules:
- !SHARDINGtables:t_user: # 邏輯表名actualDataNodes: server_user.t_user # 由數據源名 + 表名組成t_order: # 邏輯表名actualDataNodes: server_order0.t_order1
然后保存退出?,我們重啟Docker容器
docker restart ss-proxy
docker ps
運行結果如下
root@VM-16-14-ubuntu:/bit/shardingsphere/proxy/conf# docker restart ss-proxy
ss-proxy
root@VM-16-14-ubuntu:/bit/shardingsphere/proxy/conf# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d9e2e351fb8a mysql:8.0.42 "docker-entrypoint.s…" 18 hours ago Up 18 hours 33060/tcp, 0.0.0.0:63311->3306/tcp, [::]:63311->3306/tcp server-order1
d2d5c33d7ca2 mysql:8.0.42 "docker-entrypoint.s…" 18 hours ago Up 18 hours 33060/tcp, 0.0.0.0:63310->3306/tcp, [::]:63310->3306/tcp server-order0
b5051d4517c4 mysql:8.0.42 "docker-entrypoint.s…" 25 hours ago Up 4 hours 33060/tcp, 0.0.0.0:53310->3306/tcp, [::]:53310->3306/tcp server-user
efc3b3c7dabc apache/shardingsphere-proxy:5.3.2 "/bin/sh -c '${LOCAL…" 43 hours ago Up 3 seconds 0.0.0.0:3307->3307/tcp, [::]:3307->3307/tcp ss-proxy
接著我們打開實時日志
tail -f /bit/shardingsphere/proxy/logs/shardingsphere.log
?然后我們去登陸
mysql -h127.0.0.1 -P3307 -uroot -p
我們接著執行下面這個?
注意:如果顯示的是下面這個情況,不要太慌張,這個是ShardingSphere-Proxy5.3.2的bug,不影響我們的!!
然后我們執行下面這個語句
-- 插入記錄并在server_order0.t_order1節點中查看是否插入成功
insert into t_order (id, order_no, user_id, amount) values (2, 'BIT002', 1, 20.00);
我們看看日志中的結果
[INFO ] 2025-08-01 08:34:50.284 [ShardingSphere-Command-2] INFO ?ShardingSphere-SQL - Logic SQL: insert into t_order (id, order_no, user_id, amount) values (2, 'BIT002', 1, 20.00)
[INFO ] 2025-08-01 08:34:50.284 [ShardingSphere-Command-2] INFO ?ShardingSphere-SQL - Actual SQL: server_order0 ::: insert into t_order1 (id, order_no, user_id, amount) values (2, 'BIT002', 1, 20.00)
可見這條插入到邏輯表t_order的數據插入到server_order0里的t_order1表了。與我們配置的一模一樣。
2.8.3.測試server_order1.t_order0節點
由于此時還沒有配置分片規則,所以先單獨測試每個數據節點是正常工作,修改配置文件并重啟
??首先我們先來到我們的/bit/shardingsphere/proxy/conf/,然后創建一個叫config-sharding.yaml的文件
cd /bit/shardingsphere/proxy/conf/
vim config-sharding.yaml
我們把我們把config-sharding.yaml里面的一部分內容修改成下面這樣子
……
rules:
- !SHARDINGtables:t_user: # 邏輯表名actualDataNodes: server_user.t_user # 由數據源名 + 表名組成t_order: # 邏輯表名actualDataNodes: server_order1.t_order0
然后保存退出?,我們重啟Docker容器
docker restart ss-proxy
docker ps
運行結果如下
root@VM-16-14-ubuntu:/bit/shardingsphere/proxy/conf# docker restart ss-proxy
ss-proxy
root@VM-16-14-ubuntu:/bit/shardingsphere/proxy/conf# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d9e2e351fb8a mysql:8.0.42 "docker-entrypoint.s…" 18 hours ago Up 18 hours 33060/tcp, 0.0.0.0:63311->3306/tcp, [::]:63311->3306/tcp server-order1
d2d5c33d7ca2 mysql:8.0.42 "docker-entrypoint.s…" 18 hours ago Up 18 hours 33060/tcp, 0.0.0.0:63310->3306/tcp, [::]:63310->3306/tcp server-order0
b5051d4517c4 mysql:8.0.42 "docker-entrypoint.s…" 25 hours ago Up 4 hours 33060/tcp, 0.0.0.0:53310->3306/tcp, [::]:53310->3306/tcp server-user
efc3b3c7dabc apache/shardingsphere-proxy:5.3.2 "/bin/sh -c '${LOCAL…" 43 hours ago Up 8 seconds 0.0.0.0:3307->3307/tcp, [::]:3307->3307/tcp ss-proxy
接著我們打開實時日志
tail -f /bit/shardingsphere/proxy/logs/shardingsphere.log
?然后我們去登陸
mysql -h127.0.0.1 -P3307 -uroot -p
我們接著執行下面這個?
注意:如果顯示的是下面這個情況,不要太慌張,這個是ShardingSphere-Proxy5.3.2的bug,不影響我們的!!
然后我們執行下面這個語句
-- 插入記錄并在server_order1.t_order0節點中查看是否插入成功
insert into t_order (id, order_no, user_id, amount) values (3, 'BIT003', 1, 20.00);
我們看看日志中的結果
[INFO ] 2025-08-01 08:38:10.453 [ShardingSphere-Command-1] INFO ?ShardingSphere-SQL - Logic SQL: insert into t_order (id, order_no, user_id, amount) values (3, 'BIT003', 1, 20.00)
[INFO ] 2025-08-01 08:38:10.454 [ShardingSphere-Command-1] INFO ?ShardingSphere-SQL - Actual SQL: server_order1 ::: insert into t_order0 (id, order_no, user_id, amount) values (3, 'BIT003', 1, 20.00)
可見這條插入到邏輯表t_order的數據插入到server_order1里的t_order0表了。與我們配置的一模一樣。
?2.8.4.測試server_order1.t_order1節點
?首先我們先來到我們的/bit/shardingsphere/proxy/conf/,然后創建一個叫config-sharding.yaml的文件
cd /bit/shardingsphere/proxy/conf/
vim config-sharding.yaml
我們把我們把config-sharding.yaml里面的一部分內容修改成下面這樣子
……
rules:
- !SHARDINGtables:t_user: # 邏輯表名actualDataNodes: server_user.t_user # 由數據源名 + 表名組成t_order: # 邏輯表名actualDataNodes: server_order1.t_order1
然后保存退出?,我們重啟Docker容器
docker restart ss-proxy
docker ps
運行結果如下
root@VM-16-14-ubuntu:/bit/shardingsphere/proxy/conf# docker restart ss-proxy
ss-proxy
root@VM-16-14-ubuntu:/bit/shardingsphere/proxy/conf# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d9e2e351fb8a mysql:8.0.42 "docker-entrypoint.s…" 18 hours ago Up 18 hours 33060/tcp, 0.0.0.0:63311->3306/tcp, [::]:63311->3306/tcp server-order1
d2d5c33d7ca2 mysql:8.0.42 "docker-entrypoint.s…" 18 hours ago Up 18 hours 33060/tcp, 0.0.0.0:63310->3306/tcp, [::]:63310->3306/tcp server-order0
b5051d4517c4 mysql:8.0.42 "docker-entrypoint.s…" 25 hours ago Up 4 hours 33060/tcp, 0.0.0.0:53310->3306/tcp, [::]:53310->3306/tcp server-user
efc3b3c7dabc apache/shardingsphere-proxy:5.3.2 "/bin/sh -c '${LOCAL…" 43 hours ago Up 5 seconds 0.0.0.0:3307->3307/tcp, [::]:3307->3307/tcp ss-proxy
接著我們打開實時日志
tail -f /bit/shardingsphere/proxy/logs/shardingsphere.log
?然后我們去登陸
mysql -h127.0.0.1 -P3307 -uroot -p
注意:如果顯示的是下面這個情況,不要太慌張,這個是ShardingSphere-Proxy5.3.2的bug,不影響我們的!!
然后我們執行下面這個語句
-- 插入記錄并在server_order1.t_order1節點中查看是否插入成功
insert into t_order (id, order_no, user_id, amount) values (4, 'BIT004', 1, 20.00);
我們看看日志中的結果
[INFO ] 2025-08-01 08:52:16.198 [ShardingSphere-Command-2] INFO ?ShardingSphere-SQL - Logic SQL: insert into t_order (id, order_no, user_id, amount) values (4, 'BIT004', 1, 20.00)
[INFO ] 2025-08-01 08:52:16.198 [ShardingSphere-Command-2] INFO ?ShardingSphere-SQL - Actual SQL: server_order1 ::: insert into t_order1 (id, order_no, user_id, amount) values (4, 'BIT004', 1, 20.00)
可見這條插入到邏輯表t_order的數據插入到server_order1里的t_order1表了。與我們配置的一模一樣。
2.8.5.完整配置?
現在四個數據節點我們都測試完成了,都沒有問題。現在我們就可以放心的去配置我們的
?首先我們先來到我們的/bit/shardingsphere/proxy/conf/,然后創建一個叫config-sharding.yaml的文件
cd /bit/shardingsphere/proxy/conf/
vim config-sharding.yaml
然后把下面這個內容粘貼進去
databaseName: bit_sharding_dbdataSources:server_user:url: jdbc:mysql://150.158.139.86:53310/user_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1server_order0:url: jdbc:mysql://150.158.139.86:63310/order_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1server_order1:url: jdbc:mysql://150.158.139.86:63311/order_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1rules:
- !SHARDINGtables:t_user: # 邏輯表名actualDataNodes: server_user.t_user # 由數據源名 + 表名組成t_order: # 邏輯表名actualDataNodes: server_order${0..1}.t_order${0..1}
?
我們保存退出。重啟Docker容器
docker restart ss-proxy
docker ps
運行結果
root@VM-16-14-ubuntu:/bit/shardingsphere/proxy/conf# docker restart ss-proxy
ss-proxy
root@VM-16-14-ubuntu:/bit/shardingsphere/proxy/conf# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d9e2e351fb8a mysql:8.0.42 "docker-entrypoint.s…" 18 hours ago Up 18 hours 33060/tcp, 0.0.0.0:63311->3306/tcp, [::]:63311->3306/tcp server-order1
d2d5c33d7ca2 mysql:8.0.42 "docker-entrypoint.s…" 18 hours ago Up 18 hours 33060/tcp, 0.0.0.0:63310->3306/tcp, [::]:63310->3306/tcp server-order0
b5051d4517c4 mysql:8.0.42 "docker-entrypoint.s…" 25 hours ago Up 4 hours 33060/tcp, 0.0.0.0:53310->3306/tcp, [::]:53310->3306/tcp server-user
efc3b3c7dabc apache/shardingsphere-proxy:5.3.2 "/bin/sh -c '${LOCAL…" 44 hours ago Up 5 seconds 0.0.0.0:3307->3307/tcp, [::]:3307->3307/tcp ss-proxy
我們登陸去看看
mysql -h127.0.0.1 -P3307 -uroot -p
這個時候ShardingSphere-Proxy5.3.2的bug就不見了!!
那么到現在,我們就實現了邏輯表t_order的數據會從下面四個表中獲取
- server_order0.t_order0
- server_order0.t_order1
- server_order1.t_order0
- server_order1.t_order1
但是也就僅此而已,我們還需要做更多的配置。
2.6 水平分庫配置
注意這次我們 需要配置的文件是: conf/config-sharding.yaml,對應我們宿主機的目錄就是/bit/shardingsphere/proxy/conf/config-sharding.yaml。
首先我們先來到我們的/bit/shardingsphere/proxy/conf/,然后創建一個叫config-sharding.yaml的文件
cd /bit/shardingsphere/proxy/conf/
vim config-sharding.yaml
思考配置思路
還記得我們創建的t_order0和t_order1表的結構嗎?其實是一模一樣的的。
-- 創建分片表0(用于存儲用戶ID為偶數的訂單)
CREATE TABLE IF NOT EXISTS t_order0 (id BIGINT PRIMARY KEY, -- 主鍵IDorder_no VARCHAR(30) COMMENT '訂單號', -- 訂單唯一編號amount DECIMAL(12, 2) COMMENT '訂單金額', -- 訂單金額(最大999億)user_id BIGINT COMMENT '用戶編號' -- 關聯用戶ID
);-- 創建分片表1(用于存儲用戶ID為奇數的訂單)
CREATE TABLE IF NOT EXISTS t_order1 (id BIGINT PRIMARY KEY, -- 主鍵IDorder_no VARCHAR(30) COMMENT '訂單號', -- 訂單唯一編號amount DECIMAL(12, 2) COMMENT '訂單金額', -- 訂單金額(最大999億)user_id BIGINT COMMENT '用戶編號' -- 關聯用戶ID
);
我們這就根據t_order0和t_order1中的user_id 來確定數據寫入下面哪個數據節點 。
該層的核心目標是將數據分散存儲到不同的物理數據庫節點(server_order0
?和?server_order1
),以提升系統的整體存儲容量、處理能力和并發性能。
具體路由策略是:系統根據訂單數據中的?user_id
?字段值來決定其所屬的數據庫節點。
- 當?
user_id
?為?偶數?時(也就是user_id%2==0時)
,數據將被路由至?server_order0
?節點。 - 當?
user_id
?為?奇數?時(也就是user_id%2!=0時)
,數據將被路由至?server_order1
?節點。
這種按用戶 ID 奇偶性分庫的策略具有顯著優勢:它有效避免了跨庫事務的復雜性(因為同一用戶相關的訂單操作通常都落在同一個數據庫節點內完成),同時大幅減少了跨庫查詢的需求(查詢特定用戶的訂單數據只需訪問其對應的單個數據庫節點即可)。
配置分庫策略
那怎么實現呢?大家可以去官網看看:數據分片 :: ShardingSphere
rules:
- !SHARDINGtables:t_user: # 邏輯表名actualDataNodes: server_user.t_user # 由數據源名 + 表名組成t_order: # 邏輯表名actualDataNodes: server_order${0..1}.t_order0${0..1}databaseStrategy: # 分庫策略standard: # 用于單分片鍵的標準分片場景shardingColumn: user_id # 分片列名稱shardingAlgorithmName: alg_db_inline_userid # 分片算法名稱(自定義)
2.6.1.行表達式分片算法
什么是行表達式分片算法
咱們用大白話解釋一下 Apache ShardingSphere 的行表達式分片算法:
它是什么?
想象一下你有一大堆訂單數據(比如存在?t_order
?表里),單張表裝不下或者查詢太慢了。你需要把它們拆分到不同的物理表(比如?t_order_0
,?t_order_1
, ...?t_order_7
)或者不同的數據庫里。
行表達式分片算法就是一種超級簡單、寫一行“公式”就能告訴 ShardingSphere 怎么拆分數據的方法。
核心思想:寫個“分片公式”
-
不用寫代碼:?你不需要為了分片去寫復雜的 Java 程序。ShardingSphere 內置了這個功能。
-
寫個表達式:?你只需要寫一個類似數學公式或簡單規則的字符串(就是?
algorithm-expression
?屬性)。 -
基于分片鍵:?這個公式是基于你選定的分片鍵(比如?
user_id
?或?order_id
)來計算的。這個算法只支持一個分片鍵。 -
支持 = 和 IN:?它最適合處理精確匹配的查詢,比如?
WHERE user_id = 123
?或者?WHERE order_id IN (1001, 1002, 1003)
。
舉個最經典的例子 🌰:
-
場景:?你想把用戶表?
t_user
?分成 8 張物理表,根據用戶 ID (u_id
) 的尾數來決定數據存在哪張表。 -
表達式:?
t_user_$->{u_id % 8}
-
怎么理解:
-
t_user_
: 這是你表名的前綴。 -
$->{ ... }
: 這是行表達式的固定語法,告訴 ShardingSphere 里面是計算規則。 -
u_id % 8
: 這就是“分片公式”。它計算?u_id
?除以 8 的余數。-
如果?
u_id
?是 10,?10 % 8 = 2
,那么這條數據就存到?t_user_2
?表。 -
如果?
u_id
?是 25,?25 % 8 = 1
,那么這條數據就存到?t_user_1
?表。 -
如果?
u_id
?是 32,?32 % 8 = 0
,那么這條數據就存到?t_user_0
?表。
-
-
-
結果:?數據會根據?
u_id
?均勻(理想情況下)分布到?t_user_0
?到?t_user_7
?這 8 張物理表中。
?如果大家想要了解更多,可以去官網看看:分片算法 :: ShardingSphere?
好,我們現在就根據行表達式算法來對我們的配置文件進行修改
訂單表中 user_id % 2 來確定當前記錄寫入哪個數據庫
rules:
- !SHARDINGtables:t_user: # 邏輯表名actualDataNodes: server_user.t_user # 由數據源名 + 表名組成t_order: # 邏輯表名actualDataNodes: server_order${0..1}.t_order0${0..1}databaseStrategy: # 分庫策略standard: # 用于單分片鍵的標準分片場景shardingColumn: user_id # 分片列名稱shardingAlgorithmName: alg_db_inline_userid # 分片算法名稱(自定義)# 分片算法配置shardingAlgorithms:alg_db_inline_userid: # 前面定義的分片算法名稱type: INLINE # 分片算法類型props:algorithm-expression: server_order${user_id % 2} # 分片算法,根據 user_id對2取模
完整配置文件?
?/bit/shardingsphere/proxy/conf/config-sharding.yaml里面的內容應該是
databaseName: bit_sharding_dbdataSources:server_user:url: jdbc:mysql://150.158.139.86:53310/user_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1server_order0:url: jdbc:mysql://150.158.139.86:63310/order_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1server_order1:url: jdbc:mysql://150.158.139.86:63311/order_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1rules:
- !SHARDINGtables:t_user: # 邏輯表名actualDataNodes: server_user.t_user # 由數據源名 + 表名組成t_order: # 邏輯表名actualDataNodes: server_order${0..1}.t_order0${0..1}databaseStrategy: # 分庫策略standard: # 用于單分片鍵的標準分片場景shardingColumn: user_id # 分片列名稱shardingAlgorithmName: alg_db_inline_userid # 分片算法名稱(自定義)# 分片算法配置shardingAlgorithms:alg_db_inline_userid: # 前面定義的分片算法名稱type: INLINE # 分片算法類型props:algorithm-expression: server_order${user_id % 2} # 分片算法,根據 user_id對2取模
我們保存退出。重啟Docker容器
docker restart ss-proxy
docker ps
運行結果
root@VM-16-14-ubuntu:/bit/shardingsphere/proxy/conf# docker restart ss-proxy
ss-proxy
root@VM-16-14-ubuntu:/bit/shardingsphere/proxy/conf# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d9e2e351fb8a mysql:8.0.42 "docker-entrypoint.s…" 22 hours ago Up 22 hours 33060/tcp, 0.0.0.0:63311->3306/tcp, [::]:63311->3306/tcp server-order1
d2d5c33d7ca2 mysql:8.0.42 "docker-entrypoint.s…" 22 hours ago Up 22 hours 33060/tcp, 0.0.0.0:63310->3306/tcp, [::]:63310->3306/tcp server-order0
b5051d4517c4 mysql:8.0.42 "docker-entrypoint.s…" 29 hours ago Up 8 hours 33060/tcp, 0.0.0.0:53310->3306/tcp, [::]:53310->3306/tcp server-user
efc3b3c7dabc apache/shardingsphere-proxy:5.3.2 "/bin/sh -c '${LOCAL…" 47 hours ago Up 33 seconds 0.0.0.0:3307->3307/tcp, [::]:3307->3307/tcp ss-proxy
寫入測試
我們登陸進這個服務器來
mysql -h127.0.0.1 -P3307 -uroot -p
?
執行INSERT語句,向訂單表中插入一條數據
insert into t_order (id, order_no, user_id, amount) values (5, 'BIT005', 1, 20.00);
插入數據后報如下錯誤
mysql> insert into t_order (id, order_no, user_id, amount) values (5, 'BIT005', 1, 20.00);
ERROR 20087 (44000): Please check your sharding conditions `ShardingConditions(conditions=[ShardingCondition(values=[t_order.user_id = 1], startIndex=0)], sqlStatementContext=org.apache.shardingsphere.infra.binder.statement.dml.InsertStatementContext@57de437d, rule=org.apache.shardingsphere.sharding.rule.ShardingRule@20999517, subqueryContainsShardingCondition=true)` to avoid same record in table `t_order` routing to multiple data nodes.
這個意思就是mysql防止t_order表中的同一記錄路由到多個數據節點
報錯的原因是當前雖然根據 user_id 指定了分庫策略,但是沒有指定分表策略,也就是說當前不知道寫入數據庫中的 t_order0 表還是 t_order1 表
我們回去修改配置文件中數據節點配置,按照下面進行更改
?完整的配置文件
?/bit/shardingsphere/proxy/conf/config-sharding.yaml里面的內容應該是
databaseName: bit_sharding_dbdataSources:server_user:url: jdbc:mysql://150.158.139.86:53310/user_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1server_order0:url: jdbc:mysql://150.158.139.86:63310/order_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1server_order1:url: jdbc:mysql://150.158.139.86:63311/order_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1rules:
- !SHARDINGtables:t_user: # 邏輯表名actualDataNodes: server_user.t_user # 由數據源名 + 表名組成t_order: # 邏輯表名actualDataNodes: server_order${0..1}.t_order0databaseStrategy: # 分庫策略standard: # 用于單分片鍵的標準分片場景shardingColumn: user_id # 分片列名稱shardingAlgorithmName: alg_db_inline_userid # 分片算法名稱(自定義)# 分片算法配置shardingAlgorithms:alg_db_inline_userid: # 前面定義的分片算法名稱type: INLINE # 分片算法類型props:algorithm-expression: server_order${user_id % 2} # 分片算法,根據 user_id對2取模
??我們保存退出。重啟Docker容器
docker restart ss-proxy
docker ps
運行結果
root@VM-16-14-ubuntu:/bit/shardingsphere/proxy/conf# docker restart ss-proxy
ss-proxy
root@VM-16-14-ubuntu:/bit/shardingsphere/proxy/conf# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d9e2e351fb8a mysql:8.0.42 "docker-entrypoint.s…" 22 hours ago Up 22 hours 33060/tcp, 0.0.0.0:63311->3306/tcp, [::]:63311->3306/tcp server-order1
d2d5c33d7ca2 mysql:8.0.42 "docker-entrypoint.s…" 22 hours ago Up 22 hours 33060/tcp, 0.0.0.0:63310->3306/tcp, [::]:63310->3306/tcp server-order0
b5051d4517c4 mysql:8.0.42 "docker-entrypoint.s…" 29 hours ago Up 8 hours 33060/tcp, 0.0.0.0:53310->3306/tcp, [::]:53310->3306/tcp server-user
efc3b3c7dabc apache/shardingsphere-proxy:5.3.2 "/bin/sh -c '${LOCAL…" 47 hours ago Up 33 seconds 0.0.0.0:3307->3307/tcp, [::]:3307->3307/tcp ss-proxy
我們先去打開實時日志
tail -f /bit/shardingsphere/proxy/logs/shardingsphere.log
?然后我們去登陸進這個服務器來
mysql -h127.0.0.1 -P3307 -uroot -p
執行INSERT語句
-- 每條記錄的用戶Id不同
insert into t_order (id, order_no, user_id, amount) values (1, 'BIT001', 1, 20.00);
insert into t_order (id, order_no, user_id, amount) values (2, 'BIT002', 2, 20.00);
insert into t_order (id, order_no, user_id, amount) values (3, 'BIT003', 3, 20.00);
insert into t_order (id, order_no, user_id, amount) values (4, 'BIT004', 4, 20.00);
查看日志
# 用戶id=1 路由到server_order1
[INFO ] 2025-08-01 13:00:09.240 [ShardingSphere-Command-2] INFO ?ShardingSphere-SQL - Logic SQL: insert into t_order (id, order_no, user_id, amount) values (1, 'BIT001', 1, 20.00)
[INFO ] 2025-08-01 13:00:09.241 [ShardingSphere-Command-2] INFO ?ShardingSphere-SQL - Actual SQL: server_order1 ::: insert into t_order0 (id, order_no, user_id, amount) values (1, 'BIT001', 1, 20.00)
# 用戶id=2 路由到server_order0
[INFO ] 2025-08-01 13:00:16.759 [ShardingSphere-Command-2] INFO ?ShardingSphere-SQL - Logic SQL: insert into t_order (id, order_no, user_id, amount) values (2, 'BIT002', 2, 20.00)
[INFO ] 2025-08-01 13:00:16.759 [ShardingSphere-Command-2] INFO ?ShardingSphere-SQL - Actual SQL: server_order0 ::: insert into t_order0 (id, order_no, user_id, amount) values (2, 'BIT002', 2, 20.00)
# 用戶id=3 路由到server_order1
[INFO ] 2025-08-01 13:00:23.936 [ShardingSphere-Command-2] INFO ?ShardingSphere-SQL - Logic SQL: insert into t_order (id, order_no, user_id, amount) values (3, 'BIT003', 3, 20.00)
[INFO ] 2025-08-01 13:00:23.937 [ShardingSphere-Command-2] INFO ?ShardingSphere-SQL - Actual SQL: server_order1 ::: insert into t_order0 (id, order_no, user_id, amount) values (3, 'BIT003', 3, 20.00)
# 用戶Id=4 路由到server_order0
[INFO ] 2025-08-01 13:01:05.492 [ShardingSphere-Command-2] INFO ?ShardingSphere-SQL - Logic SQL: insert into t_order (id, order_no, user_id, amount) values (4, 'BIT004', 4, 20.00)
[INFO ] 2025-08-01 13:01:05.492 [ShardingSphere-Command-2] INFO ?ShardingSphere-SQL - Actual SQL: server_order0 ::: insert into t_order0 (id, order_no, user_id, amount) values (4, 'BIT004', 4, 20.00)
很好啊,完全實現了我們需要的功能。
2.6.2.取模分片算法
其實我們不止可以使用行表達式來實現這個功能,我們也可以通過取模分片算法來實現
分片算法參考官方文檔:分片算法 :: ShardingSphere
databaseName: bit_sharding_dbdataSources:server_user:url: jdbc:mysql://150.158.139.86:53310/user_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1server_order0:url: jdbc:mysql://150.158.139.86:63310/order_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1server_order1:url: jdbc:mysql://150.158.139.86:63311/order_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1rules:
- !SHARDINGtables:t_user: # 邏輯表名actualDataNodes: server_user.t_user # 由數據源名 + 表名組成t_order: # 邏輯表名actualDataNodes: server_order${0..1}.t_order0databaseStrategy: # 分庫策略standard: # 用于單分片鍵的標準分片場景shardingColumn: user_id # 分片列名稱shardingAlgorithmName: alg_mod # 分片算法名稱(自定義)——和下面的alg_mod對應# 分片算法配置shardingAlgorithms:alg_db_inline_userid: # 自定義的分片算法名稱_行表達式分片算法type: INLINE # 分片算法類型props:algorithm-expression: server_order${user_id % 2} # 分片算法,根據 user_id對2取模alg_mod: # 自定義算法名_取模分片算法type: MODprops:sharding-count: 2
?我們保存退出。重啟Docker容器
docker restart ss-proxy
docker ps
運行結果
root@VM-16-14-ubuntu:/bit/shardingsphere/proxy/conf# docker restart ss-proxy
ss-proxy
root@VM-16-14-ubuntu:/bit/shardingsphere/proxy/conf# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d9e2e351fb8a mysql:8.0.42 "docker-entrypoint.s…" 22 hours ago Up 22 hours 33060/tcp, 0.0.0.0:63311->3306/tcp, [::]:63311->3306/tcp server-order1
d2d5c33d7ca2 mysql:8.0.42 "docker-entrypoint.s…" 22 hours ago Up 22 hours 33060/tcp, 0.0.0.0:63310->3306/tcp, [::]:63310->3306/tcp server-order0
b5051d4517c4 mysql:8.0.42 "docker-entrypoint.s…" 29 hours ago Up 8 hours 33060/tcp, 0.0.0.0:53310->3306/tcp, [::]:53310->3306/tcp server-user
efc3b3c7dabc apache/shardingsphere-proxy:5.3.2 "/bin/sh -c '${LOCAL…" 47 hours ago Up 33 seconds 0.0.0.0:3307->3307/tcp, [::]:3307->3307/tcp ss-proxy
我們先去打開實時日志
tail -f /bit/shardingsphere/proxy/logs/shardingsphere.log
?然后我們去登陸進這個服務器來
mysql -h127.0.0.1 -P3307 -uroot -p
執行INSERT語句
-- 每條記錄的用戶Id不同
insert into t_order (id, order_no, user_id, amount) values (5, 'BIT001', 5, 20.00);
insert into t_order (id, order_no, user_id, amount) values (6, 'BIT002', 6, 20.00);
insert into t_order (id, order_no, user_id, amount) values (7, 'BIT003', 7, 20.00);
insert into t_order (id, order_no, user_id, amount) values (8, 'BIT004', 8, 20.00);
執行完上面四句,日志里面就出現了下面這些
# 用戶id=5 路由到server_order1
[INFO ] 2025-08-01 13:49:09.192 [ShardingSphere-Command-1] INFO ?ShardingSphere-SQL - Logic SQL: insert into t_order (id, order_no, user_id, amount) values (5, 'BIT001', 5, 20.00)
[INFO ] 2025-08-01 13:49:09.193 [ShardingSphere-Command-1] INFO ?ShardingSphere-SQL - Actual SQL: server_order1 ::: insert into t_order0 (id, order_no, user_id, amount) values (5, 'BIT001', 5, 20.00)
# 用戶id=6?路由到server_order0
[INFO ] 2025-08-01 13:49:18.316 [ShardingSphere-Command-1] INFO ?ShardingSphere-SQL - Logic SQL: insert into t_order (id, order_no, user_id, amount) values (6, 'BIT002', 6, 20.00)
[INFO ] 2025-08-01 13:49:18.317 [ShardingSphere-Command-1] INFO ?ShardingSphere-SQL - Actual SQL: server_order0 ::: insert into t_order0 (id, order_no, user_id, amount) values (6, 'BIT002', 6, 20.00)
# 用戶id=7?路由到server_order1
[INFO ] 2025-08-01 13:49:26.735 [ShardingSphere-Command-1] INFO ?ShardingSphere-SQL - Logic SQL: insert into t_order (id, order_no, user_id, amount) values (7, 'BIT003', 7, 20.00)
[INFO ] 2025-08-01 13:49:26.736 [ShardingSphere-Command-1] INFO ?ShardingSphere-SQL - Actual SQL: server_order1 ::: insert into t_order0 (id, order_no, user_id, amount) values (7, 'BIT003', 7, 20.00)
# 用戶id=8?路由到server_order0
[INFO ] 2025-08-01 13:49:33.587 [ShardingSphere-Command-1] INFO ?ShardingSphere-SQL - Logic SQL: insert into t_order (id, order_no, user_id, amount) values (8, 'BIT004', 8, 20.00)
[INFO ] 2025-08-01 13:49:33.587 [ShardingSphere-Command-1] INFO ?ShardingSphere-SQL - Actual SQL: server_order0 ::: insert into t_order0 (id, order_no, user_id, amount) values (8, 'BIT004', 8, 20.00)
與行表達式的效果一致。
2.7 水平分表配置
2.7.1.配置分表策略
我們在上面就單單只是配置了下面這個數據源
然后我們插入數據后報如下錯誤
mysql> insert into t_order (id, order_no, user_id, amount) values (5, 'BIT005', 1, 20.00);
ERROR 20087 (44000): Please check your sharding conditions `ShardingConditions(conditions=[ShardingCondition(values=[t_order.user_id = 1], startIndex=0)], sqlStatementContext=org.apache.shardingsphere.infra.binder.statement.dml.InsertStatementContext@57de437d, rule=org.apache.shardingsphere.sharding.rule.ShardingRule@20999517, subqueryContainsShardingCondition=true)` to avoid same record in table `t_order` routing to multiple data nodes.
報錯的原因是當前雖然根據 user_id 指定了分庫策略,但是沒有指定分表策略,也就是說當前不知道寫入數據庫中的 t_order0 表還是 t_order1 表
現在我們就來配置一下這個到底是寫入到哪一個表里面呢?
我們現在就根據插入的數據里面中的 order_no字段 來確定數據寫入t_order0和t_order1表哪個數據節點
注意這次我們 需要配置的文件是: conf/config-sharding.yaml,對應我們宿主機的目錄就是/bit/shardingsphere/proxy/conf/config-sharding.yaml。
首先我們先來到我們的/bit/shardingsphere/proxy/conf/,然后創建一個叫config-sharding.yaml的文件
cd /bit/shardingsphere/proxy/conf/
vim config-sharding.yaml
我們把下面的內容粘貼進去?
databaseName: bit_sharding_dbdataSources:server_user:url: jdbc:mysql://150.158.139.86:53310/user_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1server_order0:url: jdbc:mysql://150.158.139.86:63310/order_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1server_order1:url: jdbc:mysql://150.158.139.86:63311/order_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1rules:
- !SHARDINGtables:t_user: # 邏輯表名actualDataNodes: server_user.t_user # 由數據源名 + 表名組成t_order: # 邏輯表名actualDataNodes: server_order${0..1}.t_order${0..1}databaseStrategy: # 分庫策略standard: # 用于單分片鍵的標準分片場景shardingColumn: user_id # 分片列名稱shardingAlgorithmName: alg_mod # 分片算法名稱(自定義)——和下面的alg_mod對應tableStrategy: #分表策略standard:shardingColumn: order_no # 分片列名稱shardingAlgorithmName: alg_mod # 分片算法名稱(自定義)——和下面的alg_mod對應 # 分片算法配置shardingAlgorithms:alg_db_inline_userid: # 自定義的分片算法名稱_行表達式分片算法type: INLINE # 分片算法類型props:algorithm-expression: server_order${user_id % 2} # 分片算法,根據 user_id對2取模alg_mod: # 自定義算法名_取模分片算法type: MODprops:sharding-count: 2
注意我修改了下面這些字段
?我們保存退出。重啟Docker容器
docker restart ss-proxy
docker ps
運行結果
root@VM-16-14-ubuntu:/bit/shardingsphere/proxy/conf# docker restart ss-proxy
ss-proxy
root@VM-16-14-ubuntu:/bit/shardingsphere/proxy/conf# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d9e2e351fb8a mysql:8.0.42 "docker-entrypoint.s…" 22 hours ago Up 22 hours 33060/tcp, 0.0.0.0:63311->3306/tcp, [::]:63311->3306/tcp server-order1
d2d5c33d7ca2 mysql:8.0.42 "docker-entrypoint.s…" 22 hours ago Up 22 hours 33060/tcp, 0.0.0.0:63310->3306/tcp, [::]:63310->3306/tcp server-order0
b5051d4517c4 mysql:8.0.42 "docker-entrypoint.s…" 29 hours ago Up 8 hours 33060/tcp, 0.0.0.0:53310->3306/tcp, [::]:53310->3306/tcp server-user
efc3b3c7dabc apache/shardingsphere-proxy:5.3.2 "/bin/sh -c '${LOCAL…" 47 hours ago Up 33 seconds 0.0.0.0:3307->3307/tcp, [::]:3307->3307/tcp ss-proxy
??然后我們去登陸進這個服務器來
mysql -h127.0.0.1 -P3307 -uroot -p
然后往我們執行下面這個
-- 插入一條數據
insert into t_order (id, order_no, user_id, amount) values (9, 'BIT009', 9, 20.00);
?得到如下錯誤信息
30000 - Unknown exception: For input string: "BIT009"
這個是因為配置文件中指定的數據分片算法類型是 MOD,但是訂單號是一個字符串,對字符串取模會報錯
2.7.2 HASH取模分片算法
修改配置文件添加HASH取模分片算法
databaseName: bit_sharding_dbdataSources:server_user:url: jdbc:mysql://150.158.139.86:53310/user_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1server_order0:url: jdbc:mysql://150.158.139.86:63310/order_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1server_order1:url: jdbc:mysql://150.158.139.86:63311/order_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1rules:
- !SHARDINGtables:t_user: # 邏輯表名actualDataNodes: server_user.t_user # 由數據源名 + 表名組成t_order: # 邏輯表名actualDataNodes: server_order${0..1}.t_order${0..1}databaseStrategy: # 分庫策略standard: # 用于單分片鍵的標準分片場景shardingColumn: user_id # 分片列名稱shardingAlgorithmName: alg_mod # 分片算法名稱(自定義)——和下面的alg_mod對應tableStrategy: #分表策略standard:shardingColumn: order_no # 分片列名稱shardingAlgorithmName: alg_hash_mod # 分片算法名稱(自定義)——和下面的alg_hash_mod對應 # 分片算法配置shardingAlgorithms:alg_db_inline_userid: # 自定義的分片算法名稱_行表達式分片算法type: INLINE # 分片算法類型props:algorithm-expression: server_order${user_id % 2} # 分片算法,根據 user_id對2取模alg_mod: # 自定義算法名_取模分片算法type: MODprops:sharding-count: 2alg_hash_mod:type: HASH_MOD # 分片算法類型props:sharding-count: 2 # 分片數量,表示根據分片列對2取模
我們保存退出。重啟Docker容器
docker restart ss-proxy
docker ps
運行結果
root@VM-16-14-ubuntu:/bit/shardingsphere/proxy/conf# docker restart ss-proxy
ss-proxy
root@VM-16-14-ubuntu:/bit/shardingsphere/proxy/conf# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d9e2e351fb8a mysql:8.0.42 "docker-entrypoint.s…" 22 hours ago Up 22 hours 33060/tcp, 0.0.0.0:63311->3306/tcp, [::]:63311->3306/tcp server-order1
d2d5c33d7ca2 mysql:8.0.42 "docker-entrypoint.s…" 22 hours ago Up 22 hours 33060/tcp, 0.0.0.0:63310->3306/tcp, [::]:63310->3306/tcp server-order0
b5051d4517c4 mysql:8.0.42 "docker-entrypoint.s…" 29 hours ago Up 8 hours 33060/tcp, 0.0.0.0:53310->3306/tcp, [::]:53310->3306/tcp server-user
efc3b3c7dabc apache/shardingsphere-proxy:5.3.2 "/bin/sh -c '${LOCAL…" 47 hours ago Up 33 seconds 0.0.0.0:3307->3307/tcp, [::]:3307->3307/tcp ss-proxy
2.7.3.測試?
?我們先去打開實時日志
tail -f /bit/shardingsphere/proxy/logs/shardingsphere.log
?然后我們去登陸進這個服務器來
mysql -h127.0.0.1 -P3307 -uroot -p
然后往我們執行下面這個
清空之前的所有數據
TRUNCATE TABLE t_order;
插入測試1:執行INSERT語句,user_id = 1
-- 每條記錄的user_id相同,order_no不同
insert into t_order (id, order_no, user_id, amount) values (1, 'BIT001', 1, 20.00);
insert into t_order (id, order_no, user_id, amount) values (2, 'BIT002', 1, 20.00);
insert into t_order (id, order_no, user_id, amount) values (3, 'BIT003', 1, 20.00);
insert into t_order (id, order_no, user_id, amount) values (4, 'BIT004', 1, 20.00);-- 訂單號對應的HASHCODE
-- BIT001 -> 1959491108
-- BIT002 -> 1959491109
-- BIT003 -> 1959491110
-- BIT004 -> 1959491111
查看日志,發現多了下面這些
# BIT001的記錄被路由到了server_order1 里的t_order0 表中
[INFO ] 2025-08-02 01:48:12.377 [ShardingSphere-Command-3] INFO ?ShardingSphere-SQL - Logic SQL: insert into t_order (id, order_no, user_id, amount) values (1, 'BIT001', 1, 20.00)
[INFO ] 2025-08-02 01:48:12.377 [ShardingSphere-Command-3] INFO ?ShardingSphere-SQL - Actual SQL: server_order1 ::: insert into t_order0 (id, order_no, user_id, amount) values (1, 'BIT001', 1, 20.00)
# BIT002的記錄被路由到了server_order1 里的t_order1 表中
[INFO ] 2025-08-02 01:48:21.181 [ShardingSphere-Command-3] INFO ?ShardingSphere-SQL - Logic SQL: insert into t_order (id, order_no, user_id, amount) values (2, 'BIT002', 1, 20.00)
[INFO ] 2025-08-02 01:48:21.181 [ShardingSphere-Command-3] INFO ?ShardingSphere-SQL - Actual SQL: server_order1 ::: insert into t_order1 (id, order_no, user_id, amount) values (2, 'BIT002', 1, 20.00)
# BIT003的記錄被路由到了server_order1 里的t_order0 表中
[INFO ] 2025-08-02 01:48:29.177 [ShardingSphere-Command-3] INFO ?ShardingSphere-SQL - Logic SQL: insert into t_order (id, order_no, user_id, amount) values (3, 'BIT003', 1, 20.00)
[INFO ] 2025-08-02 01:48:29.177 [ShardingSphere-Command-3] INFO ?ShardingSphere-SQL - Actual SQL: server_order1 ::: insert into t_order0 (id, order_no, user_id, amount) values (3, 'BIT003', 1, 20.00)
# BIT004的記錄被路由到了server_order1 里的t_order1 表中
[INFO ] 2025-08-02 01:48:37.805 [ShardingSphere-Command-3] INFO ?ShardingSphere-SQL - Logic SQL: insert into t_order (id, order_no, user_id, amount) values (4, 'BIT004', 1, 20.00)
[INFO ] 2025-08-02 01:48:37.805 [ShardingSphere-Command-3] INFO ?ShardingSphere-SQL - Actual SQL: server_order1 ::: insert into t_order1 (id, order_no, user_id, amount) values (4, 'BIT004', 1, 20.00)
?
插入測試2:執行INSERT語句,user_id = 2
-- 每條記錄的用戶Id相同,訂單號不同
insert into t_order (id, order_no, user_id, amount) values (5, 'BIT005', 2, 20.00);
insert into t_order (id, order_no, user_id, amount) values (6, 'BIT006', 2, 20.00);
insert into t_order (id, order_no, user_id, amount) values (7, 'BIT007', 2, 20.00);
insert into t_order (id, order_no, user_id, amount) values (8, 'BIT008', 2, 20.00);-- 訂單號對應的HASHCODE
-- BIT006 -> 1959491112
-- BIT006 -> 1959491113
-- BIT007 -> 1959491114
-- BIT008 -> 1959491115
?
我們查看一下日志,發現多下面這個
# BIT005的記錄被路由到了server_order0?里的t_order0 表中
[INFO ] 2025-08-02 01:53:32.014 [ShardingSphere-Command-4] INFO ?ShardingSphere-SQL - Logic SQL: insert into t_order (id, order_no, user_id, amount) values (5, 'BIT005', 2, 20.00)
[INFO ] 2025-08-02 01:53:32.014 [ShardingSphere-Command-4] INFO ?ShardingSphere-SQL - Actual SQL: server_order0 ::: insert into t_order0 (id, order_no, user_id, amount) values (5, 'BIT005', 2, 20.00)
# BIT006的記錄被路由到了server_order0?里的t_order1?表中
[INFO ] 2025-08-02 01:53:41.512 [ShardingSphere-Command-4] INFO ?ShardingSphere-SQL - Logic SQL: insert into t_order (id, order_no, user_id, amount) values (6, 'BIT006', 2, 20.00)
[INFO ] 2025-08-02 01:53:41.512 [ShardingSphere-Command-4] INFO ?ShardingSphere-SQL - Actual SQL: server_order0 ::: insert into t_order1 (id, order_no, user_id, amount) values (6, 'BIT006', 2, 20.00)
# BIT007的記錄被路由到了server_order0?里的t_order0 表中
[INFO ] 2025-08-02 01:53:49.294 [ShardingSphere-Command-4] INFO ?ShardingSphere-SQL - Logic SQL: insert into t_order (id, order_no, user_id, amount) values (7, 'BIT007', 2, 20.00)
[INFO ] 2025-08-02 01:53:49.294 [ShardingSphere-Command-4] INFO ?ShardingSphere-SQL - Actual SQL: server_order0 ::: insert into t_order0 (id, order_no, user_id, amount) values (7, 'BIT007', 2, 20.00)
# BIT008的記錄被路由到了server_order0?里的t_order1?表中
[INFO ] 2025-08-02 01:53:56.474 [ShardingSphere-Command-4] INFO ?ShardingSphere-SQL - Logic SQL: insert into t_order (id, order_no, user_id, amount) values (8, 'BIT008', 2, 20.00)
[INFO ] 2025-08-02 01:53:56.475 [ShardingSphere-Command-4] INFO ?ShardingSphere-SQL - Actual SQL: server_order0 ::: insert into t_order1 (id, order_no, user_id, amount) values (8, 'BIT008', 2, 20.00)
?
很完美啊!!
查詢測試1:查詢所有內容
查詢t_order表所有內容
-- 查詢所有訂單記錄
select * from t_order;
查看日志
# 邏輯SQL
[INFO ] 2025-08-02 01:59:52.401 [ShardingSphere-Command-5] INFO ?ShardingSphere-SQL - Logic SQL: select * from t_order
# 實際SQL,查詢了所有的數據節點
[INFO ] 2025-08-02 01:59:52.402 [ShardingSphere-Command-5] INFO ?ShardingSphere-SQL - Actual SQL: server_order0 ::: select * from t_order0 UNION ALL select * from t_order1
[INFO ] 2025-08-02 01:59:52.402 [ShardingSphere-Command-5] INFO ?ShardingSphere-SQL - Actual SQL: server_order1 ::: select * from t_order0 UNION ALL select * from t_order1
可以看出ShardinSphere在所有的數據節點查詢記錄,并把各個節點的記錄組裝好,統一返回給客戶端
查詢測試2:條件查詢
-- 查詢指定用戶的訂單
select * from t_order where user_id = 1;
查看日志
# 邏輯SQL
[INFO ] 2025-08-02 02:01:10.337 [ShardingSphere-Command-6] INFO ?ShardingSphere-SQL - Logic SQL: select * from t_order where user_id = 1
# 實際SQL,查詢部分數據節點
[INFO ] 2025-08-02 02:01:10.338 [ShardingSphere-Command-6] INFO ?ShardingSphere-SQL - Actual SQL: server_order1 ::: select * from t_order0 where user_id = 1 UNION ALL select * from t_order1 where user_id = 1
-- 查詢指定用戶的訂單
select * from t_order where user_id = 2;
查看日志
# 邏輯SQL
[INFO ] 2025-08-02 02:02:23.620 [ShardingSphere-Command-7] INFO ?ShardingSphere-SQL - Logic SQL: select * from t_order where user_id = 2
# 實際SQL,查詢部分數據節點
[INFO ] 2025-08-02 02:02:23.620 [ShardingSphere-Command-7] INFO ?ShardingSphere-SQL - Actual SQL: server_order0 ::: select * from t_order0 where user_id = 2 UNION ALL select * from t_order1 where user_id = 2?
根據分片字段進行查詢時ShardinSphere根據分片規則訪問記錄所有的數據節點,并返回結果
分片時應使用經常查詢的字段進行分片。
2.8 分布式序列算法
2.8.1.分布式環境下主鍵的自動生成存在的問題
在傳統單機數據庫的軟件開發中,主鍵的自動生成是一項基礎且關鍵的需求。主流數據庫系統為此提供了成熟的內置支持機制,例如 MySQL 的?AUTO_INCREMENT
?自增列和 Oracle 的序列 (SEQUENCE
)。這些機制在單一數據庫實例內能夠高效、可靠地保證主鍵的唯一性和連續性。
然而,當數據量激增需要進行水平分片(Sharding)?時,確保全局唯一主鍵的生成就變得極具挑戰性。在分片架構下,原本單一的邏輯表的數據會被分散存儲在多個物理分片(數據節點)?的不同實際表中。問題在于,這些位于不同分片上的實際表,其內置的自增機制(如 MySQL 的?AUTO_INCREMENT
?或類似功能)是彼此獨立、互不感知的。每個分片只在其自身的范圍內維護自增序列,無法得知其他分片的當前值。這必然導致一個嚴重后果:不同分片上的實際表會生成完全相同的自增值作為主鍵,從而破壞了全局唯一性約束。
這么說還是有點不太直觀,完美看個例子
想象一所學校(相當于整個數據庫系統):
-
單班教學(傳統單機數據庫):
-
學校只有一個班級(一個數據庫實例)。
-
老師(數據庫的自增機制,如 MySQL?
AUTO_INCREMENT
)負責給班里的每個新學生(新記錄)分配一個唯一的學號(主鍵)。 -
老師手里有一份名單,記錄著上一個學號是多少(比如 50),新學生來了就順延(51,52,53...)。非常簡單,保證學號在班里絕不重復。
-
-
學生太多,需要分班(數據分片/Sharding):
-
學校學生暴漲(數據量激增),一個班裝不下了。
-
學校決定分成 3 個平行班:一班、二班、三班(3個物理分片/數據節點)。每個班都有一份學生名單(實際表),記錄自己班的學生。
-
學校依然需要保證全校范圍內每個學生的學號都是唯一的(全局唯一主鍵)。
-
-
獨立班主任的問題(分片獨立的自增機制):
-
每個班都安排了一位班主任(分片上的自增機制)。
-
學校沒有統一協調學號的辦公室。
-
一班班主任:?自己手里有一份名單,從 1 開始給學生編號(1, 2, 3...)。
-
二班班主任:?同樣,自己手里也有一份名單,也從 1 開始給學生編號(1, 2, 3...)。
-
三班班主任:?同樣如此(1, 2, 3...)。
-
-
學號沖突(主鍵重復):
-
現在,一班有個學生學號是 1(張三),二班也有個學生學號是 1(李四),三班也有個學生學號是 1(王五)。
-
問題來了:?當學校需要把所有班級的學生名單合并起來看(查詢邏輯表數據)時,系統會發現有三個學號為 1 的學生!這嚴重違反了“全校唯一學號”的規定(破壞了主鍵的全局唯一性)。
-
即使班主任們各自在班內編號是連續的、不重復的(如一班:1,2,3;二班:1,2,3;三班:1,2,3),但放到全校范圍看,學號 1 重復了三次,學號 2、3 等等也都重復了。
-
這個例子如何對應技術概念:
-
學校:?整個數據庫系統 / 邏輯表。
-
班級:?物理分片 / 數據節點 / 實際表。
-
班主任:?分片上獨立運行的數據庫自增機制(如 MySQL?
AUTO_INCREMENT
)。 -
班主任手里的獨立名單:?每個分片/實際表維護的獨立的、互不感知的自增序列。
-
學號:?主鍵值。
-
學號沖突:?不同分片上的實際表生成了相同的 ID,導致全局主鍵重復。
-
“全校唯一學號”規定:?數據庫主鍵必須全局唯一的約束。
核心難點:
就像班主任們不知道其他班用了哪些學號一樣,分片一上的 MySQL 不知道分片二和分片三下一個自增 ID 是什么。它們各自在本地計數,必然會導致全局范圍內的 ID 重復。
結論:
這就是為什么在分片數據庫環境下,不能直接依賴數據庫原生的單機自增機制(如 MySQL?AUTO_INCREMENT
?或 Oracle?SEQUENCE
)來生成主鍵。需要引入分布式 ID 生成方案(如 Snowflake 算法、UUID、基于數據庫號段的分配、中心化的 ID 生成服務等)來解決班主任們(各分片)之間缺乏協調的問題,確保全校(全局)學號(主鍵)的唯一性。
目前有許多第三方解決方案可以完美解決這個問題,如 UUID 等依靠特定算法自生成不重復鍵,或者通過引入主鍵生成服務等。為了方便用戶使用、滿足不同用戶不同使用場景的需求,Apache ShardingSphere 不僅提供了內置的分布式主鍵生成器,例如 UUID、SNOWFLAKE,還抽離出分布式主鍵生成器的接口,方便用戶自行實現自定義的自增主鍵生成器。
2.8.2 UUID
UUID是ShardingSphere內置的分布式序列算法之一。
可以生成時間、空間上都獨一無二的值,但是由于UUID是無序的,不太適合作為數據庫的主鍵
2.8.3? 雪花算法
簡介
ShardingSphere在分片規則配置模塊可配置每個表的主鍵生成策略,默認使用雪花算法(snowflake)生成64bit的長整型數據。
雪花算法是由Twitter公布的分布式主鍵生成算法,它能夠保證不同進程主鍵的不重復性,以及相同進程主鍵的有序性。
實現原理
核心目標:?在分布式系統(很多臺機器一起干活)中,給每一條數據生成一個全局唯一且大體有序的ID(主鍵)。?
想象一個超大的學校(分布式系統)需要給學生發學號(主鍵):
-
傳統問題:?如果讓每個班級(數據庫分片)的班主任(數據庫自增)自己發學號,就會出現多個班都有“學號1”的情況,全校范圍就重復了。
-
雪花算法解決方案:?學校設立了一個智能學號生成中心(雪花算法),統一負責給全校所有學生發學號。這個中心發學號的規則非常精密:
學號的構成(64位二進制,最終轉成一個長整數):
這個學號(ID)就像一張信息卡片,由4個關鍵部分組成:
-
第1部分:預留位 (1 bit) - “國籍標識”
-
恒定為0(正數)。就像標識這個學號是“本校”的,暫時沒用,先占個位置。
-
-
第2部分:時間戳 (41 bits) - “入學年份+精確到毫秒的報到時間”
-
這是最重要的部分!?它記錄了學生報到(ID生成)的精確時間(毫秒級)。
-
能表示的時間范圍超級長:大約69年(從2016年11月1日開始算,能用到2086年左右)。足夠學校用很久很久了。
-
作用1 (唯一性基礎):?時間在一直往前走,不同毫秒生成的學生,學號的時間部分肯定不同。
-
作用2 (有序性):?后報到的學生,學號的時間部分一定比先報到的學生大。這樣學號整體上就是按時間順序增長的,方便排序和管理(就像按入學時間排學號)。
-
-
第3部分:工作機器ID (10 bits) - “班級編號”
-
學校有很多個報名點(不同的服務器/服務進程)。每個報名點需要有一個唯一的編號(比如001班,002班...)。
-
10位二進制最多能表示1024個不同的報名點(2^10=1024)。足夠給學校里的每個報名點都分配一個唯一編號。
-
作用 (分布式隔離):?保證即使同一毫秒,在不同報名點報到的學生,他們的學號也不會沖突。因為班級編號不同。比如001班和002班在同一毫秒各報到一個學生,他們的時間部分相同,但班級編號不同。
-
-
第4部分:序列號 (12 bits) - “當天流水號”
-
想象同一個報名點(同一個班級),在同一毫秒內,可能有好幾個學生同時來報到(高并發)。
-
12位序列號就是用來區分這些“同一毫秒、同一報名點”的學生的流水號(0001, 0002, 0003...)。
-
最多能區分4096個(2^12=4096)。如果某個報名點某一毫秒內涌進來超過4096個學生(極端高并發),怎么辦?算法會“等一等”,等到下一毫秒再繼續從0開始編號。
-
作用 (毫秒內唯一):?保證同一個報名點(服務器)在同一毫秒內產生的多個ID也是唯一的。
-
總結雪花算法的工作流程(發學號):
-
看時間:?獲取當前的毫秒級時間戳。
-
看地點:?知道自己是哪個報名點(工作機器ID)。
-
看現場:?查詢自己這個報名點、在當前這個毫秒內,已經發出了多少個學號(序列號)。
-
組學號:?把 [0] + [當前時間戳] + [我的報名點編號] + [下一個流水號] 這四部分拼裝起來,形成一個完整的64位二進制數(最終展現為一個很大的長整數)。
-
記一筆:?更新一下記錄,標記自己在這個毫秒內已經發到第幾個流水號了。如果流水號用完了(達到4096),就等到下一個毫秒再發,流水號重置為0。
優勢:
-
全局唯一:?時間戳(不同時間)+ 工作機器ID(不同地點)+ 序列號(同一時間地點區分)的組合,理論上保證了在整個分布式系統中生成的ID都是唯一的。
-
趨勢遞增(有序):?因為時間戳是不斷增長的,生成的ID整體上是越來越大的。這對于數據庫按主鍵索引插入數據非常高效(如MySQL InnoDB)。
-
高性能:?完全在本地生成(無需像數據庫自增那樣去中心節點申請),速度非常快。
-
可伸縮:?通過配置不同的工作機器ID,可以輕松支持大量的服務器節點。
時鐘回撥?
問題是什么??
想象學校用來計時的鐘表,突然因為故障或同步問題往回走了(比如從10:00:05跳回到了10:00:03)。這會導致一個嚴重問題:雪花算法嚴重依賴時間遞增。如果時間回撥了,算法就可能在新生成的ID中使用了一個過去的時間戳。而在這個“過去的時間戳”里,它之前可能已經發過一些學號了。這就可能導致生成重復的ID!
雪花算法怎么應對?
-
容忍小回撥:?算法可以設置一個最大容忍的時鐘回撥時間(比如100毫秒)。如果發現時鐘只回撥了一點點(小于這個閾值),它就暫停發號,等待系統時間“自然走”到超過它最后一次成功生成ID的時間點之后,再繼續工作。這期間如果有生成ID的請求,可能會等待或報錯(看具體實現)。
-
拒絕大回撥:?如果時鐘回撥得太厲害(超過了設置的容忍閾值),算法會直接拋出錯誤,拒絕生成ID。因為這超出了它能安全處理的范圍,必須由人工干預(檢查并修復服務器時間同步問題)。
分布式序列配置
注意這次我們 需要配置的文件是: conf/config-sharding.yaml,對應我們宿主機的目錄就是/bit/shardingsphere/proxy/conf/config-sharding.yaml。
首先我們先來到我們的/bit/shardingsphere/proxy/conf/,然后創建一個叫config-sharding.yaml的文件
cd /bit/shardingsphere/proxy/conf/
vim config-sharding.yaml
我們把下面的內容粘貼進去?
databaseName: bit_sharding_dbdataSources:server_user:url: jdbc:mysql://150.158.139.86:53310/user_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1server_order0:url: jdbc:mysql://150.158.139.86:63310/order_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1server_order1:url: jdbc:mysql://150.158.139.86:63311/order_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1rules:
- !SHARDINGtables:t_user: # 邏輯表名actualDataNodes: server_user.t_user # 由數據源名 + 表名組成t_order: # 邏輯表名actualDataNodes: server_order${0..1}.t_order${0..1}databaseStrategy: # 分庫策略standard: # 用于單分片鍵的標準分片場景shardingColumn: user_id # 分片列名稱shardingAlgorithmName: alg_mod # 分片算法名稱(自定義)——和下面的alg_mod對應tableStrategy: #分表策略standard:shardingColumn: order_no # 分片列名稱shardingAlgorithmName: alg_hash_mod # 分片算法名稱(自定義)——和下面的alg_hash_mod對應 keyGenerateStrategy: # 分布式序列策略column: id # 列名keyGeneratorName: alg_snowflake # 分布式序列算法名# 分片算法配置shardingAlgorithms:alg_db_inline_userid: # 自定義的分片算法名稱_行表達式分片算法type: INLINE # 分片算法類型props:algorithm-expression: server_order${user_id % 2} # 分片算法,根據 user_id對2取模alg_mod: # 自定義算法名_取模分片算法type: MODprops:sharding-count: 2alg_hash_mod:type: HASH_MOD # 分片算法類型props:sharding-count: 2 # 分片數量,表示根據分片列對2取模# 分布式序列配置keyGenerators:alg_snowflake: # 分布式序列算法名type: SNOWFLAKE # 分布式序列類型(雪花算法)
注意我們修改了配置文件的下面這些地方
?我們保存退出。重啟Docker容器
docker restart ss-proxy
docker ps
運行結果
root@VM-16-14-ubuntu:/bit/shardingsphere/proxy/conf# docker restart ss-proxy
ss-proxy
root@VM-16-14-ubuntu:/bit/shardingsphere/proxy/conf# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d9e2e351fb8a mysql:8.0.42 "docker-entrypoint.s…" 22 hours ago Up 22 hours 33060/tcp, 0.0.0.0:63311->3306/tcp, [::]:63311->3306/tcp server-order1
d2d5c33d7ca2 mysql:8.0.42 "docker-entrypoint.s…" 22 hours ago Up 22 hours 33060/tcp, 0.0.0.0:63310->3306/tcp, [::]:63310->3306/tcp server-order0
b5051d4517c4 mysql:8.0.42 "docker-entrypoint.s…" 29 hours ago Up 8 hours 33060/tcp, 0.0.0.0:53310->3306/tcp, [::]:53310->3306/tcp server-user
efc3b3c7dabc apache/shardingsphere-proxy:5.3.2 "/bin/sh -c '${LOCAL…" 47 hours ago Up 33 seconds 0.0.0.0:3307->3307/tcp, [::]:3307->3307/tcp ss-proxy
寫入測試
???我們先去打開實時日志
tail -f /bit/shardingsphere/proxy/logs/shardingsphere.log
?然后我們去登陸進這個服務器來
mysql -h127.0.0.1 -P3307 -uroot -p
然后我們執行下面這個
清空之前的所有數據
TRUNCATE TABLE t_order;
接著我們執行INSERT語句,注意:這次我們不用指定主鍵的值
-- user_id=1的訂單
insert into t_order (order_no, user_id, amount) values ('BIT001', 1, 20.00);
insert into t_order (order_no, user_id, amount) values ('BIT002', 1, 20.00);
insert into t_order (order_no, user_id, amount) values ('BIT003', 1, 20.00);
insert into t_order (order_no, user_id, amount) values ('BIT004', 1, 20.00);
-- user_id=2的訂單
insert into t_order (order_no, user_id, amount) values ('BIT005', 2, 20.00);
insert into t_order (order_no, user_id, amount) values ('BIT006', 2, 20.00);
insert into t_order (order_no, user_id, amount) values ('BIT007', 2, 20.00);
insert into t_order (order_no, user_id, amount) values ('BIT008', 2, 20.00);
查看日志
# 數據根據分片策略寫入到不同的數據節點,并生成了主鍵值?
# BIT001的記錄被路由到了server_order1 里的t_order0 表中,并生成了主鍵值
[INFO ] 2025-08-02 03:01:38.295 [ShardingSphere-Command-1] INFO ?ShardingSphere-SQL - Logic SQL: insert into t_order (order_no, user_id, amount) values ('BIT001', 1, 20.00)
[INFO ] 2025-08-02 03:01:38.295 [ShardingSphere-Command-1] INFO ?ShardingSphere-SQL - Actual SQL: server_order1 ::: insert into t_order0 (order_no, user_id, amount, id) values ('BIT001', 1, 20.00, 1158237329177968640)
# BIT002的記錄被路由到了server_order1?里的t_order1?表中,并生成了主鍵值
[INFO ] 2025-08-02 03:01:47.326 [ShardingSphere-Command-1] INFO ?ShardingSphere-SQL - Logic SQL: insert into t_order (order_no, user_id, amount) values ('BIT002', 1, 20.00)
[INFO ] 2025-08-02 03:01:47.327 [ShardingSphere-Command-1] INFO ?ShardingSphere-SQL - Actual SQL: server_order1 ::: insert into t_order1 (order_no, user_id, amount, id) values ('BIT002', 1, 20.00, 1158237367098671105)
# BIT003的記錄被路由到了server_order1 里的t_order0 表中,并生成了主鍵值
[INFO ] 2025-08-02 03:01:59.026 [ShardingSphere-Command-1] INFO ?ShardingSphere-SQL - Logic SQL: insert into t_order (order_no, user_id, amount) values ('BIT003', 1, 20.00)
[INFO ] 2025-08-02 03:01:59.026 [ShardingSphere-Command-1] INFO ?ShardingSphere-SQL - Actual SQL: server_order1 ::: insert into t_order0 (order_no, user_id, amount, id) values ('BIT003', 1, 20.00, 1158237416167833600)
# BIT004的記錄被路由到了server_order1 里的t_order1?表中,并生成了主鍵值
[INFO ] 2025-08-02 03:02:06.901 [ShardingSphere-Command-1] INFO ?ShardingSphere-SQL - Logic SQL: insert into t_order (order_no, user_id, amount) values ('BIT004', 1, 20.00)
[INFO ] 2025-08-02 03:02:06.901 [ShardingSphere-Command-1] INFO ?ShardingSphere-SQL - Actual SQL: server_order1 ::: insert into t_order1 (order_no, user_id, amount, id) values ('BIT004', 1, 20.00, 1158237449197977601)
# BIT005的記錄被路由到了server_order0?里的t_order0 表中,并生成了主鍵值
[INFO ] 2025-08-02 03:02:14.375 [ShardingSphere-Command-1] INFO ?ShardingSphere-SQL - Logic SQL: insert into t_order (order_no, user_id, amount) values ('BIT005', 2, 20.00)
[INFO ] 2025-08-02 03:02:14.375 [ShardingSphere-Command-1] INFO ?ShardingSphere-SQL - Actual SQL: server_order0 ::: insert into t_order0 (order_no, user_id, amount, id) values ('BIT005', 2, 20.00, 1158237480546205696)
# BIT006的記錄被路由到了server_order0?里的t_order1?表中,并生成了主鍵值
[INFO ] 2025-08-02 03:02:22.718 [ShardingSphere-Command-1] INFO ?ShardingSphere-SQL - Logic SQL: insert into t_order (order_no, user_id, amount) values ('BIT006', 2, 20.00)
[INFO ] 2025-08-02 03:02:22.718 [ShardingSphere-Command-1] INFO ?ShardingSphere-SQL - Actual SQL: server_order0 ::: insert into t_order1 (order_no, user_id, amount, id) values ('BIT006', 2, 20.00, 1158237515539283969)
# BIT007的記錄被路由到了server_order0?里的t_order0 表中,并生成了主鍵值
[INFO ] 2025-08-02 03:02:29.942 [ShardingSphere-Command-1] INFO ?ShardingSphere-SQL - Logic SQL: insert into t_order (order_no, user_id, amount) values ('BIT007', 2, 20.00)
[INFO ] 2025-08-02 03:02:29.942 [ShardingSphere-Command-1] INFO ?ShardingSphere-SQL - Actual SQL: server_order0 ::: insert into t_order0 (order_no, user_id, amount, id) values ('BIT007', 2, 20.00, 1158237545838936064)
# BIT008的記錄被路由到了server_order0?里的t_order1?表中,并生成了主鍵值
[INFO ] 2025-08-02 03:02:38.577 [ShardingSphere-Command-1] INFO ?ShardingSphere-SQL - Logic SQL: insert into t_order (order_no, user_id, amount) values ('BIT008', 2, 20.00)
[INFO ] 2025-08-02 03:02:38.577 [ShardingSphere-Command-1] INFO ?ShardingSphere-SQL - Actual SQL: server_order0 ::: insert into t_order1 (order_no, user_id, amount, id) values ('BIT008', 2, 20.00, 1158237582056751105)
2.9 多表關聯查詢?
還記得我們的業務嗎?
一個用戶可能會有多個訂單,而且每個訂單也有多個訂單信息。
在真實的電商業務場景中,訂單數據通常與訂單詳情數據緊密關聯。其中:
-
訂單表 (
t_order
)?記錄訂單的核心信息(如訂單號、用戶ID、總金額、狀態等)。 -
訂單詳情表 (
t_order_item
)?則記錄用戶購買的具體商品信息,包括商品ID、單價、購買數量等。一個訂單可能包含多個商品項,因此訂單 (t_order
)?與訂單詳情 (t_order_item
)?之間是典型的一對多關系。
為了優化數據庫性能并避免復雜的跨庫關聯查詢(JOIN),一個關鍵的設計原則是:將同一個用戶產生的訂單數據及其關聯的所有訂單詳情數據,存儲在同一個物理數據源(分片)中。
基于此原則:
-
t_order
?表的分片策略:?通常會選擇?user_id
?作為分片鍵(Sharding Key)。這樣能確保同一用戶的所有訂單都落在同一個分片上。 -
t_order_item
?表的分片策略:?為了實現上述“同用戶訂單及詳情同庫”的目標,t_order_item
?表必須采用與?t_order
?表相同的分片策略。雖然?t_order_item
?表本身與訂單直接關聯的字段是?order_no
?(訂單號),但僅靠?order_no
?無法保證其與父訂單?t_order
?記錄在同一分片。
因此,在?t_order_item
?表中:
-
需要冗余添加?
user_id
?字段。這個字段來源于其所屬訂單 (t_order
) 中的?user_id
。 -
同時,
t_order_item
?表的分片鍵也需要包含?user_id
?(通常會與?order_no
?組合使用,但核心是?user_id
?確保分片路由)。
關鍵補充說明:
-
為什么需要?
user_id
??訂單詳情 (t_order_item
) 通過?order_no
?關聯到訂單 (t_order
)。如果?t_order_item
?只按?order_no
?分片,而?t_order
?按?user_id
?分片,那么一個用戶的不同訂單可能分布在不同的分片上(如果?user_id
?相同但?order_no
?的哈希值不同)。這會導致查詢某個用戶的所有訂單詳情時,需要跨多個分片掃描,性能低下。 -
冗余?
user_id
?的作用:?在?t_order_item
?表中添加?user_id
?字段(即使邏輯上可通過?order_no
?關聯到?t_order.user_id
),是為了讓?t_order_item
?表直接擁有?user_id
?這個信息。這樣,t_order_item
?就可以直接使用?user_id
?作為分片鍵(或作為組合分片鍵的主要部分)。 -
分片策略一致性:?當?
t_order
?和?t_order_item
?都使用?user_id
?(或?user_id
?+?order_no
?等組合) 作為分片鍵時,系統就能保證:-
同一個?
user_id
?下的所有?t_order
?記錄落在分片 A。 -
同一個?
user_id
?下的所有?t_order_item
?記錄也必然落在同一個分片 A。
-
-
避免跨庫關聯:?由于同一個用戶的所有訂單及其關聯的所有訂單詳情都存儲在同一個物理分片上,任何需要查詢“用戶+訂單+訂單詳情”的操作(例如:查詢用戶張三的所有訂單及其包含的商品),都只需要訪問一個分片即可完成,無需跨分片進行低效的 JOIN 操作,極大提升了查詢性能。
2.9.1?配置策略
?創建關聯表
分別在server-order0和server-order1服務器創建訂單詳情表t_order_item0和t_order_item1,表結構如下:
-- 創建訂單詳情 t_order_item0
CREATE TABLE IF NOT EXISTS t_order_item0 (id BIGINT PRIMARY KEY COMMENT '主鍵ID',order_no VARCHAR(30) COMMENT '訂單號',user_id BIGINT COMMENT '用戶編號',price DECIMAL(12, 2) COMMENT '商品單價',count INT COMMENT '商品個數'
);-- 創建訂單詳情 t_order_item1
CREATE TABLE IF NOT EXISTS t_order_item1 (id BIGINT PRIMARY KEY COMMENT '主鍵ID',order_no VARCHAR(30) COMMENT '訂單號',user_id BIGINT COMMENT '用戶編號',price DECIMAL(12, 2) COMMENT '商品單價',count INT COMMENT '商品個數'
);
我們登陸server-order0去看看
mysql -h127.0.0.1 -P63310 -u root -p
?
接著我們登陸server-order1去看看
mysql -h127.0.0.1 -P63311 -u root -p
?
配置策略
注意這次我們 需要配置的文件是: conf/config-sharding.yaml,對應我們宿主機的目錄就是/bit/shardingsphere/proxy/conf/config-sharding.yaml。
首先我們先來到我們的/bit/shardingsphere/proxy/conf/,然后創建一個叫config-sharding.yaml的文件
cd /bit/shardingsphere/proxy/conf/
vim config-sharding.yaml
我們把下面的內容粘貼進去?
databaseName: bit_sharding_dbdataSources:server_user:url: jdbc:mysql://150.158.139.86:53310/user_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1server_order0:url: jdbc:mysql://150.158.139.86:63310/order_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1server_order1:url: jdbc:mysql://150.158.139.86:63311/order_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1rules:
- !SHARDINGtables:t_user: # 邏輯表名actualDataNodes: server_user.t_user # 由數據源名 + 表名組成t_order: # 邏輯表名actualDataNodes: server_order${0..1}.t_order${0..1}databaseStrategy: # 分庫策略standard: # 用于單分片鍵的標準分片場景shardingColumn: user_id # 分片列名稱shardingAlgorithmName: alg_mod # 分片算法名稱(自定義)——和下面的alg_mod對應tableStrategy: #分表策略standard:shardingColumn: order_no # 分片列名稱shardingAlgorithmName: alg_hash_mod # 分片算法名稱(自定義)——和下面的alg_hash_mod對應 keyGenerateStrategy: # 分布式序列策略column: id # 列名keyGeneratorName: alg_snowflake # 分布式序列算法名t_order_item: # 邏輯表名t_order_itemactualDataNodes: server_order${0..1}.t_order_item${0..1} # 真實數據節點databaseStrategy: # 分庫策略standard: # 用于單分片鍵的標準分片場景shardingColumn: user_id # 分片列名稱#shardingAlgorithmName: alg_db_inline_userid ?# 分片算法名稱shardingAlgorithmName: alg_mod # 分片算法名稱tableStrategy: # 分表策略standard:shardingColumn: order_no # 分片列名稱shardingAlgorithmName: alg_hash_mod # 分片算法名稱keyGenerateStrategy: # 分布式序列策略column: id # 列名keyGeneratorName: alg_snowflake # 分布式序列算法名# 分片算法配置shardingAlgorithms:alg_db_inline_userid: # 自定義的分片算法名稱_行表達式分片算法type: INLINE # 分片算法類型props:algorithm-expression: server_order${user_id % 2} # 分片算法,根據 user_id對2取模alg_mod: # 自定義算法名_取模分片算法type: MODprops:sharding-count: 2alg_hash_mod:type: HASH_MOD # 分片算法類型props:sharding-count: 2 # 分片數量,表示根據分片列對2取模# 分布式序列配置keyGenerators:alg_snowflake: # 分布式序列算法名type: SNOWFLAKE # 分布式序列類型(雪花算法)
注意:其實我就添加了下面這些
現在我們就有3個邏輯表了
?我們保存退出。重啟Docker容器
docker restart ss-proxy
docker ps
運行結果
root@VM-16-14-ubuntu:/bit/shardingsphere/proxy/conf# docker restart ss-proxy
ss-proxy
root@VM-16-14-ubuntu:/bit/shardingsphere/proxy/conf# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d9e2e351fb8a mysql:8.0.42 "docker-entrypoint.s…" 22 hours ago Up 22 hours 33060/tcp, 0.0.0.0:63311->3306/tcp, [::]:63311->3306/tcp server-order1
d2d5c33d7ca2 mysql:8.0.42 "docker-entrypoint.s…" 22 hours ago Up 22 hours 33060/tcp, 0.0.0.0:63310->3306/tcp, [::]:63310->3306/tcp server-order0
b5051d4517c4 mysql:8.0.42 "docker-entrypoint.s…" 29 hours ago Up 8 hours 33060/tcp, 0.0.0.0:53310->3306/tcp, [::]:53310->3306/tcp server-user
efc3b3c7dabc apache/shardingsphere-proxy:5.3.2 "/bin/sh -c '${LOCAL…" 47 hours ago Up 33 seconds 0.0.0.0:3307->3307/tcp, [::]:3307->3307/tcp ss-proxy
2.9.2.測試
??我們先去打開實時日志
tail -f /bit/shardingsphere/proxy/logs/shardingsphere.log
?然后我們去登陸進這個服務器來
mysql -h127.0.0.1 -P3307 -uroot -p
構造數據
為每個訂單構造訂單詳情記錄,SQL語句如下:
INSERT INTO t_order_item (order_no, user_id, price, count) VALUES ('BIT001', 1, 10, 3);
INSERT INTO t_order_item (order_no, user_id, price, count) VALUES ('BIT001', 1, 10, 3);
INSERT INTO t_order_item (order_no, user_id, price, count) VALUES ('BIT001', 1, 10, 3);INSERT INTO t_order_item (order_no, user_id, price, count) VALUES ('BIT002', 1, 10, 3);
INSERT INTO t_order_item (order_no, user_id, price, count) VALUES ('BIT002', 1, 10, 3);
INSERT INTO t_order_item (order_no, user_id, price, count) VALUES ('BIT002', 1, 10, 3);INSERT INTO t_order_item (order_no, user_id, price, count) VALUES ('BIT003', 1, 10, 3);
INSERT INTO t_order_item (order_no, user_id, price, count) VALUES ('BIT003', 1, 10, 3);
INSERT INTO t_order_item (order_no, user_id, price, count) VALUES ('BIT003', 1, 10, 3);INSERT INTO t_order_item (order_no, user_id, price, count) VALUES ('BIT004', 1, 10, 3);
INSERT INTO t_order_item (order_no, user_id, price, count) VALUES ('BIT004', 1, 10, 3);
INSERT INTO t_order_item (order_no, user_id, price, count) VALUES ('BIT004', 1, 10, 3);INSERT INTO t_order_item (order_no, user_id, price, count) VALUES ('BIT005', 2, 20, 5);
INSERT INTO t_order_item (order_no, user_id, price, count) VALUES ('BIT005', 2, 20, 5);
INSERT INTO t_order_item (order_no, user_id, price, count) VALUES ('BIT005', 2, 20, 5);INSERT INTO t_order_item (order_no, user_id, price, count) VALUES ('BIT006', 2, 20, 5);
INSERT INTO t_order_item (order_no, user_id, price, count) VALUES ('BIT006', 2, 20, 5);
INSERT INTO t_order_item (order_no, user_id, price, count) VALUES ('BIT006', 2, 20, 5);INSERT INTO t_order_item (order_no, user_id, price, count) VALUES ('BIT007', 2, 20, 5);
INSERT INTO t_order_item (order_no, user_id, price, count) VALUES ('BIT007', 2, 20, 5);
INSERT INTO t_order_item (order_no, user_id, price, count) VALUES ('BIT007', 2, 20, 5);INSERT INTO t_order_item (order_no, user_id, price, count) VALUES ('BIT008', 2, 20, 5);
INSERT INTO t_order_item (order_no, user_id, price, count) VALUES ('BIT008', 2, 20, 5);
INSERT INTO t_order_item (order_no, user_id, price, count) VALUES ('BIT008', 2, 20, 5);
在各個數據節點驗證是否寫入成功
關聯查詢?
關聯查詢時,使用分片鍵進行表關聯,為了后面使用綁定表,ShardinSphere可以根據分片策略幫
我們自動路由到對應的數據節點
也可以使用訂單號進行關聯,但是ShardinSphere無法感知到目標數據在哪個數據節點,會在所有
數據源中進行查詢,對效率的影響比較大
根據條件查詢訂單和訂單詳情信息
-- 查詢所有訂單和詳情信息?
select * from t_order o, t_order_item i where o.order_no = i.order_no;
查看日志?
日志內容如下
邏輯SQL
[INFO ] 2025-08-02 07:50:44.323 [ShardingSphere-Command-2] INFO ?ShardingSphere-SQL - Logic SQL: select * from t_order o, t_order_item i where o.order_no = i.order_no
實際執行SQL
[INFO ] 2025-08-02 07:50:44.324 [ShardingSphere-Command-2] INFO ?ShardingSphere-SQL - Actual SQL: server_order1 ::: select * from t_order0 o, t_order_item0 i where o.order_no = i.order_no
[INFO ] 2025-08-02 07:50:44.324 [ShardingSphere-Command-2] INFO ?ShardingSphere-SQL - Actual SQL: server_order1 ::: select * from t_order1 o, t_order_item0 i where o.order_no = i.order_no
[INFO ] 2025-08-02 07:50:44.324 [ShardingSphere-Command-2] INFO ?ShardingSphere-SQL - Actual SQL: server_order1 ::: select * from t_order0 o, t_order_item1 i where o.order_no = i.order_no
[INFO ] 2025-08-02 07:50:44.324 [ShardingSphere-Command-2] INFO ?ShardingSphere-SQL - Actual SQL: server_order1 ::: select * from t_order1 o, t_order_item1 i where o.order_no = i.order_no
[INFO ] 2025-08-02 07:50:44.324 [ShardingSphere-Command-2] INFO ?ShardingSphere-SQL - Actual SQL: server_order0 ::: select * from t_order0 o, t_order_item0 i where o.order_no = i.order_no
[INFO ] 2025-08-02 07:50:44.324 [ShardingSphere-Command-2] INFO ?ShardingSphere-SQL - Actual SQL: server_order0 ::: select * from t_order1 o, t_order_item0 i where o.order_no = i.order_no
[INFO ] 2025-08-02 07:50:44.324 [ShardingSphere-Command-2] INFO ?ShardingSphere-SQL - Actual SQL: server_order0 ::: select * from t_order0 o, t_order_item1 i where o.order_no = i.order_no
[INFO ] 2025-08-02 07:50:44.324 [ShardingSphere-Command-2] INFO ?ShardingSphere-SQL - Actual SQL: server_order0 ::: select * from?t_order1 o, t_order_item1 i where o.order_no = i.order_no
上面紅色字體表示不應該存在的!!!
問題現象:
在執行邏輯 SQL SELECT * FROM t_order o, t_order_item i WHERE o.order_no = i.order_no 時,ShardingSphere 將其路由為 8 條實際 SQL(如日志所示),分別在兩個數據源 (server_order0, server_order1) 上對所有物理表 (t_order0, t_order1, t_order_item0, t_order_item1) 的組合進行了笛卡爾積式的關聯查詢。
核心問題:無效查詢與資源浪費
這種全組合路由方式導致了嚴重的資源浪費。
根據已配置的分片策略規則:
- t_order0 表中的訂單,其關聯的訂單詳情必定存儲在 t_order_item0 表中。
- t_order1 表中的訂單,其關聯的訂單詳情必定存儲在 t_order_item1 表中。
這意味著:
-
有效關聯組合只有兩種:
-
t_order0
?JOIN?t_order_item0
-
t_order1
?JOIN?t_order_item1
-
-
無效關聯組合同樣有兩種:
-
t_order0
?JOIN?t_order_item1
(跨表后綴組合) -
t_order1
?JOIN?t_order_item0
(跨表后綴組合)
-
無效查詢分析:
在生成的8條實際SQL中:
每個數據源包含4條SQL(2種有效組合 + 2種無效組合)
兩個數據源共產生 4條無效SQL(每個數據源各2條)
具體無效SQL示例:
- server_order0 ::: select * from t_order0 o, t_order_item1 i where o.order_no = i.order_no
- server_order0 ::: select * from t_order1 o, t_order_item0 i where o.order_no = i.order_no
- server_order1 ::: select * from t_order0 o, t_order_item1 i where o.order_no = i.order_no
- server_order1 ::: select * from t_order1 o, t_order_item0 i where o.order_no = i.order_no
后果:
這4條無效SQL:
- 必然返回空結果集:因分片規則保證數據不會存在于跨后綴表中
- 消耗50%的查詢資源:在總共8條查詢中占一半比例
- 增加數據庫負載:需要執行全表掃描或索引查找
- 延長響應時間:尤其在大數據量場景下性能影響顯著
優化方向:
- 通過配置綁定表(Binding Table)關系,告知ShardingSphere:
- t_order 和 t_order_item 具有相同的分片規則
相同分片鍵值的數據必定落在相同后綴的物理表中
配置后,ShardingSphere將只路由有效組合:
- server_order0 ::: t_order0 JOIN t_order_item0
- server_order0 ::: t_order1 JOIN t_order_item1
- server_order1 ::: t_order0 JOIN t_order_item0
- server_order1 ::: t_order1 JOIN t_order_item1
從而將查詢數量從8條優化至4條,完全消除4條無效查詢,提升50%的執行效率。
2.9.3. 綁定表
什么是綁定表?
我們用學校儲物柜的例子,把「綁定表」講得像故事一樣清楚:
📦?想象一個超大校園(分布式數據庫)
-
學生(訂單表?
t_order
)?太多,學校建了?兩棟宿舍樓(分片庫?server_order0
,?server_order1
)?來住。 -
每棟樓里,又按?班級(分片表)?分了儲物柜:
-
t_order0
?表 → 代表?1班?的訂單儲物柜(放在兩棟樓里) -
t_order1
?表 → 代表?2班?的訂單儲物柜(放在兩棟樓里)
-
-
書本(訂單詳情?
t_order_item
)?也要存,同樣分柜子:-
t_order_item0
?→?1班?的書本柜 -
t_order_item1
?→?2班?的書本柜
-
關鍵規則📌:
1班學生的書本,只會放在1班書本柜;
2班學生的書本,只會放在2班書本柜。
(這就是分片策略:按班級(如user_id
)分片,保證同班數據在一起)
? 問題場景:查“張三的訂單和書本”
-
沒綁定表時:管理員懵了!
-
他跑到?宿舍樓A (
server_order0
),翻遍所有組合:-
1班訂單柜 + 1班書本柜 ? (可能有張三)
-
1班訂單柜 +?2班書本柜?? (白翻!1班書本不可能在2班柜)
-
2班訂單柜 +?1班書本柜?? (白翻!2班書本不可能在1班柜)
-
2班訂單柜 + 2班書本柜 ? (可能有張三)
-
-
接著又跑去?宿舍樓B (
server_order1
),同樣翻4遍柜子組合。 -
結果:總共翻了?8次柜子,其中?4次是白費力氣(翻錯班級組合)!
-
? 解決方案:告訴系統「班級柜子是一套的!」(綁定表)
綁定后的智能搜索🔍:
管理員現在懂了規則:
-
只查配套柜子!
-
找?1班?的張三 → 只看?1班訂單柜 + 1班書本柜(絕不去翻2班書本柜)
-
找?2班?的張三 → 只看?2班訂單柜 + 2班書本柜(絕不去翻1班書本柜)
-
-
每棟樓只查2次有效組合:
-
宿舍樓A:查?
(1班訂單柜 + 1班書本柜)
?+?(2班訂單柜 + 2班書本柜)
-
宿舍樓B:同樣查這兩組有效配套
-
-
結果:總共只翻?4次柜子,0次無效勞動!效率翻倍!
🌟 綁定表的核心作用
像配對鑰匙和鎖🔑:告訴數據庫系統哪些表是“一套”的(分片規則完全一致),查詢時自動跳過無效組合,直奔正確分片!
💡 你的日志發生了什么?
場景 | 執行的SQL數 | 有效查詢 | 無效查詢 |
---|---|---|---|
未綁定表 | 8條 | 4條 (同班級配套查詢) | 4條?(跨班級錯誤組合) |
綁定表后 | 4條 | 4條?(同班級配套) | 0條?(系統跳過無效組合) |
效果:資源消耗減半,查詢速度更快,數據庫再也不做“無用功”!
我們現在就來配置綁定表!!
注意這次我們 需要配置的文件是: conf/config-sharding.yaml,對應我們宿主機的目錄就是/bit/shardingsphere/proxy/conf/config-sharding.yaml。
首先我們先來到我們的/bit/shardingsphere/proxy/conf/,然后創建一個叫config-sharding.yaml的文件
cd /bit/shardingsphere/proxy/conf/
vim config-sharding.yaml
我們把下面的內容粘貼進去?
databaseName: bit_sharding_dbdataSources:server_user:url: jdbc:mysql://150.158.139.86:53310/user_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1server_order0:url: jdbc:mysql://150.158.139.86:63310/order_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1server_order1:url: jdbc:mysql://150.158.139.86:63311/order_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1rules:
- !SHARDINGtables:t_user: # 邏輯表名actualDataNodes: server_user.t_user # 由數據源名 + 表名組成t_order: # 邏輯表名actualDataNodes: server_order${0..1}.t_order${0..1}databaseStrategy: # 分庫策略standard: # 用于單分片鍵的標準分片場景shardingColumn: user_id # 分片列名稱shardingAlgorithmName: alg_mod # 分片算法名稱(自定義)——和下面的alg_mod對應tableStrategy: #分表策略standard:shardingColumn: order_no # 分片列名稱shardingAlgorithmName: alg_hash_mod # 分片算法名稱(自定義)——和下面的alg_hash_mod對應 keyGenerateStrategy: # 分布式序列策略column: id # 列名keyGeneratorName: alg_snowflake # 分布式序列算法名t_order_item: # 邏輯表名t_order_itemactualDataNodes: server_order${0..1}.t_order_item${0..1} # 真實數據節點databaseStrategy: # 分庫策略standard: # 用于單分片鍵的標準分片場景shardingColumn: user_id # 分片列名稱#shardingAlgorithmName: alg_db_inline_userid ?# 分片算法名稱shardingAlgorithmName: alg_mod # 分片算法名稱tableStrategy: # 分表策略standard:shardingColumn: order_no # 分片列名稱shardingAlgorithmName: alg_hash_mod # 分片算法名稱keyGenerateStrategy: # 分布式序列策略column: id # 列名keyGeneratorName: alg_snowflake # 分布式序列算法名# 綁定表規則bindingTables:- t_order,t_order_item# 分片算法配置shardingAlgorithms:alg_db_inline_userid: # 自定義的分片算法名稱_行表達式分片算法type: INLINE # 分片算法類型props:algorithm-expression: server_order${user_id % 2} # 分片算法,根據 user_id對2取模alg_mod: # 自定義算法名_取模分片算法type: MODprops:sharding-count: 2alg_hash_mod:type: HASH_MOD # 分片算法類型props:sharding-count: 2 # 分片數量,表示根據分片列對2取模# 分布式序列配置keyGenerators:alg_snowflake: # 分布式序列算法名type: SNOWFLAKE # 分布式序列類型(雪花算法)
注意我只是修改了下面這一處
?我們保存退出。重啟Docker容器
docker restart ss-proxy
docker ps
運行結果
root@VM-16-14-ubuntu:/bit/shardingsphere/proxy/conf# docker restart ss-proxy
ss-proxy
root@VM-16-14-ubuntu:/bit/shardingsphere/proxy/conf# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d9e2e351fb8a mysql:8.0.42 "docker-entrypoint.s…" 22 hours ago Up 22 hours 33060/tcp, 0.0.0.0:63311->3306/tcp, [::]:63311->3306/tcp server-order1
d2d5c33d7ca2 mysql:8.0.42 "docker-entrypoint.s…" 22 hours ago Up 22 hours 33060/tcp, 0.0.0.0:63310->3306/tcp, [::]:63310->3306/tcp server-order0
b5051d4517c4 mysql:8.0.42 "docker-entrypoint.s…" 29 hours ago Up 8 hours 33060/tcp, 0.0.0.0:53310->3306/tcp, [::]:53310->3306/tcp server-user
efc3b3c7dabc apache/shardingsphere-proxy:5.3.2 "/bin/sh -c '${LOCAL…" 47 hours ago Up 33 seconds 0.0.0.0:3307->3307/tcp, [::]:3307->3307/tcp ss-proxy
測試
??我們先去打開實時日志
tail -f /bit/shardingsphere/proxy/logs/shardingsphere.log
?然后我們去登陸進這個服務器來
mysql -h127.0.0.1 -P3307 -uroot -p
測試
- 執行查詢語句
-- 查詢所有訂單和詳情信息
select * from t_order o, t_order_item i where o.order_no = i.order_no;
查看日志,實際SQL使用了正確的數據節點
[INFO ] 2025-08-02 08:07:35.521 [ShardingSphere-Command-0] INFO ?ShardingSphere-SQL - Logic SQL: select * from t_order o, t_order_item i where o.order_no = i.order_no
[INFO ] 2025-08-02 08:07:35.521 [ShardingSphere-Command-0] INFO ?ShardingSphere-SQL - Actual SQL: server_order0 ::: select * from t_order0 o, t_order_item0 i where o.order_no = i.order_no
[INFO ] 2025-08-02 08:07:35.521 [ShardingSphere-Command-0] INFO ?ShardingSphere-SQL - Actual SQL: server_order0 ::: select * from t_order1 o, t_order_item1 i where o.order_no = i.order_no
[INFO ] 2025-08-02 08:07:35.521 [ShardingSphere-Command-0] INFO ?ShardingSphere-SQL - Actual SQL: server_order1 ::: select * from t_order0 o, t_order_item0 i where o.order_no = i.order_no
[INFO ] 2025-08-02 08:07:35.521 [ShardingSphere-Command-0] INFO ?ShardingSphere-SQL - Actual SQL: server_order1 ::: select * from t_order1 o, t_order_item1 i where o.order_no = i.order_no
現在我們就實現了
- t_order0 表中的訂單,其關聯的訂單詳情必定存儲在 t_order_item0 表中。
- t_order1 表中的訂單,其關聯的訂單詳情必定存儲在 t_order_item1 表中。
2.11 廣播表
2.11.1.什么是廣播表
在數據庫中有些表中存的數據一般不怎么改變,比如用于配置的表,也就是常說的字典表,這種類型的表在表關聯查詢時也會被經常使用,在分布式場景中,這類表在每個數據源中都保存一個復本,可以減少跨庫關聯提升查詢效率。
廣播表: 指所有分片數據源中都存在的表,表結構及表中的數據在每個數據庫中完全一致,適用于數據量不大且需要與海量數據表進行關聯的場景。
廣播表具有以下特性:
- 插入、更新操作會實時在所有數據節點上執行,保持各個分片的數據一致性
- 查詢時只從一個數據節點獲取數據
- 可以和任何一個表進行表關聯查詢
深入理解廣播表
我們用「連鎖便利店」的例子,把廣播表講得明明白白:
🏪?想象一個全國連鎖便利店(分布式數據庫)
-
你有上千家分店(分片數據庫),每家店都有:
-
本地銷售記錄表(分片表):記錄本店的訂單(比如?
北京店_訂單表
、上海店_訂單表
) -
本地商品庫存表(分片表):記錄本店的庫存(比如?
北京店_庫存表
、上海店_庫存表
)
-
-
但總有些東西是所有店統一的:
-
📖?《商品總目錄手冊》(這就是廣播表!)
-
包含所有商品信息:商品ID、名稱、分類、建議售價...
-
關鍵:每家店都有一本完全相同的《手冊》!
-
-
📣 為什么叫“廣播”表?
就像總部發廣播通知:
“所有分店注意!最新版《商品手冊》已發布,請立刻放在收銀臺!”
于是所有分店同時更新手冊,保持全國統一。
? 廣播表的三大特性(用便利店解釋)
1???數據全局一致:同款手冊,每家店都有
-
場景:上架新商品“螺螄粉月餅”(商品ID=100)
-
操作:總部更新《手冊》,自動同步到所有分店
-
結果:北京店、上海店... 所有店的《手冊》第100頁都是“螺螄粉月餅”
📌 這就是:插入/更新實時同步所有節點
2???查詢高效:隨便找家店問就行
-
場景:程序員想知道“商品ID=100是什么?”
-
操作:系統隨機選一家店(比如北京店),翻開它的《手冊》
-
結果:直接得到答案:“螺螄粉月餅”,無需聯系其他店
📌 這就是:查詢只需訪問一個節點
3???關聯查詢神器:和本地表無縫搭配
-
場景:查“北京店今天賣了哪些商品?顯示商品名和售價”
-
表關聯:
北京店_銷售記錄表
(只有商品ID)JOIN?《商品手冊》
(有ID和名稱) -
操作:
北京店員工自己就能搞定!-
翻本店銷售記錄 → “賣了商品ID=100,5盒”
-
翻本店的《手冊》 → “ID=100是螺螄粉月餅,售價¥99”
-
合并結果 →?“螺螄粉月餅,售價¥99,5盒”
-
-
優勢:不用打電話問總部,也不用查其他分店!
📌 這就是:可與任意表關聯,避免跨庫查詢
? 如果沒有廣播表會怎樣?
-
每次查商品名都要打電話問總部(跨網絡訪問中心庫)
-
總部電話被打爆(中心庫壓力大)
-
響應超慢(網絡延遲)
-
北京店無法獨立完成“銷售記錄+商品名”的查詢
🌟 哪些數據適合做廣播表?(便利店例子)
-
商品字典表(所有店共用商品信息)→ 📖 手冊
-
城市編碼表(北京=010,上海=021)→ 🏙? 行政區劃手冊
-
門店信息表(店長、地址、電話)→ 📞 分店通訊錄
-
支付方式表(現金、支付寶、微信)→ 💳 支付標識卡
??共同點:數據量小、改動少、所有業務都用到
?2.11.2?配置廣播表
?在所有數據源創建廣播表
在所有數據源 server-user,server-order0,server-order1 中創建廣播表
-- 創建?播表t_dict,不使??增主鍵,由應?程序傳?
create table if not exists t_dict (id bigint primary key,type varchar(30) comment '類型'
);
在server-user里面創建廣播表
?
在server-order0里面創建廣播表
?
在server-order1里面創建廣播表
?
修改配置文件,添加廣播表規則
?
# 規則配置
rules:
# 分片配置
!SHARDING
tables:
# 邏輯表配置
# ... 省略
# 綁定表規則
bindingTables:
# ... 省略
# 分片算法配置
shardingAlgorithms:
# ... 省略
# 分布式序列配置
keyGenerators:
# ... 省略
?注意這次我們 需要配置的文件是: conf/config-sharding.yaml,對應我們宿主機的目錄就是/bit/shardingsphere/proxy/conf/config-sharding.yaml。
首先我們先來到我們的/bit/shardingsphere/proxy/conf/,然后創建一個叫config-sharding.yaml的文件
cd /bit/shardingsphere/proxy/conf/
vim config-sharding.yaml
我們把下面的內容粘貼進去?
databaseName: bit_sharding_dbdataSources:server_user:url: jdbc:mysql://150.158.139.86:53310/user_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1server_order0:url: jdbc:mysql://150.158.139.86:63310/order_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1server_order1:url: jdbc:mysql://150.158.139.86:63311/order_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1rules:
- !SHARDINGtables:t_user: # 邏輯表名actualDataNodes: server_user.t_user # 由數據源名 + 表名組成t_order: # 邏輯表名actualDataNodes: server_order${0..1}.t_order${0..1}databaseStrategy: # 分庫策略standard: # 用于單分片鍵的標準分片場景shardingColumn: user_id # 分片列名稱shardingAlgorithmName: alg_mod # 分片算法名稱(自定義)——和下面的alg_mod對應tableStrategy: #分表策略standard:shardingColumn: order_no # 分片列名稱shardingAlgorithmName: alg_hash_mod # 分片算法名稱(自定義)——和下面的alg_hash_mod對應 keyGenerateStrategy: # 分布式序列策略column: id # 列名keyGeneratorName: alg_snowflake # 分布式序列算法名t_order_item: # 邏輯表名t_order_itemactualDataNodes: server_order${0..1}.t_order_item${0..1} # 真實數據節點databaseStrategy: # 分庫策略standard: # 用于單分片鍵的標準分片場景shardingColumn: user_id # 分片列名稱#shardingAlgorithmName: alg_db_inline_userid ?# 分片算法名稱shardingAlgorithmName: alg_mod # 分片算法名稱tableStrategy: # 分表策略standard:shardingColumn: order_no # 分片列名稱shardingAlgorithmName: alg_hash_mod # 分片算法名稱keyGenerateStrategy: # 分布式序列策略column: id # 列名keyGeneratorName: alg_snowflake # 分布式序列算法名# 綁定表規則bindingTables:- t_order,t_order_item# 分片算法配置shardingAlgorithms:alg_db_inline_userid: # 自定義的分片算法名稱_行表達式分片算法type: INLINE # 分片算法類型props:algorithm-expression: server_order${user_id % 2} # 分片算法,根據 user_id對2取模alg_mod: # 自定義算法名_取模分片算法type: MODprops:sharding-count: 2alg_hash_mod:type: HASH_MOD # 分片算法類型props:sharding-count: 2 # 分片數量,表示根據分片列對2取模# 分布式序列配置keyGenerators:alg_snowflake: # 分布式序列算法名type: SNOWFLAKE # 分布式序列類型(雪花算法)# 廣播表broadcastTables:- t_dict
注意我只是修改了下面這一處
?我們保存退出。重啟Docker容器
docker restart ss-proxy
docker ps
運行結果
root@VM-16-14-ubuntu:/bit/shardingsphere/proxy/conf# docker restart ss-proxy
ss-proxy
root@VM-16-14-ubuntu:/bit/shardingsphere/proxy/conf# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d9e2e351fb8a mysql:8.0.42 "docker-entrypoint.s…" 22 hours ago Up 22 hours 33060/tcp, 0.0.0.0:63311->3306/tcp, [::]:63311->3306/tcp server-order1
d2d5c33d7ca2 mysql:8.0.42 "docker-entrypoint.s…" 22 hours ago Up 22 hours 33060/tcp, 0.0.0.0:63310->3306/tcp, [::]:63310->3306/tcp server-order0
b5051d4517c4 mysql:8.0.42 "docker-entrypoint.s…" 29 hours ago Up 8 hours 33060/tcp, 0.0.0.0:53310->3306/tcp, [::]:53310->3306/tcp server-user
efc3b3c7dabc apache/shardingsphere-proxy:5.3.2 "/bin/sh -c '${LOCAL…" 47 hours ago Up 33 seconds 0.0.0.0:3307->3307/tcp, [::]:3307->3307/tcp ss-proxy
測試
??我們先去打開實時日志
tail -f /bit/shardingsphere/proxy/logs/shardingsphere.log
?然后我們去登陸進這個服務器來
mysql -h127.0.0.1 -P3307 -uroot -p
測試
插入一條記錄,主鍵由應用程序傳入
-- 向廣播表中寫入記錄
insert into t_dict (id, type) values (1, 'ADMIN');
查看日志,實際SQL使用了正確的數據節點
邏輯SQL
[INFO ] 2025-08-02 09:55:37.848 [ShardingSphere-Command-0] INFO ?ShardingSphere-SQL - Logic SQL: insert into t_dict (id, type) values (1, 'ADMIN')
實際執行SQL
[INFO ] 2025-08-02 09:55:37.848 [ShardingSphere-Command-0] INFO ?ShardingSphere-SQL - Actual SQL: server_user ::: insert into t_dict (id, type) values (1, 'ADMIN')
[INFO ] 2025-08-02 09:55:37.848 [ShardingSphere-Command-0] INFO ?ShardingSphere-SQL - Actual SQL: server_order0 ::: insert into t_dict (id, type) values (1, 'ADMIN')
[INFO ] 2025-08-02 09:55:37.848 [ShardingSphere-Command-0] INFO ?ShardingSphere-SQL - Actual SQL: server_order1 ::: insert into t_dict (id, type) values (1, 'ADMIN')
由此可見ShardinSphere向所有數據源中都寫入了記錄。
查詢測試
select * from t_dict;
我們多次執行上面那個SQL語句,然后我們查看日志
發現就會出現下面這種情況?
從server_order0節點獲取數據
[INFO ] 2025-08-02 09:57:33.481 [ShardingSphere-Command-1] INFO ?ShardingSphere-SQL - Logic SQL: select * from t_dict
[INFO ] 2025-08-02 09:57:33.482 [ShardingSphere-Command-1] INFO ?ShardingSphere-SQL - Actual SQL: server_order0 ::: select * from t_dict
從server_order1節點獲取數據
[INFO ] 2025-08-02 09:57:34.272 [ShardingSphere-Command-1] INFO ?ShardingSphere-SQL - Logic SQL: select * from t_dict
[INFO ] 2025-08-02 09:57:34.272 [ShardingSphere-Command-1] INFO ?ShardingSphere-SQL - Actual SQL: server_order1 ::: select * from t_dict
從server_user節點獲取數據
[INFO ] 2025-08-02 09:58:32.256 [ShardingSphere-Command-1] INFO ?ShardingSphere-SQL - Logic SQL: select * from t_dict
[INFO ] 2025-08-02 09:58:32.256 [ShardingSphere-Command-1] INFO ?ShardingSphere-SQL - Actual SQL: server_user ::: select * from t_dict
可以看到啊,獲取數據的時候是隨機從幾個數據節點獲取數據的
?
至此,水平分片就算是講完了,內容很多,請大家耐心閱讀。