文章目錄

  • @[toc]
  • 第01章 高性能架構模式
  • 1、讀寫分離架構
  • 2、數據庫分片架構
  • 2.1、垂直分片
  • 2.2、水平分片
  • 3、讀寫分離和數據分片架構
  • 4、實現方式
  • 4.1、程序代碼封裝
  • 4.2、中間件封裝
  • 4.3、常用解決方案
  • 第02章 ShardingSphere
  • 1、簡介
  • 2、ShardingSphere-JDBC
  • 3、ShardingSphere-Proxy
  • 第03章 MySQL主從同步
  • 1、MySQL主從同步原理
  • 2、一主多從配置
  • 2.1、準備主服務器
  • 2.2、準備從服務器
  • 2.3、啓動主從同步
  • 2.4、實現主從同步
  • 2.5、停止和重置
  • **2.6、常見問題**
  • 問題1
  • 問題2
  • 第04章 ShardingSphere-JDBC讀寫分離
  • 1、創建SpringBoot程序
  • 1.1、創建項目
  • 1.2、添加依賴
  • 1.3、創建實體類
  • 1.4、創建Mapper
  • 1.5、配置讀寫分離
  • 2、測試
  • 2.1、讀寫分離測試
  • 2.2、事務測試
  • 2.3、負載均衡測試
  • 第05章 ShardingSphere-JDBC垂直分片
  • 1、準備服務器
  • 1.1、創建server-user容器
  • 1.2、創建server-order容器
  • 2、程序實現
  • 2.1、創建實體類
  • 2.2、創建Mapper
  • 2.3、配置垂直分片
  • 3、測試垂直分片
  • 常見錯誤
  • 第06章 ShardingSphere-JDBC水平分片
  • 1、準備服務器
  • 1.1、創建server-order0容器
  • 1.2、創建server-order1容器
  • 2、基本水平分片
  • 2.1、基本配置
  • 2.2、數據源配置
  • 2.3、標椎分片表配置
  • 2.4、行表達式
  • 2.5、分片算法配置
  • 2.6、分佈式序列算法
  • 3、多表關聯
  • 3.1、創建關聯表
  • 3.2、創建實體類
  • 3.3、創建Mapper
  • 3.4、配置關聯表
  • 3.5、測試插入數據
  • 4、綁定表
  • 4.1、創建VO對象
  • 4.2、添加Mapper方法
  • 4.3、測試關聯查詢
  • 4.4、配置綁定表
  • 5、廣播表
  • 4.1、什麼是廣播表
  • 4.2、創建廣播表
  • 4.3、程序實現
  • 4.3.1、創建實體類
  • 4.3.2、創建Mapper
  • 4.3.3、配置廣播表
  • 4.4、測試廣播表
  • 第07章 啓動ShardingSphere-Proxy
  • 1、獲取
  • 2、使用二進制發佈包安裝
  • 3、使用Docker安裝
  • 第08章 ShardingSphere-Proxy讀寫分離
  • 1、修改配置文件
  • 2、實時查看日誌
  • 3、遠程訪問測試
  • 4、應用程序訪問Proxy
  • 4.1、創建項目
  • 4.2、添加依賴
  • 4.3、創建實體類
  • 4.4、創建Mapper
  • 4.5、配置數據源
  • 4.6、測試
  • 第09章 ShardingSphere-Proxy垂直分片
  • 1、修改配置文件
  • 2、實時查看日誌
  • 3、遠程訪問測試
  • 第10章 ShardingSphere-Proxy水平分片
  • 1、修改配置文件
  • 2、實時查看日誌
  • 3、遠程訪問測試

第01章 高性能架構模式

互聯網業務興起之後,海量用户加上海量數據的特點,單個數據庫服務器已經難以滿足業務需要,必須考慮數據庫集羣的方式來提升性能。高性能數據庫集羣的第一種方式是“讀寫分離”第二種方式是“數據庫分片”

1、讀寫分離架構

**讀寫分離原理:**讀寫分離的基本原理是將數據庫讀寫操作分散到不同的節點上,下面是其基本架構圖:

postgresql shardingsphere 分庫分表_#java

讀寫分離的基本實現:

  • 主庫負責處理事務性的增刪改操作,從庫負責處理查詢操作,能夠有效的避免由數據更新導致的行鎖,使得整個系統的查詢性能得到極大的改善。
  • 讀寫分離是根據 SQL 語義的分析將讀操作和寫操作分別路由至主庫與從庫
  • 通過一主多從的配置方式,可以將查詢請求均勻的分散到多個數據副本,能夠進一步的提升系統的處理能力。
  • 使用多主多從的方式,不但能夠提升系統的吞吐量,還能夠提升系統的可用性,可以達到在任何一個數據庫宕機,甚至磁盤物理損壞的情況下仍然不影響系統的正常運行。

下圖展示了根據業務需要,將用户表的寫操作和讀操路由到不同的數據庫的方案:

postgresql shardingsphere 分庫分表_數據_02

CAP 理論:

CAP 定理(CAP theorem)又被稱作布魯爾定理(Brewer’s theorem),是加州大學伯克利分校的計算機科學家埃裏克·布魯爾(Eric Brewer)在 2000 年的 ACM PODC 上提出的一個猜想。對於設計分佈式系統的架構師來説,CAP 是必須掌握的理論。

在一個分佈式系統中,當涉及讀寫操作時,只能保證一致性(Consistence)、可用性(Availability)、分區容錯性(Partition Tolerance)三者中的兩個,另外一個必須被犧牲。

  • C 一致性(Consistency):對某個指定的客户端來説,讀操作保證能夠返回最新的寫操作結果
  • A 可用性(Availability):非故障的節點在合理的時間內返回合理的響應(不是錯誤和超時的響應)
  • P 分區容忍性(Partition Tolerance):當出現網絡分區後(可能是丟包,也可能是連接中斷,還可能是擁塞),系統能夠繼續“履行職責”

CAP特點:

  • 在實際設計過程中,每個系統不可能只處理一種數據,而是包含多種類型的數據,有的數據必須選擇 CP,有的數據必須選擇 AP,分佈式系統理論上不可能選擇 CA 架構。
  • CP:如下圖所示,為了保證一致性,當發生分區現象後,N1 節點上的數據已經更新到 y,但由於 N1 和 N2 之間的複製通道中斷,數據 y 無法同步到 N2,N2 節點上的數據還是 x。這時客户端 C 訪問 N2 時,N2 需要返回 Error,提示客户端 C“系統現在發生了錯誤”,這種處理方式違背了可用性(Availability)的要求,因此 CAP 三者只能滿足 CP。

postgresql shardingsphere 分庫分表_#學習_03

  • AP:如下圖所示,為了保證可用性,當發生分區現象後,N1 節點上的數據已經更新到 y,但由於 N1 和 N2 之間的複製通道中斷,數據 y 無法同步到 N2,N2 節點上的數據還是 x。這時客户端 C 訪問 N2 時,N2 將當前自己擁有的數據 x 返回給客户端 C 了,而實際上當前最新的數據已經是 y 了,這就不滿足一致性(Consistency)的要求了,因此 CAP 三者只能滿足 AP。注意:這裏 N2 節點返回 x,雖然不是一個“正確”的結果,但是一個“合理”的結果,因為 x 是舊的數據,並不是一個錯亂的值,只是不是最新的數據而已。

postgresql shardingsphere 分庫分表_數據_04

  • CAP 理論中的 C 在實踐中是不可能完美實現的,在數據複製的過程中,節點N1 和節點 N2 的數據並不一致(強一致性)。即使無法做到強一致性,但應用可以採用適合的方式達到最終一致性。具有如下特點:
  • 基本可用(Basically Available):分佈式系統在出現故障時,允許損失部分可用性,即保證核心可用。
  • 軟狀態(Soft State):允許系統存在中間狀態,而該中間狀態不會影響系統整體可用性。這裏的中間狀態就是 CAP 理論中的數據不一致。
  • 最終一致性(Eventual Consistency):系統中的所有數據副本經過一定時間後,最終能夠達到一致的狀態。

2、數據庫分片架構

讀寫分離的問題:

讀寫分離分散了數據庫讀寫操作的壓力,但沒有分散存儲壓力,為了滿足業務數據存儲的需求,就需要將存儲分散到多台數據庫服務器上

數據分片:

將存放在單一數據庫中的數據分散地存放至多個數據庫或表中,以達到提升性能瓶頸以及可用性的效果。 數據分片的有效手段是對關係型數據庫進行分庫和分表。數據分片的拆分方式又分為垂直分片和水平分片

2.1、垂直分片

垂直分庫:

按照業務拆分的方式稱為垂直分片,又稱為縱向拆分,它的核心理念是專庫專用。 在拆分之前,一個數據庫由多個數據表構成,每個表對應着不同的業務。而拆分之後,則是按照業務將表進行歸類,分佈到不同的數據庫中,從而將壓力分散至不同的數據庫。

postgresql shardingsphere 分庫分表_#java_05

下圖展示了根據業務需要,將用户表和訂單表垂直分片到不同的數據庫的方案:

postgresql shardingsphere 分庫分表_#java_06

垂直拆分可以緩解數據量和訪問量帶來的問題,但無法根治。如果垂直拆分之後,表中的數據量依然超過單節點所能承載的閾值,則需要水平分片來進一步處理。

垂直分表:

垂直分表適合將表中某些不常用的列,或者是佔了大量空間的列拆分出去。

假設我們是一個婚戀網站,用户在篩選其他用户的時候,主要是用 age 和 sex 兩個字段進行查詢,而 nickname 和 description 兩個字段主要用於展示,一般不會在業務查詢中用到。description 本身又比較長,因此我們可以將這兩個字段獨立到另外一張表中,這樣在查詢 age 和 sex 時,就能帶來一定的性能提升。

垂直分表引入的複雜性主要體現在表操作的數量要增加。例如,原來只要一次查詢就可以獲取 name、age、sex、nickname、description,現在需要兩次查詢,一次查詢獲取 name、age、sex,另外一次查詢獲取 nickname、description。

postgresql shardingsphere 分庫分表_數據_07

水平分表適合錶行數特別大的表,水平分表屬於水平分片

2.2、水平分片

水平分片又稱為橫向拆分。 相對於垂直分片,它不再將數據根據業務邏輯分類,而是通過某個字段(或某幾個字段),根據某種規則將數據分散至多個庫或表中,每個分片僅包含數據的一部分。 例如:根據主鍵分片,偶數主鍵的記錄放入 0 庫(或表),奇數主鍵的記錄放入 1 庫(或表),如下圖所示。

postgresql shardingsphere 分庫分表_數據_08

單表進行切分後,是否將多個表分散在不同的數據庫服務器中,可以根據實際的切分效果來確定。

  • **水平分表:**單表切分為多表後,新的表即使在同一個數據庫服務器中,也可能帶來可觀的性能提升,如果性能能夠滿足業務要求,可以不拆分到多台數據庫服務器,畢竟業務分庫也會引入很多複雜性;
  • **水平分庫:**如果單表拆分為多表後,單台服務器依然無法滿足性能要求,那就需要將多個表分散在不同的數據庫服務器中。

阿里巴巴Java開發手冊:

【推薦】單錶行數超過 500 萬行或者單表容量超過 2GB,才推薦進行分庫分表。

説明:如果預計三年後的數據量根本達不到這個級別,請不要在創建表時就分庫分表

3、讀寫分離和數據分片架構

下圖展現了將數據分片與讀寫分離一同使用時,應用程序與數據庫集羣之間的複雜拓撲關係。

postgresql shardingsphere 分庫分表_數據_09

4、實現方式

讀寫分離和數據分片具體的實現方式一般有兩種: 程序代碼封裝中間件封裝

4.1、程序代碼封裝

程序代碼封裝指在代碼中抽象一個數據訪問層(或中間層封裝),實現讀寫操作分離和數據庫服務器連接的管理。

**其基本架構是:**以讀寫分離為例

postgresql shardingsphere 分庫分表_#學習_10

4.2、中間件封裝

中間件封裝指的是獨立一套系統出來,實現讀寫操作分離和數據庫服務器連接的管理。對於業務服務器來説,訪問中間件和訪問數據庫沒有區別,在業務服務器看來,中間件就是一個數據庫服務器。

**基本架構是:**以讀寫分離為例

postgresql shardingsphere 分庫分表_#java_11

4.3、常用解決方案

Apache ShardingSphere(程序級別和中間件級別)

MyCat(數據庫中間件)

第02章 ShardingSphere

1、簡介

官網:https://shardingsphere.apache.org/index_zh.html

文檔:https://shardingsphere.apache.org/document/5.1.1/cn/overview/

Apache ShardingSphere 由 JDBC、Proxy 和 Sidecar(規劃中)這 3 款既能夠獨立部署,又支持混合部署配合使用的產品組成。

2、ShardingSphere-JDBC

程序代碼封裝

定位為輕量級 Java 框架,在 Java 的 JDBC 層提供的額外服務。 它使用客户端直連數據庫,以 jar 包形式提供服務,無需額外部署和依賴,可理解為增強版的 JDBC 驅動,完全兼容 JDBC 和各種 ORM 框架。

postgresql shardingsphere 分庫分表_#學習_12

3、ShardingSphere-Proxy

中間件封裝

定位為透明化的數據庫代理端,提供封裝了數據庫二進制協議的服務端版本,用於完成對異構語言的支持。 目前提供 MySQL 和 PostgreSQL版本,它可以使用任何兼容 MySQL/PostgreSQL 協議的訪問客户端(如:MySQL Command Client, MySQL Workbench, Navicat 等)操作數據,對 DBA 更加友好。

postgresql shardingsphere 分庫分表_mysql_13

第03章 MySQL主從同步

1、MySQL主從同步原理

postgresql shardingsphere 分庫分表_#數據庫_14

基本原理:

slave會從master讀取binlog來進行數據同步

具體步驟:

  • step1:master將數據改變記錄到二進制日誌(binary log)中。
  • step2: 當slave上執行 start slave 命令之後,slave會創建一個 IO 線程用來連接master,請求master中的binlog。
  • step3:當slave連接master時,master會創建一個 log dump 線程,用於發送 binlog 的內容。在讀取 binlog 的內容的操作中,會對主節點上的 binlog 加鎖,當讀取完成併發送給從服務器後解鎖。
  • step4:IO 線程接收主節點 binlog dump 進程發來的更新之後,保存到 中繼日誌(relay log) 中。
  • step5:slave的SQL線程,讀取relay log日誌,並解析成具體操作,從而實現主從操作一致,最終數據一致。

2、一主多從配置

服務器規劃:使用docker方式創建,主從服務器IP一致,端口號不一致

postgresql shardingsphere 分庫分表_#學習_15

  • 主服務器:容器名atguigu-mysql-master,端口3306
  • 從服務器:容器名atguigu-mysql-slave1,端口3307
  • 從服務器:容器名atguigu-mysql-slave2,端口3308

**注意:**如果此時防火牆是開啓的,則先關閉防火牆,並重啓docker,否則後續安裝的MySQL無法啓動

#關閉docker
systemctl stop docker
#關閉防火牆
systemctl stop firewalld
#啓動docker
systemctl start docker

2.1、準備主服務器

  • step1:在docker中創建並啓動MySQL主服務器:端口3306
docker run -d \
-p 3306:3306 \
-v /atguigu/mysql/master/conf:/etc/mysql/conf.d \
-v /atguigu/mysql/master/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--name atguigu-mysql-master \
mysql:8.0.29
  • step2:創建MySQL主服務器配置文件:

默認情況下MySQL的binlog日誌是自動開啓的,可以通過如下配置定義一些可選配置

vim /atguigu/mysql/master/conf/my.cnf

配置如下內容

[mysqld]
# 服務器唯一id,默認值1
server-id=1
# 設置日誌格式,默認值ROW
binlog_format=STATEMENT
# 二進制日誌名,默認binlog
# log-bin=binlog
# 設置需要複製的數據庫,默認複製全部數據庫
#binlog-do-db=mytestdb
# 設置不需要複製的數據庫
#binlog-ignore-db=mysql
#binlog-ignore-db=infomation_schema

重啓MySQL容器

docker restart atguigu-mysql-master

binlog格式説明:

  • binlog_format=STATEMENT:日誌記錄的是主機數據庫的寫指令,性能高,但是now()之類的函數以及獲取系統參數的操作會出現主從數據不同步的問題。
  • binlog_format=ROW(默認):日誌記錄的是主機數據庫的寫後的數據,批量操作時性能較差,解決now()或者 user()或者 @@hostname 等操作在主從機器上不一致的問題。
  • binlog_format=MIXED:是以上兩種level的混合使用,有函數用ROW,沒函數用STATEMENT,但是無法識別系統變量

binlog-ignore-db和binlog-do-db的優先級問題:

postgresql shardingsphere 分庫分表_#學習_16

  • step3:使用命令行登錄MySQL主服務器:
#進入容器:env LANG=C.UTF-8 避免容器中顯示中文亂碼
docker exec -it atguigu-mysql-master env LANG=C.UTF-8 /bin/bash
#進入容器內的mysql命令行
mysql -uroot -p
#修改默認密碼校驗方式
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
  • step4:主機中創建slave用户:
-- 創建slave用户
CREATE USER 'atguigu_slave'@'%';
-- 設置密碼
ALTER USER 'atguigu_slave'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
-- 授予複製權限
GRANT REPLICATION SLAVE ON *.* TO 'atguigu_slave'@'%';
-- 刷新權限
FLUSH PRIVILEGES;
  • step5:主機中查詢master狀態:

執行完此步驟後不要再操作主服務器MYSQL,防止主服務器狀態值變化

SHOW MASTER STATUS;

記下FilePosition的值。執行完此步驟後不要再操作主服務器MYSQL,防止主服務器狀態值變化。

postgresql shardingsphere 分庫分表_#java_17

2.2、準備從服務器

可以配置多台從機slave1、slave2…,這裏以配置slave1為例

  • step1:在docker中創建並啓動MySQL從服務器:端口3307
docker run -d \
-p 3307:3306 \
-v /atguigu/mysql/slave1/conf:/etc/mysql/conf.d \
-v /atguigu/mysql/slave1/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--name atguigu-mysql-slave1 \
mysql:8.0.29
  • step2:創建MySQL從服務器配置文件:
vim /atguigu/mysql/slave1/conf/my.cnf

配置如下內容:

[mysqld]
# 服務器唯一id,每台服務器的id必須不同,如果配置其他從機,注意修改id
server-id=2
# 中繼日誌名,默認xxxxxxxxxxxx-relay-bin
#relay-log=relay-bin

重啓MySQL容器

docker restart atguigu-mysql-slave1
  • step3:使用命令行登錄MySQL從服務器:
#進入容器:
docker exec -it atguigu-mysql-slave1 env LANG=C.UTF-8 /bin/bash
#進入容器內的mysql命令行
mysql -uroot -p
#修改默認密碼校驗方式
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
  • step4:在從機上配置主從關係:

從機上執行以下SQL操作

CHANGE MASTER TO MASTER_HOST='192.168.100.201', 
MASTER_USER='atguigu_slave',MASTER_PASSWORD='123456', MASTER_PORT=3306,
MASTER_LOG_FILE='binlog.000003',MASTER_LOG_POS=1357;

2.3、啓動主從同步

啓動從機的複製功能,執行SQL:

START SLAVE;
-- 查看狀態(不需要分號)
SHOW SLAVE STATUS\G

**兩個關鍵進程:**下面兩個參數都是Yes,則説明主從配置成功!

postgresql shardingsphere 分庫分表_#java_18

2.4、實現主從同步

在主機中執行以下SQL,在從機中查看數據庫、表和數據是否已經被同步

CREATE DATABASE db_user;
USE db_user;
CREATE TABLE t_user (
 id BIGINT AUTO_INCREMENT,
 uname VARCHAR(30),
 PRIMARY KEY (id)
);
INSERT INTO t_user(uname) VALUES('zhang3');
INSERT INTO t_user(uname) VALUES(@@hostname);

2.5、停止和重置

需要的時候,可以使用如下SQL語句

-- 在從機上執行。功能説明:停止I/O 線程和SQL線程的操作。
stop slave; 

-- 在從機上執行。功能説明:用於刪除SLAVE數據庫的relaylog日誌文件,並重新啓用新的relaylog文件。
reset slave;

-- 在主機上執行。功能説明:刪除所有的binglog日誌文件,並將日誌索引文件清空,重新開始所有新的日誌文件。
-- 用於第一次進行搭建主從庫時,進行主庫binlog初始化工作;
reset master;

2.6、常見問題

問題1

啓動主從同步後,常見錯誤是Slave_IO_Running: No 或者 Connecting 的情況,此時查看下方的 Last_IO_ERROR錯誤日誌,根據日誌中顯示的錯誤信息在網上搜索解決方案即可

postgresql shardingsphere 分庫分表_#數據庫_19

典型的錯誤例如:Last_IO_Error: Got fatal error 1236 from master when reading data from binary log: 'Client requested master to start replication from position > file size'

解決方案:

-- 在從機停止slave
SLAVE STOP;

-- 在主機查看mater狀態
SHOW MASTER STATUS;
-- 在主機刷新日誌
FLUSH LOGS;
-- 再次在主機查看mater狀態(會發現File和Position發生了變化)
SHOW MASTER STATUS;
-- 修改從機連接主機的SQL,並重新連接即可
問題2

啓動docker容器後提示 WARNING: IPv4 forwarding is disabled. Networking will not work.

postgresql shardingsphere 分庫分表_#java_20

此錯誤,雖然不影響主從同步的搭建,但是如果想從遠程客户端通過以下方式連接docker中的MySQL則沒法連接

C:\Users\administrator>mysql -h 192.168.100.201 -P 3306 -u root -p

解決方案:

#修改配置文件:
vim /usr/lib/sysctl.d/00-system.conf
#追加
net.ipv4.ip_forward=1
#接着重啓網絡
systemctl restart network

第04章 ShardingSphere-JDBC讀寫分離

1、創建SpringBoot程序

1.1、創建項目

項目類型:Spring Initializr

SpringBoot腳手架:http://start.aliyun.com

項目名:sharding-jdbc-demo

SpringBoot版本:2.3.7.RELEASE

1.2、添加依賴

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.apache.shardingsphere</groupId>
        <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
        <version>5.1.1</version>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>

    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.3.1</version>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

1.3、創建實體類

package com.atguigu.shardingjdbcdemo.entity;

@TableName("t_user")
@Data
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String uname;
}

1.4、創建Mapper

package com.atguigu.shardingjdbcdemo.mapper;

@Mapper
public interface UserMapper extends BaseMapper<User> {
}

1.5、配置讀寫分離

application.properties:

# 應用名稱
spring.application.name=sharging-jdbc-demo
# 開發環境設置
spring.profiles.active=dev
# 內存模式
spring.shardingsphere.mode.type=Memory

# 配置真實數據源
spring.shardingsphere.datasource.names=master,slave1,slave2

# 配置第 1 個數據源
spring.shardingsphere.datasource.master.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.master.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.master.jdbc-url=jdbc:mysql://192.168.100.201:3306/db_user
spring.shardingsphere.datasource.master.username=root
spring.shardingsphere.datasource.master.password=123456

# 配置第 2 個數據源
spring.shardingsphere.datasource.slave1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.slave1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.slave1.jdbc-url=jdbc:mysql://192.168.100.201:3307/db_user
spring.shardingsphere.datasource.slave1.username=root
spring.shardingsphere.datasource.slave1.password=123456

# 配置第 3 個數據源
spring.shardingsphere.datasource.slave2.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.slave2.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.slave2.jdbc-url=jdbc:mysql://192.168.100.201:3308/db_user
spring.shardingsphere.datasource.slave2.username=root
spring.shardingsphere.datasource.slave2.password=123456

# 讀寫分離類型,如: Static,Dynamic
spring.shardingsphere.rules.readwrite-splitting.data-sources.myds.type=Static
# 寫數據源名稱
spring.shardingsphere.rules.readwrite-splitting.data-sources.myds.props.write-data-source-name=master
# 讀數據源名稱,多個從數據源用逗號分隔
spring.shardingsphere.rules.readwrite-splitting.data-sources.myds.props.read-data-source-names=slave1,slave2

# 負載均衡算法名稱
spring.shardingsphere.rules.readwrite-splitting.data-sources.myds.load-balancer-name=alg_round

# 負載均衡算法配置
# 負載均衡算法類型
spring.shardingsphere.rules.readwrite-splitting.load-balancers.alg_round.type=ROUND_ROBIN
spring.shardingsphere.rules.readwrite-splitting.load-balancers.alg_random.type=RANDOM
spring.shardingsphere.rules.readwrite-splitting.load-balancers.alg_weight.type=WEIGHT
spring.shardingsphere.rules.readwrite-splitting.load-balancers.alg_weight.props.slave1=1
spring.shardingsphere.rules.readwrite-splitting.load-balancers.alg_weight.props.slave2=2

# 打印SQl
spring.shardingsphere.props.sql-show=true

2、測試

2.1、讀寫分離測試

package com.atguigu.shardingjdbcdemo;

@SpringBootTest
class ReadwriteTest {

    @Autowired
    private UserMapper userMapper;

    /**
     * 寫入數據的測試
     */
    @Test
    public void testInsert(){

        User user = new User();
        user.setUname("張三丰");
        userMapper.insert(user);
    }

}

2.2、事務測試

為了保證主從庫間的事務一致性,避免跨服務的分佈式事務,ShardingSphere-JDBC的主從模型中,事務中的數據讀寫均用主庫

  • 不添加@Transactional:insert對主庫操作,select對從庫操作
  • 添加@Transactional:則insert和select均對主庫操作
  • **注意:**在JUnit環境下的@Transactional註解,默認情況下就會對事務進行回滾(即使在沒加註解@Rollback,也會對事務回滾)
/**
     * 事務測試
     */
@Transactional//開啓事務
@Test
public void testTrans(){

    User user = new User();
    user.setUname("鐵錘");
    userMapper.insert(user);

    List<User> users = userMapper.selectList(null);
}

2.3、負載均衡測試

/**
     * 讀數據測試
     */
@Test
public void testSelectAll(){
    List<User> users = userMapper.selectList(null);
    List<User> users = userMapper.selectList(null);//執行第二次測試負載均衡
    users.forEach(System.out::println);
}

也可以在web請求中測試負載均衡

package com.atguigu.shardingjdbcdemo.controller;

@RestController
@RequestMapping("/userController")
public class UserController {

    @Autowired
    private UserMapper userMapper;

    /**
     * 測試負載均衡策略
     */
    @GetMapping("selectAll")
    public void selectAll(){
        List<User> users = userMapper.selectList(null);
        users.forEach(System.out::println);
    }
}

第05章 ShardingSphere-JDBC垂直分片

1、準備服務器

服務器規劃:使用docker方式創建如下容器

postgresql shardingsphere 分庫分表_#數據庫_21

  • 服務器:容器名server-user,端口3301
  • 服務器:容器名server-order,端口3302

1.1、創建server-user容器

  • step1:創建容器:
docker run -d \
-p 3301:3306 \
-v /atguigu/server/user/conf:/etc/mysql/conf.d \
-v /atguigu/server/user/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--name server-user \
mysql:8.0.29
  • step2:登錄MySQL服務器:
#進入容器:
docker exec -it server-user env LANG=C.UTF-8 /bin/bash
#進入容器內的mysql命令行
mysql -uroot -p
#修改默認密碼插件
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
  • step3:創建數據庫:
CREATE DATABASE db_user;
USE db_user;
CREATE TABLE t_user (
 id BIGINT AUTO_INCREMENT,
 uname VARCHAR(30),
 PRIMARY KEY (id)
);

1.2、創建server-order容器

  • step1:創建容器:
docker run -d \
-p 3302:3306 \
-v /atguigu/server/order/conf:/etc/mysql/conf.d \
-v /atguigu/server/order/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--name server-order \
mysql:8.0.29
  • step2:登錄MySQL服務器:
#進入容器:
docker exec -it server-order env LANG=C.UTF-8 /bin/bash
#進入容器內的mysql命令行
mysql -uroot -p
#修改默認密碼插件
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
  • step3:創建數據庫:
CREATE DATABASE db_order;
USE db_order;
CREATE TABLE t_order (
  id BIGINT AUTO_INCREMENT,
  order_no VARCHAR(30),
  user_id BIGINT,
  amount DECIMAL(10,2),
  PRIMARY KEY(id) 
);

2、程序實現

2.1、創建實體類

package com.atguigu.shardingjdbcdemo.entity;

@TableName("t_order")
@Data
public class Order {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String orderNo;
    private Long userId;
    private BigDecimal amount;
}

2.2、創建Mapper

package com.atguigu.shardingjdbcdemo.mapper;

@Mapper
public interface OrderMapper extends BaseMapper<Order> {
}

2.3、配置垂直分片

# 應用名稱
spring.application.name=sharding-jdbc-demo
# 環境設置
spring.profiles.active=dev

# 配置真實數據源
spring.shardingsphere.datasource.names=server-user,server-order

# 配置第 1 個數據源
spring.shardingsphere.datasource.server-user.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.server-user.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.server-user.jdbc-url=jdbc:mysql://192.168.100.201:3301/db_user
spring.shardingsphere.datasource.server-user.username=root
spring.shardingsphere.datasource.server-user.password=123456

# 配置第 2 個數據源
spring.shardingsphere.datasource.server-order.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.server-order.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.server-order.jdbc-url=jdbc:mysql://192.168.100.201:3302/db_order
spring.shardingsphere.datasource.server-order.username=root
spring.shardingsphere.datasource.server-order.password=123456

# 標準分片表配置(數據節點)
# spring.shardingsphere.rules.sharding.tables.<table-name>.actual-data-nodes=值
# 值由數據源名 + 表名組成,以小數點分隔。
# <table-name>:邏輯表名
spring.shardingsphere.rules.sharding.tables.t_user.actual-data-nodes=server-user.t_user
spring.shardingsphere.rules.sharding.tables.t_order.actual-data-nodes=server-order.t_order


# 打印SQL
spring.shardingsphere.props.sql-show=true

3、測試垂直分片

package com.atguigu.shardingjdbcdemo;

@SpringBootTest
public class ShardingTest {


    @Autowired
    private UserMapper userMapper;

    @Autowired
    private OrderMapper orderMapper;

    /**
     * 垂直分片:插入數據測試
     */
    @Test
    public void testInsertOrderAndUser(){

        User user = new User();
        user.setUname("強哥");
        userMapper.insert(user);

        Order order = new Order();
        order.setOrderNo("ATGUIGU001");
        order.setUserId(user.getId());
        order.setAmount(new BigDecimal(100));
        orderMapper.insert(order);

    }

    /**
     * 垂直分片:查詢數據測試
     */
    @Test
    public void testSelectFromOrderAndUser(){
        User user = userMapper.selectById(1L);
        Order order = orderMapper.selectById(1L);
    }
}

常見錯誤

postgresql shardingsphere 分庫分表_#數據庫_22

ShardingSphere-JDBC遠程連接的方式默認的密碼加密規則是:mysql_native_password

因此需要在服務器端修改服務器的密碼加密規則,如下:

ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';

第06章 ShardingSphere-JDBC水平分片

1、準備服務器

服務器規劃:使用docker方式創建如下容器

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-BhYWqn9j-1667321256741)(assets/image-20220808033239206.png)]

  • 服務器:容器名server-order0,端口3310
  • 服務器:容器名server-order1,端口3311

1.1、創建server-order0容器

  • step1:創建容器:
docker run -d \
-p 3310:3306 \
-v /atguigu/server/order0/conf:/etc/mysql/conf.d \
-v /atguigu/server/order0/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--name server-order0 \
mysql:8.0.29
  • step2:登錄MySQL服務器:
#進入容器:
docker exec -it server-order0 env LANG=C.UTF-8 /bin/bash
#進入容器內的mysql命令行
mysql -uroot -p
#修改默認密碼插件
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
  • step3:創建數據庫:

注意:水平分片的id需要在業務層實現,不能依賴數據庫的主鍵自增

CREATE DATABASE db_order;
USE db_order;
CREATE TABLE t_order0 (
  id BIGINT,
  order_no VARCHAR(30),
  user_id BIGINT,
  amount DECIMAL(10,2),
  PRIMARY KEY(id) 
);
CREATE TABLE t_order1 (
  id BIGINT,
  order_no VARCHAR(30),
  user_id BIGINT,
  amount DECIMAL(10,2),
  PRIMARY KEY(id) 
);

1.2、創建server-order1容器

  • step1:創建容器:
docker run -d \
-p 3311:3306 \
-v /atguigu/server/order1/conf:/etc/mysql/conf.d \
-v /atguigu/server/order1/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--name server-order1 \
mysql:8.0.29
  • step2:登錄MySQL服務器:
#進入容器:
docker exec -it server-order1 env LANG=C.UTF-8 /bin/bash
#進入容器內的mysql命令行
mysql -uroot -p
#修改默認密碼插件
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
  • **step3:創建數據庫:**和server-order0相同

注意:水平分片的id需要在業務層實現,不能依賴數據庫的主鍵自增

CREATE DATABASE db_order;
USE db_order;
CREATE TABLE t_order0 (
  id BIGINT,
  order_no VARCHAR(30),
  user_id BIGINT,
  amount DECIMAL(10,2),
  PRIMARY KEY(id) 
);
CREATE TABLE t_order1 (
  id BIGINT,
  order_no VARCHAR(30),
  user_id BIGINT,
  amount DECIMAL(10,2),
  PRIMARY KEY(id) 
);

2、基本水平分片

2.1、基本配置

#========================基本配置
# 應用名稱
spring.application.name=sharging-jdbc-demo
# 開發環境設置
spring.profiles.active=dev
# 內存模式
spring.shardingsphere.mode.type=Memory
# 打印SQl
spring.shardingsphere.props.sql-show=true

2.2、數據源配置

#========================數據源配置
# 配置真實數據源
spring.shardingsphere.datasource.names=server-user,server-order0,server-order1

# 配置第 1 個數據源
spring.shardingsphere.datasource.server-user.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.server-user.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.server-user.jdbc-url=jdbc:mysql://192.168.100.201:3301/db_user
spring.shardingsphere.datasource.server-user.username=root
spring.shardingsphere.datasource.server-user.password=123456

# 配置第 2 個數據源
spring.shardingsphere.datasource.server-order.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.server-order.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.server-order.jdbc-url=jdbc:mysql://192.168.100.201:3310/db_order
spring.shardingsphere.datasource.server-order.username=root
spring.shardingsphere.datasource.server-order.password=123456

# 配置第 3 個數據源
spring.shardingsphere.datasource.server-order.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.server-order.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.server-order.jdbc-url=jdbc:mysql://192.168.100.201:3311/db_order
spring.shardingsphere.datasource.server-order.username=root
spring.shardingsphere.datasource.server-order.password=123456

2.3、標椎分片表配置

#========================標準分片表配置(數據節點配置)
# spring.shardingsphere.rules.sharding.tables.<table-name>.actual-data-nodes=值
# 值由數據源名 + 表名組成,以小數點分隔。多個表以逗號分隔,支持 inline 表達式。
# <table-name>:邏輯表名
spring.shardingsphere.rules.sharding.tables.t_user.actual-data-nodes=server-user.t_user
spring.shardingsphere.rules.sharding.tables.t_order.actual-data-nodes=server-order0.t_order0,server-order0.t_order1,server-order1.t_order0,server-order1.t_order1

修改Order實體類的主鍵策略:

//@TableId(type = IdType.AUTO)//依賴數據庫的主鍵自增策略
@TableId(type = IdType.ASSIGN_ID)//分佈式id

測試:保留上面配置中的一個分片表節點分別進行測試,檢查每個分片節點是否可用

/**
     * 水平分片:插入數據測試
     */
@Test
public void testInsertOrder(){

    Order order = new Order();
    order.setOrderNo("ATGUIGU001");
    order.setUserId(1L);
    order.setAmount(new BigDecimal(100));
    orderMapper.insert(order);
}

2.4、行表達式

優化上一步的分片表配置

https://shardingsphere.apache.org/document/5.1.1/cn/features/sharding/concept/inline-expression/

#========================標準分片表配置(數據節點配置)
# spring.shardingsphere.rules.sharding.tables.<table-name>.actual-data-nodes=值
# 值由數據源名 + 表名組成,以小數點分隔。多個表以逗號分隔,支持 inline 表達式。
# <table-name>:邏輯表名
spring.shardingsphere.rules.sharding.tables.t_user.actual-data-nodes=server-user.t_user
spring.shardingsphere.rules.sharding.tables.t_order.actual-data-nodes=server-order$->{0..1}.t_order$->{0..1}

2.5、分片算法配置

水平分庫:

分片規則:order表中user_id為偶數時,數據插入server-order0服務器user_id為奇數時,數據插入server-order1服務器。這樣分片的好處是,同一個用户的訂單數據,一定會被插入到同一台服務器上,查詢一個用户的訂單時效率較高。

#------------------------分庫策略
# 分片列名稱
spring.shardingsphere.rules.sharding.tables.t_order.database-strategy.standard.sharding-column=user_id
# 分片算法名稱
spring.shardingsphere.rules.sharding.tables.t_order.database-strategy.standard.sharding-algorithm-name=alg_inline_userid

#------------------------分片算法配置
# 行表達式分片算法
# 分片算法類型
spring.shardingsphere.rules.sharding.sharding-algorithms.alg_inline_userid.type=INLINE
# 分片算法屬性配置
spring.shardingsphere.rules.sharding.sharding-algorithms.alg_inline_userid.props.algorithm-expression=server-order$->{user_id % 2}

# 取模分片算法
# 分片算法類型
spring.shardingsphere.rules.sharding.sharding-algorithms.alg_mod.type=MOD
# 分片算法屬性配置
spring.shardingsphere.rules.sharding.sharding-algorithms.alg_mod.props.sharding-count=2

為了方便測試,先設置只在 t_order0表上進行測試

xxx.actual-data-nodes=server-order$->{0..1}.t_order0

測試:可以分別測試行表達式分片算法和取模分片算法

/**
     * 水平分片:分庫插入數據測試
     */
@Test
public void testInsertOrderDatabaseStrategy(){

    for (long i = 0; i < 4; i++) {
        Order order = new Order();
        order.setOrderNo("ATGUIGU001");
        order.setUserId(i + 1);
        order.setAmount(new BigDecimal(100));
        orderMapper.insert(order);
    }

}

水平分表:

分片規則:order表中order_no的哈希值為偶數時,數據插入對應服務器的t_order0表order_no的哈希值為奇數時,數據插入對應服務器的t_order1表。因為order_no是字符串形式,因此不能直接取模。

#------------------------分表策略
# 分片列名稱
spring.shardingsphere.rules.sharding.tables.t_order.table-strategy.standard.sharding-column=order_no
# 分片算法名稱
spring.shardingsphere.rules.sharding.tables.t_order.table-strategy.standard.sharding-algorithm-name=alg_hash_mod


#------------------------分片算法配置
# 哈希取模分片算法
# 分片算法類型
spring.shardingsphere.rules.sharding.sharding-algorithms.alg_hash_mod.type=HASH_MOD
# 分片算法屬性配置
spring.shardingsphere.rules.sharding.sharding-algorithms.alg_hash_mod.props.sharding-count=2

測試前不要忘記將如下節點改回原來的狀態

xxx.actual-data-nodes=server-order$->{0..1}.t_order$->{0..1}

測試:

/**
     * 水平分片:分表插入數據測試
     */
@Test
public void testInsertOrderTableStrategy(){

    for (long i = 1; i < 5; i++) {

        Order order = new Order();
        order.setOrderNo("ATGUIGU" + i);
        order.setUserId(1L);
        order.setAmount(new BigDecimal(100));
        orderMapper.insert(order);
    }

    for (long i = 5; i < 9; i++) {

        Order order = new Order();
        order.setOrderNo("ATGUIGU" + i);
        order.setUserId(2L);
        order.setAmount(new BigDecimal(100));
        orderMapper.insert(order);
    }
}

/**
     * 測試哈希取模
     */
@Test
public void testHash(){

    //注意hash取模的結果是整個字符串hash後再取模,和數值後綴是奇數還是偶數無關
    System.out.println("ATGUIGU001".hashCode() % 2);
    System.out.println("ATGUIGU0011".hashCode() % 2);
}

查詢測試:

/**
     * 水平分片:查詢所有記錄
     * 查詢了兩個數據源,每個數據源中使用UNION ALL連接兩個表
     */
@Test
public void testShardingSelectAll(){

    List<Order> orders = orderMapper.selectList(null);
    orders.forEach(System.out::println);
}

/**
     * 水平分片:根據user_id查詢記錄
     * 查詢了一個數據源,每個數據源中使用UNION ALL連接兩個表
     */
@Test
public void testShardingSelectByUserId(){

    QueryWrapper<Order> orderQueryWrapper = new QueryWrapper<>();
    orderQueryWrapper.eq("user_id", 1L);
    List<Order> orders = orderMapper.selectList(orderQueryWrapper);
    orders.forEach(System.out::println);
}

2.6、分佈式序列算法

雪花算法:

https://shardingsphere.apache.org/document/5.1.1/cn/features/sharding/concept/key-generator/

水平分片需要關注全局序列,因為不能簡單的使用基於數據庫的主鍵自增。

這裏有兩種方案:一種是基於MyBatisPlus的id策略;一種是ShardingSphere-JDBC的全局序列配置。

基於MyBatisPlus的id策略:將Order類的id設置成如下形式

@TableId(type = IdType.ASSIGN_ID)
private Long id;

基於ShardingSphere-JDBC的全局序列配置:和前面的MyBatisPlus的策略二選一

#------------------------分佈式序列策略配置
# 分佈式序列列名稱
spring.shardingsphere.rules.sharding.tables.t_order.key-generate-strategy.column=id
# 分佈式序列算法名稱
spring.shardingsphere.rules.sharding.tables.t_order.key-generate-strategy.key-generator-name=alg_snowflake

# 分佈式序列算法配置
# 分佈式序列算法類型
spring.shardingsphere.rules.sharding.key-generators.alg_snowflake.type=SNOWFLAKE
# 分佈式序列算法屬性配置
#spring.shardingsphere.rules.sharding.key-generators.alg_snowflake.props.xxx=

此時,需要將實體類中的id策略修改成以下形式:

//當配置了shardingsphere-jdbc的分佈式序列時,自動使用shardingsphere-jdbc的分佈式序列
//當沒有配置shardingsphere-jdbc的分佈式序列時,自動依賴數據庫的主鍵自增策略
@TableId(type = IdType.AUTO)

3、多表關聯

3.1、創建關聯表

server-order0、server-order1服務器中分別創建兩張訂單詳情表t_order_item0、t_order_item1

我們希望同一個用户的訂單表和訂單詳情表中的數據都在同一個數據源中,避免跨庫關聯,因此這兩張表我們使用相同的分片策略。

那麼在t_order_item中我們也需要創建order_nouser_id這兩個分片鍵

CREATE TABLE t_order_item0(
    id BIGINT,
    order_no VARCHAR(30),
    user_id BIGINT,
    price DECIMAL(10,2),
    `count` INT,
    PRIMARY KEY(id)
);

CREATE TABLE t_order_item1(
    id BIGINT,
    order_no VARCHAR(30),
    user_id BIGINT,
    price DECIMAL(10,2),
    `count` INT,
    PRIMARY KEY(id)
);

3.2、創建實體類

package com.atguigu.shardingjdbcdemo.entity;

@TableName("t_order_item")
@Data
public class OrderItem {
    //當配置了shardingsphere-jdbc的分佈式序列時,自動使用shardingsphere-jdbc的分佈式序列
    @TableId(type = IdType.AUTO)
    private Long id;
    private String orderNo;
    private Long userId;
    private BigDecimal price;
    private Integer count;
}

3.3、創建Mapper

package com.atguigu.shargingjdbcdemo.mapper;

@Mapper
public interface OrderItemMapper extends BaseMapper<OrderItem> {

}

3.4、配置關聯表

t_order_item的分片表、分片策略、分佈式序列策略和t_order一致

#------------------------標準分片表配置(數據節點配置)
spring.shardingsphere.rules.sharding.tables.t_order_item.actual-data-nodes=server-order$->{0..1}.t_order_item$->{0..1}

#------------------------分庫策略
# 分片列名稱
spring.shardingsphere.rules.sharding.tables.t_order_item.database-strategy.standard.sharding-column=user_id
# 分片算法名稱
spring.shardingsphere.rules.sharding.tables.t_order_item.database-strategy.standard.sharding-algorithm-name=alg_mod

#------------------------分表策略
# 分片列名稱
spring.shardingsphere.rules.sharding.tables.t_order_item.table-strategy.standard.sharding-column=order_no
# 分片算法名稱
spring.shardingsphere.rules.sharding.tables.t_order_item.table-strategy.standard.sharding-algorithm-name=alg_hash_mod

#------------------------分佈式序列策略配置
# 分佈式序列列名稱
spring.shardingsphere.rules.sharding.tables.t_order_item.key-generate-strategy.column=id
# 分佈式序列算法名稱
spring.shardingsphere.rules.sharding.tables.t_order_item.key-generate-strategy.key-generator-name=alg_snowflake

3.5、測試插入數據

同一個用户的訂單表和訂單詳情表中的數據都在同一個數據源中,避免跨庫關聯

/**
     * 測試關聯表插入
     */
@Test
public void testInsertOrderAndOrderItem(){

    for (long i = 1; i < 3; i++) {

        Order order = new Order();
        order.setOrderNo("ATGUIGU" + i);
        order.setUserId(1L);
        orderMapper.insert(order);

        for (long j = 1; j < 3; j++) {
            OrderItem orderItem = new OrderItem();
            orderItem.setOrderNo("ATGUIGU" + i);
            orderItem.setUserId(1L);
            orderItem.setPrice(new BigDecimal(10));
            orderItem.setCount(2);
            orderItemMapper.insert(orderItem);
        }
    }

    for (long i = 5; i < 7; i++) {

        Order order = new Order();
        order.setOrderNo("ATGUIGU" + i);
        order.setUserId(2L);
        orderMapper.insert(order);

        for (long j = 1; j < 3; j++) {
            OrderItem orderItem = new OrderItem();
            orderItem.setOrderNo("ATGUIGU" + i);
            orderItem.setUserId(2L);
            orderItem.setPrice(new BigDecimal(1));
            orderItem.setCount(3);
            orderItemMapper.insert(orderItem);
        }
    }

}

4、綁定表

**需求:**查詢每個訂單的訂單號和總訂單金額

4.1、創建VO對象

package com.atguigu.shardingjdbcdemo.entity;

@Data
public class OrderVo {
    private String orderNo;
    private BigDecimal amount;
}

4.2、添加Mapper方法

package com.atguigu.shardingjdbcdemo.mapper;

@Mapper
public interface OrderMapper extends BaseMapper<Order> {

    @Select({"SELECT o.order_no, SUM(i.price * i.count) AS amount",
            "FROM t_order o JOIN t_order_item i ON o.order_no = i.order_no",
            "GROUP BY o.order_no"})
    List<OrderVo> getOrderAmount();

}

4.3、測試關聯查詢

/**
     * 測試關聯表查詢
     */
@Test
public void testGetOrderAmount(){

    List<OrderVo> orderAmountList = orderMapper.getOrderAmount();
    orderAmountList.forEach(System.out::println);
}

4.4、配置綁定表

在原來水平分片配置的基礎上添加如下配置:

#------------------------綁定表
spring.shardingsphere.rules.sharding.binding-tables[0]=t_order,t_order_item

配置完綁定表後再次進行關聯查詢的測試:

  • **如果不配置綁定表:測試的結果為8個SQL。**多表關聯查詢會出現笛卡爾積關聯。
  • 如果配置綁定表:測試的結果為4個SQL。 多表關聯查詢不會出現笛卡爾積關聯,關聯查詢效率將大大提升。

綁定表:指分片規則一致的一組分片表。 使用綁定表進行多表關聯查詢時,必須使用分片鍵進行關聯,否則會出現笛卡爾積關聯或跨庫關聯,從而影響查詢效率。

5、廣播表

4.1、什麼是廣播表

指所有的分片數據源中都存在的表,表結構及其數據在每個數據庫中均完全一致。 適用於數據量不大且需要與海量數據的表進行關聯查詢的場景,例如:字典表。

廣播具有以下特性:

(1)插入、更新操作會實時在所有節點上執行,保持各個分片的數據一致性

(2)查詢操作,只從一個節點獲取

(3)可以跟任何一個表進行 JOIN 操作

4.2、創建廣播表

在server-order0、server-order1和server-user服務器中分別創建t_dict表

CREATE TABLE t_dict(
    id BIGINT,
    dict_type VARCHAR(200),
    PRIMARY KEY(id)
);

4.3、程序實現

4.3.1、創建實體類
package com.atguigu.shardingjdbcdemo.entity;

@TableName("t_dict")
@Data
public class Dict {
    //可以使用MyBatisPlus的雪花算法
    @TableId(type = IdType.ASSIGN_ID)
    private Long id;
    private String dictType;
}
4.3.2、創建Mapper
package com.atguigu.shardingjdbcdemo.mapper;

@Mapper
public interface DictMapper extends BaseMapper<Dict> {
}
4.3.3、配置廣播表
#數據節點可不配置,默認情況下,向所有數據源廣播
spring.shardingsphere.rules.sharding.tables.t_dict.actual-data-nodes=server-user.t_dict,server-order$->{0..1}.t_dict

# 廣播表
spring.shardingsphere.rules.sharding.broadcast-tables[0]=t_dict

4.4、測試廣播表

@Autowired
private DictMapper dictMapper;

/**
     * 廣播表:每個服務器中的t_dict同時添加了新數據
     */
@Test
public void testBroadcast(){

    Dict dict = new Dict();
    dict.setDictType("type1");
    dictMapper.insert(dict);
}

/**
     * 查詢操作,只從一個節點獲取數據
     * 隨機負載均衡規則
     */
@Test
public void testSelectBroadcast(){

    List<Dict> dicts = dictMapper.selectList(null);
    dicts.forEach(System.out::println);
}

第07章 啓動ShardingSphere-Proxy

1、獲取

目前 ShardingSphere-Proxy 提供了 3 種獲取方式:

  • 二進制發佈包
  • Docker
  • Helm

2、使用二進制發佈包安裝

二進制包既可以Linux系統運行,又可以在windows系統運行

step1:解壓二進制包

apache-shardingsphere-5.1.1-shardingsphere-proxy-bin.tar.gz

windows:使用解壓軟件解壓文件

Linux:將文件上傳至/opt目錄,並解壓

tar -zxvf apache-shardingsphere-5.1.1-shardingsphere-proxy-bin.tar.gz

step2:MySQL驅動

mysql-connector-java-8.0.22.jar

將MySQl驅動放至解壓目錄中的ext-lib目錄

spte3:修改配置conf/server.yaml

rules:
  - !AUTHORITY
    users:
      - root@%:root
    provider:
      type: ALL_PRIVILEGES_PERMITTED

props:
  sql-show: true

spte4:啓動ShardingSphere-Proxy

Linux 操作系統請運行 bin/start.sh

Windows 操作系統請運行 bin/start.bat

指定端口號和配置文件目錄:bin/start.bat ${proxy_port} ${proxy_conf_directory}

step5:遠程連接ShardingSphere-Proxy

遠程訪問

mysql -h192.168.100.1 -P3307 -uroot -p

step6:訪問測試

show databases;

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-f6Q3PqID-1667321256743)(assets/image-20220819152009158.png)]

3、使用Docker安裝

step1:啓動Docker容器

docker run -d \
-v /atguigu/server/proxy-a/conf:/opt/shardingsphere-proxy/conf \
-v /atguigu/server/proxy-a/ext-lib:/opt/shardingsphere-proxy/ext-lib \
-e ES_JAVA_OPTS="-Xmx256m -Xms256m -Xmn128m" \
-p 3321:3307 \
--name server-proxy-a \
apache/shardingsphere-proxy:5.1.1

step2:上傳MySQL驅動

將MySQl驅動上傳至/atguigu/server/proxy-a/ext-lib目錄

spte3:修改配置server.yaml

rules:
  - !AUTHORITY
    users:
      - root@%:root
    provider:
      type: ALL_PRIVILEGES_PERMITTED

props:
  sql-show: true

將配置文件上傳至/atguigu/server/proxy-a/conf目錄

spte4:重啓容器

docker restart server-proxy-a

step5:遠程連接ShardingSphere-Proxy

ShardingSphere-Proxy容器中默認情況下沒有mysql命令行客户端的安裝,因此需要遠程訪問

mysql -h192.168.100.201 -P3321 -uroot -p

step6:訪問測試

show databases;

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-2blYLtn6-1667321256744)(assets/image-20220819152009158.png)]

常見問題:docker容器無法遠程連接

容器可以成功的創建並啓動,但是無法遠程連接。排除防火牆和網絡等問題後,看看是不是因為容器內存不足導致。

原因:容器可分配內存不足

查看辦法:進入容器後查看ShardingSphere-Proxy的日誌,如有有cannot allocate memory,則説明容器內存不足

docker exec -it server-proxy-a env LANG=C.UTF-8 /bin/bash
cd /opt/shardingsphere-proxy/logs
tail stdout.log

postgresql shardingsphere 分庫分表_#學習_23

解決方案:創建容器的時候使用JVM參數

-e ES_JAVA_OPTS="-Xmx256m -Xms256m -Xmn128m"

第08章 ShardingSphere-Proxy讀寫分離

1、修改配置文件

修改配置config-readwrite-splitting.yaml

schemaName: readwrite_splitting_db

dataSources:
  write_ds:
    url: jdbc:mysql://192.168.100.201:3306/db_user?serverTimezone=UTC&useSSL=false
    username: root
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1
  read_ds_0:
    url: jdbc:mysql://192.168.100.201:3307/db_user?serverTimezone=UTC&useSSL=false
    username: root
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1
  read_ds_1:
    url: jdbc:mysql://192.168.100.201:3308/db_user?serverTimezone=UTC&useSSL=false
    username: root
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1

rules:
- !READWRITE_SPLITTING
  dataSources:
    readwrite_ds:
      type: Static
      props:
        write-data-source-name: write_ds
        read-data-source-names: read_ds_0,read_ds_1

將配置文件上傳至/atguigu/server/proxy-a/conf目錄

重啓容器

docker restart server-proxy-a

2、實時查看日誌

可以通過這種方式查看服務器中輸出的SQL語句

docker exec -it server-proxy-a env LANG=C.UTF-8 /bin/bash
tail -f /opt/shardingsphere-proxy/logs/stdout.log

3、遠程訪問測試

mysql> show databases;
mysql> use readwrite_splitting_db;
mysql> show tables;
mysql> select * from t_user;
mysql> select * from t_user;
mysql> insert into t_user(uname) values('wang5');

4、應用程序訪問Proxy

4.1、創建項目

項目類型:Spring Initializr

SpringBoot腳手架:http://start.aliyun.com

項目名:sharding-proxy-demo

SpringBoot版本:2.3.7.RELEASE

4.2、添加依賴

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>

    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.3.1</version>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

4.3、創建實體類

package com.atguigu.shardingproxydemo.entity;

@TableName("t_user")
@Data
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String uname;
}

4.4、創建Mapper

package com.atguigu.shardingproxydemo.mapper;

@Mapper
public interface UserMapper extends BaseMapper<User> {
}

4.5、配置數據源

# 應用名稱
spring.application.name=sharding-proxy-demo
# 開發環境設置
spring.profiles.active=dev

#mysql數據庫連接(proxy)
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.100.201:3321/readwrite_splitting_db?serverTimezone=GMT%2B8&useSSL=false
spring.datasource.username=root
spring.datasource.password=root

#mybatis日誌
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

4.6、測試

package com.atguigu.shardingproxydemo;

@SpringBootTest
class ShardingProxyDemoApplicationTests {

    @Autowired
    private UserMapper userMapper;

    /**
     * 讀數據測試
     */
    @Test
    public void testSelectAll(){
        List<User> users = userMapper.selectList(null);
        users.forEach(System.out::println);
    }
}

第09章 ShardingSphere-Proxy垂直分片

1、修改配置文件

修改配置config-sharding.yaml

schemaName: sharding_db

dataSources:
  ds_0:
    url: jdbc:mysql://192.168.100.201:3301/db_user?serverTimezone=UTC&useSSL=false
    username: root
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1
  ds_1:
    url: jdbc:mysql://192.168.100.201:3302/db_order?serverTimezone=UTC&useSSL=false
    username: root
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1

rules:
- !SHARDING
  tables:
    t_user:
      actualDataNodes: ds_0.t_user
    t_order:
      actualDataNodes: ds_1.t_order

2、實時查看日誌

可以通過這種方式查看服務器中輸出的SQL語句

docker exec -it server-proxy-a env LANG=C.UTF-8 /bin/bash
tail -f /opt/shardingsphere-proxy/logs/stdout.log

3、遠程訪問測試

mysql> show databases;
mysql> use sharding_db;
mysql> show tables;
mysql> select * from t_order;
mysql> select * from t_user;

第10章 ShardingSphere-Proxy水平分片

1、修改配置文件

修改配置config-sharding.yaml

schemaName: sharding_db

dataSources:
  ds_user:
    url: jdbc:mysql://192.168.100.201:3301/db_user?serverTimezone=UTC&useSSL=false
    username: root
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1
  ds_order0:
    url: jdbc:mysql://192.168.100.201:3310/db_order?serverTimezone=UTC&useSSL=false
    username: root
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1
  ds_order1:
    url: jdbc:mysql://192.168.100.201:3311/db_order?serverTimezone=UTC&useSSL=false
    username: root
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1

rules:
- !SHARDING
  tables:
    t_user:
      actualDataNodes: ds_user.t_user

    t_order:
      actualDataNodes: ds_order${0..1}.t_order${0..1}
      databaseStrategy:
        standard:
          shardingColumn: user_id
          shardingAlgorithmName: alg_mod
      tableStrategy:
        standard:
          shardingColumn: order_no
          shardingAlgorithmName: alg_hash_mod
      keyGenerateStrategy:
        column: id
        keyGeneratorName: snowflake
    t_order_item:
      actualDataNodes: ds_order${0..1}.t_order_item${0..1}
      databaseStrategy:
        standard:
          shardingColumn: user_id
          shardingAlgorithmName: alg_mod
      tableStrategy:
        standard:
          shardingColumn: order_no
          shardingAlgorithmName: alg_hash_mod
      keyGenerateStrategy:
        column: id
        keyGeneratorName: snowflake

  bindingTables:
    - t_order,t_order_item


  broadcastTables:
    - t_dict

  shardingAlgorithms:
    alg_inline_userid:
      type: INLINE
      props:
        algorithm-expression: server-order$->{user_id % 2}
    alg_mod:
      type: MOD
      props:
        sharding-count: 2
    alg_hash_mod:
      type: HASH_MOD
      props:
        sharding-count: 2
  
  keyGenerators:
    snowflake:
      type: SNOWFLAKE

2、實時查看日誌

可以通過這種方式查看服務器中輸出的SQL語句

docker exec -it server-proxy-a env LANG=C.UTF-8 /bin/bash
tail -f /opt/shardingsphere-proxy/logs/stdout.log

3、遠程訪問測試

mysql> show databases;
mysql> use sharding_db;
mysql> show tables;
mysql> select * from t_order; --測試水平分片
mysql> select * from t_dict; --測試廣播表