數據庫中間件ShardingSphere5

一、高性能架構模式

數據庫集群,第一種方式“讀寫分離”,第二種方式“數據庫分片”。

1.1 讀寫分離架構

讀寫分離原理:將數據庫讀寫操作分散到不同的節點上。

讀寫分離的基本實現:

  • 主庫負責處理事務性的增刪改操作,從庫負責處理查詢操作。
  • 讀寫分離是根據SQL語義的分析,將讀寫操作分別路由至主庫和從庫。
  • 通過一主多從的配置方式,可以將查詢請求均勻的分散到多個數據副本,進一步提升系統處理能力。
  • 使用多主多從,提升系統的吞吐量,可用性,任何一個數據庫宕機或者磁盤損壞,不影響系統正常運行。
  • 根據業務需要,將用戶表讀寫操作路由到不同的數據庫

CAP理論:

在一個分布式系統中,涉及讀寫操作時,保證一致性(Consistence)、可用性(Availability)、分區容錯性(Partition Tolerance),只能做到CP,AP。

CP:為保證一致性,發生分區現象后,N1節點數據更新到y,當N1與N2之間的通道中斷后,N2為同步數據,客戶端訪問N2時返回Error。

AP:為保證可用性,客戶端訪問N2將數據返回給C。

CAP理論C實踐中不能完美實現,無法做到強一致性。可以采用適合的方式達到最終一致性。

  1. 基本可用(Basically Available):分布式系統在出現故障時,允許損失部分可用性,即保證核心可用。
  2. 軟狀態(Soft State):允許系統存在中間狀態,而該中間狀態不會影響系統整體可用性。這里的中間狀態就是 CAP 理論中的數據不一致。
  3. 最終一致性(Eventual Consistency):系統中的所有數據副本經過一定時間后,最終能夠達到一致的狀態。

1.2 數據庫分片架構

  • 讀寫分離問題:

????????分散數據庫讀寫操作壓力,沒有分散存儲壓力,需要將存儲分散到多臺數據庫服務器上。

  • 數據分片:

????????將存放在單一數據庫中的數據分散地存放到多個數據庫或表,數據分片的有效手段是對關系型是對關系型數據庫進行分庫和分表。數據分片的拆分方式又分為垂直分片和水平分片。

1.2.1 垂直分片

垂直分庫:

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

將用戶表和訂單表垂直分片到不同的數據庫的方案:

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

?????????垂直分表:

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

1.2.2 水平分片

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

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

  • 水平分表:單表切分為多表后,新的表即使在同一個數據庫服務器中,也可能帶來可觀的性能提升,如果性能能夠滿足業務要求,可以不拆分到多臺數據庫服務器,畢竟業務分庫也會引入很多復雜性;

  • 水平分庫:如果單表拆分為多表后,單臺服務器依然無法滿足性能要求,那就需要將多個表分散在不同的數據庫服務器中。

阿里巴巴Java開發手冊:

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

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

第2章 解決方案

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

2.1 程序代碼封裝

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

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

2.2 中間件封裝

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

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

2.3 常用解決方案

- Apache ShardingSphere

? - 程序代碼封裝:ShardingSphere-JDBC
? - 中間件封裝:ShardingSphere-Proxy


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

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

??

- MyCat:數據庫中間件?

三、MySQL主從同步

3.1 MySQL主從同步原理

  • 基本原理:
  1. slave會從master讀取binlog來進行數據同步
  2. 具體步驟:
  3. master將數據改變記錄到二進制日志中。
  4. 當slave上執行start slave命令之后,salve會創建一個IO線程用來連接master,請求master中的binlog.
  5. 當slave連接上master時,master會創建一個log dump線程,用于發送binlog的內容。在讀取binlog的內容。在讀取binlog的內容的操作中,會對主節點上的binlog加鎖,當讀取完成并發送給從服務器后解鎖。
  6. IO線程接收主節點binlog dump 進程發來的更新之后,保存到中繼日志(relay log)中。
  7. slave的SQL線程,讀取 relay log 日志,并解析成具體操作,實現主從操作一直,最終數據一致。

3.2 一主多從配置

docker方式創建,主從服務器IP一致,端口號不一致。

  • 主服務器:容器名mysql-master,端口3307

  • 從服務器:容器名mysql-slave1,端口3308

  • 從服務器:容器名mysql-slave2,端口3309

注意:如果此時防火墻是開啟的,則先關閉防火墻,并重啟docker,否則后續安裝的MySQL無法啟動

#關閉docker
systemctl stop docker
#關閉防火墻
systemctl stop firewalld
#啟動docker
systemctl start docker

3.2.1 準備主服務器

  • step1:在docker中創建并啟動MySQL主服務器:端口3307

docker run -d \
-p 3307:3306 \
-v /mysql/master/conf:/etc/mysql/conf.d \
-v /mysql/master/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--name mysql-master \
mysql:8.0.30

  • step2:創建MySQL主服務器配置文件:

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

vim mysql/master/conf/my.cnf

配置如下內容

[mysqld]
# 服務器唯一id,默認值1
server-id=1
# 設置日志格式,默認值ROW
binlog_format=STATEMENT
# 二進制日志名,默認binlog
# log-bin=binlog
# 設置需要復制的數據庫,默認復制全部數據庫
#binlog-do-db=mytestdb1
#binlog-do-db=mytestdb2
# 設置不需要復制的數據庫
#binlog-ignore-db=mytestdb3
#binlog-ignore-db=mytestdb4

重啟MySQL容器

docker restart 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的優先級問題:

  • step3:使用命令行登錄MySQL主服務器:

#進入容器:env LANG=C.UTF-8 避免容器中顯示中文亂碼
docker exec -it 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 'slave'@'%';
-- 設置密碼
ALTER USER 'slave'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
-- 授予復制權限
GRANT REPLICATION SLAVE ON *.* TO 'slave'@'%';
-- 刷新權限
FLUSH PRIVILEGES;

  • step5:主機中查詢master狀態:

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

SHOW MASTER STATUS;

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

reset master

3.2.2 準備從服務器1

可以配置多臺從機slave1、slave2...,這里以配置slave1為例,請參考slave1獨立完成slave2的配置

  • step1:在docker中創建并啟動MySQL從服務器:端口3308

docker run -d \
-p 3308:3306 \
-v mysql/slave1/conf:/etc/mysql/conf.d \
-v mysql/slave1/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--name mysql-slave1 \
mysql:8.0.29

  • step2:創建MySQL從服務器配置文件:

  • vim /mysql/slave1/conf/my.cnf

配置如下內容:

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

重啟MySQL容器

  • docker restart mysql-slave1

  • step3:使用命令行登錄MySQL從服務器:

#進入容器:
docker exec -it 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.200.130', 
MASTER_USER='slave',MASTER_PASSWORD='123456', MASTER_PORT=3306,
MASTER_LOG_FILE='binlog.000003',MASTER_LOG_POS=1357; 

3.2.3 準備從服務器2

參考3.2.2

3.2.4 啟動主從同步

分別在兩臺從機上啟動從機的復制功能,執行SQL:

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

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

3.2.5 測試主從同步

在主機中執行以下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);

3.2.6 常見問題

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

典型的錯誤例如:

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
-- 在從機上執行。功能說明:停止I/O 線程和SQL線程的操作。
stop slave; 
?
-- 在從機上執行。功能說明:用于刪除SLAVE數據庫的relaylog日志文件,并重新啟用新的relaylog文件。
reset slave;
?
-- 在主機上執行。功能說明:刪除所有的binlog日志文件,并將日志索引文件清空,重新開始所有新的日志文件。
-- 用于第一次進行搭建主從庫時,進行主庫binlog初始化工作;
reset master;
?
-- 還原主服務器之前的操作
?
-- 在主機查看mater狀態
SHOW MASTER STATUS;
-- 在主機刷新日志
FLUSH LOGS;
-- 再次在主機查看mater狀態(會發現File和Position發生了變化)
SHOW MASTER STATUS;
-- 修改從機連接主機的SQL,并重新連接即可

第4章 ShardingSphere-JDBC讀寫分離

4.1 創建SpringBoot程序

4.1.1 創建項目

項目類型:Spring Initializr

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

項目名:sharding-jdbc-demo

SpringBoot版本:3.0.5

4.1.2 添加依賴

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency>
?<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
?<dependency><groupId>org.apache.shardingsphere</groupId><artifactId>shardingsphere-jdbc-core</artifactId><version>5.4.0</version></dependency>
?<!--兼容jdk17和spring boot3--><dependency><groupId>org.yaml</groupId><artifactId>snakeyaml</artifactId><version>1.33</version></dependency><dependency><groupId>org.glassfish.jaxb</groupId><artifactId>jaxb-runtime</artifactId><version>2.3.8</version></dependency>
?<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.30</version></dependency>
?<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.1</version></dependency>
?<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency>
</dependencies>

4.1.3 創建實體類

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

4.1.4 創建Mapper

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

4.1.5 配置 Spring Boot

application.properties:

# 配置 DataSource Driver
spring.datasource.driver-class-name=org.apache.shardingsphere.driver.ShardingSphereDriver
# 指定 YAML 配置文件
spring.datasource.url=jdbc:shardingsphere:classpath:shardingsphere.yaml

4.1.6 配置shardingsphere

shardingsphere.yaml

模式配置:

mode:type: Standalonerepository:type: JDBC

  • 數據源配置:
dataSources:write_ds:dataSourceClassName: com.zaxxer.hikari.HikariDataSourcedriverClassName: com.mysql.cj.jdbc.DriverjdbcUrl: jdbc:mysql://192.168.200.130:3307/db_userusername: rootpassword: 123456read_ds_0:dataSourceClassName: com.zaxxer.hikari.HikariDataSourcedriverClassName: com.mysql.cj.jdbc.DriverjdbcUrl: jdbc:mysql://192.168.200.130:3308/db_userusername: rootpassword: 123456read_ds_1:dataSourceClassName: com.zaxxer.hikari.HikariDataSourcedriverClassName: com.mysql.cj.jdbc.DriverjdbcUrl: jdbc:mysql://192.168.200.130:3309/db_userusername: rootpassword: 123456

讀寫分離配置:

rules:- !READWRITE_SPLITTING#讀寫dataSources:readwrite_ds:writeDataSourceName: write_ds#數據源指向readDataSourceNames:#數據源指向- read_ds_0- read_ds_1transactionalReadQueryStrategy: PRIMARY # 事務內讀請求的路由策略,可選值:PRIMARY(路由至主庫)、FIXED(同一事務內路由至固定數據源)、DYNAMIC(同一事務內路由至非固定數據源)。默認值:DYNAMICloadBalancerName: random#負載均衡隨機loadBalancers:random:type: RANDOM

輸出sql:

props:sql-show: true

4.2 測試

4.2.1 讀寫分離測試

?
@SpringBootTest
class ShardingJdbcDemoApplicationTests {
?@Autowiredprivate UserMapper userMapper;
?/*** 寫入數據的測試*/@Testpublic void testInsert(){
?User user = new User();user.setUname("張三豐");userMapper.insert(user);}
?
}

4.2.2 負載均衡測試

/*** 負載均衡測試*/
@Test
public void testSelect(){
?for (int i = 0; i < 100; i++) {User user1 = userMapper.selectById(1);}
}

負載均衡算法配置:

rules:- !READWRITE_SPLITTINGloadBalancers:random:type: RANDOMround_robin:type: ROUND_ROBINweight:type: WEIGHTprops:read_ds_0: 1read_ds_1: 2#設置權重

4.2.3 事務測試

transactionalReadQueryStrategy: PRIMARY

事務內讀請求的路由策略,可選值:

PRIMARY(路由至主庫)

FIXED(同一事務內路由至固定數據源)

DYNAMIC(同一事務內路由至非固定數據源)。默認值:DYNAMIC

1、測試1

不添加@Transactional:insert對主庫操作,select對從庫操作

2、測試2

添加@Transactional:則insert和select按照transactionalReadQueryStrategy的配置執行

/*** 事務測試*/
@Transactional//開啟事務
@Test
public void testTrans(){
?User user = new User();user.setUname("鐵錘");userMapper.insert(user);
?List<User> users = userMapper.selectList(null);
}

注意:在JUnit環境下的@Transactional注解,默認情況下就會對事務進行回滾(即使在沒加注解@Rollback,也會對事務回滾)

3、常見錯誤

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

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

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

第5章 ShardingSphere-JDBC垂直分片

5.1 準備服務器

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

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

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

5.1.1 創建server-user容器

  • step1:創建容器:

docker run -d \
-p 3301:3306 \
-v server/user/conf:/etc/mysql/conf.d \
-v 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)
);

5.1.2 創建server-order容器

  • step1:創建容器:

docker run -d \
-p 3302:3306 \
-v /server/order/conf:/etc/mysql/conf.d \
-v /server/order/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--name server-order \
mysql:8.0.30

  • 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,PRIMARY KEY(id) 
);

5.2 程序實現

5.2.1 創建實體類

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

5.2.2 創建Mapper

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

5.2.3 配置垂直分片

模式配置

mode:type: Standalonerepository:type: JDBC

數據源配置:

dataSources:user_ds:dataSourceClassName: com.zaxxer.hikari.HikariDataSourcedriverClassName: com.mysql.cj.jdbc.DriverjdbcUrl: jdbc:mysql://192.168.200.130:3301/db_userusername: rootpassword: 123456order_ds:dataSourceClassName: com.zaxxer.hikari.HikariDataSourcedriverClassName: com.mysql.cj.jdbc.DriverjdbcUrl: jdbc:mysql://192.168.200.130:3302/db_orderusername: rootpassword: 123456

垂直分片配置:

rules:- !SHARDINGtables:t_user:actualDataNodes: user_ds.t_usert_order:actualDataNodes: order_ds.t_order

輸出sql:

props:sql-show: true

5.3 測試垂直分片

@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("001");order.setUserId(user.getId());orderMapper.insert(order);
?
}
?
/*** 垂直分片:查詢數據測試*/
@Test
public void testSelectFromOrderAndUser(){User user = userMapper.selectById(1L);Order order = orderMapper.selectById(1L);
}

第6章 ShardingSphere-JDBC水平分片

6.1 準備服務器

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

  • 服務器:容器名server-order0,端口3320

  • 服務器:容器名server-order1,端口3321

6.1.1 創建server-order0容器

  • step1:創建容器:

docker run -d \
-p 3320:3306 \
-v /server/order0/conf:/etc/mysql/conf.d \
-v /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(50),user_id BIGINT,PRIMARY KEY(id) 
);
CREATE TABLE t_order1 (id BIGINT,order_no VARCHAR(50),user_id BIGINT,PRIMARY KEY(id) 
);

6.1.2 創建server-order1容器

  • step1:創建容器:

docker run -d \
-p 3321:3306 \
-v /server/order1/conf:/etc/mysql/conf.d \
-v /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需要在業務層實現,不能依賴數據庫的主鍵自增

//數據庫主鍵自增機制(如 MySQL 的?AUTO_INCREMENT)無法直接滿足分布式環境下的全局唯一性需求,因此通常需要在業務層實現全局唯一 ID 生成策略

?Snowflake 算法及其變種
  • 原理:生成 64 位長整型 ID,結構為:時間戳(41位)+ 機器ID(10位)+ 序列號(12位)
CREATE DATABASE db_order;
USE db_order;
CREATE TABLE t_order0 (id BIGINT,order_no VARCHAR(30),user_id BIGINT,PRIMARY KEY(id) 
);
CREATE TABLE t_order1 (id BIGINT,order_no VARCHAR(30),user_id BIGINT,PRIMARY KEY(id) 
);

6.2 水平分片

6.2.1 配置一個分片節點

模式配置

mode:type: Standalonerepository:type: JDBC

數據源配置:

dataSources:user_ds:dataSourceClassName: com.zaxxer.hikari.HikariDataSourcedriverClassName: com.mysql.cj.jdbc.DriverjdbcUrl: jdbc:mysql://192.168.200.130:3301/db_userusername: rootpassword: 123456order_ds_0:dataSourceClassName: com.zaxxer.hikari.HikariDataSourcedriverClassName: com.mysql.cj.jdbc.DriverjdbcUrl: jdbc:mysql://192.168.200.130:3320/db_orderusername: rootpassword: 123456order_ds_1:dataSourceClassName: com.zaxxer.hikari.HikariDataSourcedriverClassName: com.mysql.cj.jdbc.DriverjdbcUrl: jdbc:mysql://192.168.200.130:3321/db_orderusername: rootpassword: 123456

配置一個order分片節點:

rules:- !SHARDINGtables:t_user:actualDataNodes: user_ds.t_usert_order:actualDataNodes: order_ds_0.t_order0

輸出sql:

props:sql-show: true

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

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

測試代碼:

/*** 水平分片:插入數據測試*/
@Test
public void testInsertOrder(){
?Order order = new Order();order.setOrderNo("001");order.setUserId(1L);orderMapper.insert(order);
}

6.2.2 水平分庫配置

使用行表達式:核心概念 :: ShardingSphere (apache.org)

將數據 分片到order_ds_0和order_ds_1中

actualDataNodes: order_ds_${0..1}.t_order0

分片算法配置

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

rules:- !SHARDINGtables:#數據庫分片和表分片t_user:actualDataNodes: user_ds.t_usert_order:actualDataNodes: order_ds_${0..1}.t_order0databaseStrategy:
#databaseStrategy:按 user_id 列使用名為 userid_inline 的算法進行分庫。
#tableStrategy:按 id 列使用名為 orderid_inline 的算法進行分表。standard:shardingColumn: user_idshardingAlgorithmName: userid_inline
?shardingAlgorithms:userid_inline:type: INLINEprops:algorithm-expression: order_ds_${user_id % 2}//分片的算法

測試:

/*** 水平分片:分庫插入數據測試*/
@Test
public void testInsertOrderDatabaseStrategy(){
?for (long i = 0; i < 4; i++) {Order order = new Order();order.setOrderNo(" + System.currentTimeMillis());order.setUserId(i + 1);orderMapper.insert(order);}
}

6.2.3 水平分表配置

將數據 分片到order_ds_0和order_ds_1的t_order0和t_order1中

actualDataNodes: order_ds_${0..1}.t_order${0..1}

分片算法配置

分片規則:order表中id為偶數時,數據插入t_order0數據庫id為奇數時,數據插入t_order1數據庫

rules:- !SHARDINGtables:t_user:actualDataNodes: user_ds.t_usert_order:actualDataNodes: order_ds_${0..1}.t_order${0..1}databaseStrategy:standard:shardingColumn: user_idshardingAlgorithmName: userid_inlinetableStrategy:standard:shardingColumn: idshardingAlgorithmName: orderid_inline
?shardingAlgorithms:userid_inline:type: INLINEprops:algorithm-expression: order_ds_${user_id % 2}orderid_inline:type: INLINEprops:algorithm-expression: t_order${id % 2}

測試:

/*** 水平分片:分表插入數據測試*/
@Test
public void testInsertOrderTableStrategy(){
?for (long i = 0; i < 4; i++) {Order order = new Order();order.setOrderNo("" + System.currentTimeMillis());order.setUserId(1L);orderMapper.insert(order);}
?for (long i = 0; i < 4; i++) {
?Order order = new Order();order.setOrderNo(System.currentTimeMillis());order.setUserId(2L);orderMapper.insert(order);}
}

6.3 多表關聯

6.3.1 創建關聯表

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

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

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

CREATE TABLE t_order_item0(id BIGINT,user_id BIGINT,order_id BIGINT,price DECIMAL(10,2),`count` INT,PRIMARY KEY(id)
);
?
CREATE TABLE t_order_item1(id BIGINT,user_id BIGINT,order_id BIGINT,price DECIMAL(10,2),`count` INT,PRIMARY KEY(id)
);

6.3.2 創建實體類

?
@TableName("t_order_item")
@Data
public class OrderItem {
?@TableId(type = IdType.ASSIGN_ID) //分布式idprivate Long id;private Long userId;private Long orderId;private BigDecimal price;private Integer count;
}

6.3.3 創建Mapper

?
@Mapper
public interface OrderItemMapper extends BaseMapper<OrderItem> {
}

6.3.4 配置關聯表

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

rules:- !SHARDINGtables:t_user:actualDataNodes: user_ds.t_usert_order:actualDataNodes: order_ds_${0..1}.t_order${0..1}databaseStrategy:standard:shardingColumn: user_idshardingAlgorithmName: userid_inlinetableStrategy:standard:shardingColumn: idshardingAlgorithmName: orderid_inlinet_order_item:actualDataNodes: order_ds_${0..1}.t_order_item${0..1}databaseStrategy:standard:shardingColumn: user_idshardingAlgorithmName: userid_inlinetableStrategy:standard:shardingColumn: order_idshardingAlgorithmName: orderid_item_inline
?shardingAlgorithms:userid_inline:type: INLINEprops:algorithm-expression: order_ds_${user_id % 2}orderid_inline:type: INLINEprops:algorithm-expression: t_order${id % 2}orderid_item_inline:type: INLINEprops:algorithm-expression: t_order_item${order_id % 2}

6.3.5 測試插入數據

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

 ? /*** 測試關聯表插入*/@Testpublic void testInsertOrderAndOrderItem(){
?
?for (long i = 0; i < 2; i++) {
?Order order = new Order();order.setOrderNo("" + System.currentTimeMillis());order.setUserId(1L);orderMapper.insert(order);
?for (long j = 0; j < 2; j++) {OrderItem orderItem = new OrderItem();orderItem.setUserId(1L);orderItem.setOrderId(order.getId());orderItem.setPrice(new BigDecimal(10));orderItem.setCount(2);orderItemMapper.insert(orderItem);}}
?for (long i = 0; i < 2; i++) {Order order = new Order();order.setOrderNo("" + System.currentTimeMillis());order.setUserId(2L);orderMapper.insert(order);for (long j = 0; j < 2; j++) {OrderItem orderItem = new OrderItem();orderItem.setUserId(2L);orderItem.setOrderId(order.getId());orderItem.setPrice(new BigDecimal(5));orderItem.setCount(2);orderItemMapper.insert(orderItem);}}}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/87675.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/87675.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/87675.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C++11 右值引用(Rvalue Reference)

在 C++11 中,右值引用(Rvalue Reference) 是一個革命性的語言特性,它為現代 C++ 的性能優化、資源管理以及語義清晰化奠定了基礎。通過引入 T&& 語法,C++11 支持了 移動語義(Move Semantics) 和 完美轉發(Perfect Forwarding),極大地提升了程序效率和代碼表達…

skynet源碼學習-skynet_main入口

skynet源碼學習-skynet_main入口 核心功能與啟動流程Shell腳本啟動示例main函數參數處理其他相關聯函數解析1. 配置加載器解析2. 環境變量設置3. 配置解析函數 核心配置項解析典型配置文件分析服務啟動與運行核心服務啟動流程完整啟動時序圖 核心功能與啟動流程 Skynet 的啟動…

前端圖文混排頁面一鍵導出PDF最佳實踐 —— 以Vue3+html2pdf.js為例

前言 在現代管理系統中,數據的歸檔、分享和線下流轉需求日益增長。如何將前端頁面的圖文內容高質量導出為PDF,成為許多企業和開發者關注的技術點。本文以實際項目為例,系統梳理前端導出PDF的完整實現思路與優化經驗。 一、項目背景與需求分析 1.1 背景故事 在某管理系統的…

19|Whisper+ChatGPT:請AI代你聽播客

今天&#xff0c;我們的課程開始進入一個新的主題了&#xff0c;那就是語音識別。過去幾周我們介紹的ChatGPT雖然很強大&#xff0c;但是只能接受文本的輸入。而在現實生活中&#xff0c;很多時候我們并不方便停下來打字。很多內容比如像播客也沒有文字版&#xff0c;所以這個時…

linux常用設置

1&#xff0c;ubuntu設置ssh-agent進入shell時自動加載 一&#xff0c;添加自動加載腳本&#xff0c;vim /etc/profile.d/keychain.sh # /etc/profile.d/keychain.sh # 自動啟動 ssh-agent 并加載多個私鑰 export KEYCHAIN_HOME"/root/.keychain" # 多個key&#xf…

電子電氣架構 --- 軟件供應商如何進入OEM體系

我是穿拖鞋的漢子,魔都中堅持長期主義的汽車電子工程師。 老規矩,分享一段喜歡的文字,避免自己成為高知識低文化的工程師: 簡單,單純,喜歡獨處,獨來獨往,不易合同頻過著接地氣的生活,除了生存溫飽問題之外,沒有什么過多的欲望,表面看起來很高冷,內心熱情,如果你身…

破解數據可視化難題:帶軸斷裂的柱狀圖繪制全指南

引言&#xff1a;當數據跨度讓圖表失真時&#xff0c;軸斷裂技術如何力挽狂瀾&#xff1f; 在數據可視化的世界里&#xff0c;我們常常會遇到這樣的困境&#xff1a;一組數據中既有 "巨無霸" 般的極端值&#xff0c;又有需要精細展示的小數據。比如在財務報表中&…

以太網基礎①以太網相關通信接口

1. 今日摸魚任務 需要學習使用ZYNQ的以太網傳輸SCPI指令 需要把PL PS兩側的都用起來&#xff08;加油鴨&#xff01;&#xff09; 吶吶吶 今天就先學一下基礎知識唄 02_【邏輯教程】基于HDL的FPGA邏輯設計與驗證教程V3.5.2.pdf 51 以太網相關通信接口詳解 52 以太網&#xff…

FPGA基礎 -- Verilog 共享任務(task)和函數(function)

Verilog 中共享任務&#xff08;task&#xff09;和函數&#xff08;function&#xff09; 的詳細專業培訓&#xff0c;適合具有一定 RTL 編程經驗的工程師深入掌握。 一、任務&#xff08;task&#xff09;與函數&#xff08;function&#xff09;的基本區別 特性taskfunctio…

學習大模型---需要掌握的數學知識

1. 線性代數&#xff1a;樂高積木的世界 想象你有很多樂高積木塊。線性代數就是研究怎么用這些積木塊搭建東西&#xff0c;以及這些搭建好的東西有什么特性的學問。 向量&#xff1a; 就像一個有方向的箭頭&#xff0c;或者一組排好隊的數字。比如&#xff1a; 一個箭頭&…

明遠智睿RK3506開發板:多核異構架構賦能高可靠性工業與商業應用

在工業4.0與物聯網&#xff08;IoT&#xff09;技術快速發展的背景下&#xff0c;嵌入式系統對性能、功耗、可靠性和實時性的要求日益嚴苛。針對這一趨勢&#xff0c;瑞芯微推出的RK3506開發板憑借其創新的三核A7單核M0多核異構架構、高能低耗設計以及豐富的外設資源&#xff0…

【AI時代速通QT】第二節:Qt SDK 的目錄介紹和第一個Qt Creator項目

目錄 一、認識 Qt SDK 的目錄結構 二、第一個 Qt 程序 2.1 Qt Creator 創建項目 2.2 介紹項目各文件 三、揭秘 Qt 的構建過程 四、運行項目與總結 &#x1f3ac; 攻城獅7號&#xff1a;個人主頁 &#x1f525; 個人專欄:CQT跨平臺界面編程 ?? 君子慎獨! &#x1f308…

CDH部署Hive詳細指南

CDH部署Hive詳細指南 本文將詳細介紹如何使用Cloudera Manager Web界面部署Hive組件,包括安裝、配置、優化和運維管理等內容。 1. 環境準備 1.1 系統要求 1.1.1 硬件要求 服務器配置 CPU:建議8核以上內存:建議32GB以上磁盤:建議使用企業級SAS或SSD網絡:建議萬兆網絡集…

黨建賦能 醫校協同|廣州附醫華南醫院與湖南中醫藥高等專科學校簽約攜手共育英才

為深入貫徹落實黨中央、國務院關于高校畢業生就業創業工作決策部署&#xff0c;教育部印發《職業學校校企合作促進辦法》&#xff0c;對深化醫教協同提供了政策指引。在醫學教育領域&#xff0c;鼓勵醫學院校與醫療機構開展深度合作&#xff0c;根據醫療行業需求調整專業設置與…

【RTSP從零實踐】2、使用RTP協議封裝并傳輸H264

&#x1f601;博客主頁&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客內容&#x1f911;&#xff1a;&#x1f36d;嵌入式開發、Linux、C語言、C、數據結構、音視頻&#x1f36d; &#x1f923;本文內容&#x1f923;&a…

行業熱點丨手機中框設計如何體現增材思維?

編者薦語&#xff1a; 通過增材設計思維在金屬邊框設計晶格結構&#xff0c;既能減輕重量&#xff0c;同時也有助于散熱&#xff0c;針對不同位置設計不同類型的晶格結構還能起到緩沖效果&#xff0c;提高手機抗沖擊能力。 以下文章來源于Inspire增材創新設計&#xff0c;作者…

鴻蒙案例實戰——添加水印

本示例為開發者展示常用的水印添加能力&#xff0c;包括兩種方式給頁面添加水印、保存圖片添加水印、拍照圖片添加水印和pdf文件添加水印。 案例效果截圖 首頁 頁面水印 圖片水印 pdf水印 案例運用到的知識點 核心知識點 頁面添加水印&#xff1a;封裝Canv…

Qt工作總結07 <qBound和std::clamp>

一、qBound簡介 1. 定義 是 Qt 框架中一個非常實用的邊界限制函數&#xff08;也稱為 "clamp" 函數&#xff09;&#xff0c;用于將一個值限制在指定的最小值和最大值之間。頭文件&#xff1a;#include <QtGlobal> 2. 函數原型 template <typename T>…

53-Oracle sqlhc多版本實操含(23 ai)

SQLHC&#xff08;SQL Health Check&#xff09;作為 Oracle 數據庫性能診斷的核心工具&#xff0c;其設計理念和核心功能在 Oracle 各版本中保持高度一致&#xff0c;但在技術實現和周邊生態上存在漸進式優化。定期對關鍵業務 SQL 執行健康檢查&#xff0c;特別是在版本升級或…

math.pow()和pow()的區別

math.pow() 和 pow() 的區別 ? 1. math.pow() 來自 math 模塊參數&#xff1a;兩個數&#xff08;底數&#xff0c;指數&#xff09;結果類型&#xff1a; 始終返回 float 類型 示例&#xff1a; import math print(math.pow(2, 3)) # 輸出&#xff1a;8.0 &#xff08;…