1. 主从配置
1.1 主机1(IP:192.168.186.77)
1.1.1 docker-compose.yml
version: '3.8'services:mysql-master:image: mysql:latestcontainer_name: mysql-masterenvironment:MYSQL_ROOT_PASSWORD: 123456MYSQL_USER: masterMYSQL_PASSWORD: 123456MYSQL_DATABASE: db1 ports:- "3306:3306"volumes:- mysql-master-data:/var/lib/mysqlcommand: --server-id=1 --log-bin=mysql-bin --binlog-format=ROWcap_add:- SYS_NICEsecurity_opt:- seccomp:unconfinedmysql-slave:image: mysql:latestcontainer_name: mysql-slaveenvironment:MYSQL_ROOT_PASSWORD: 123456MYSQL_USER: slaveMYSQL_PASSWORD: 123456MYSQL_DATABASE: db1ports:- "3307:3306"volumes:- mysql-slave-data:/var/lib/mysqlcommand: --server-id=2 --log-bin=mysql-bin --binlog-format=ROW --relay-log=relay-bin --relay-log-index=relay-bin.indexdepends_on:- mysql-mastercap_add:- SYS_NICEsecurity_opt:- seccomp:unconfinedvolumes:mysql-master-data:mysql-slave-data:
说明:端口3306充当主库,端口3307充当从库,创建的时候同时创建新用户和密码,如用户master,同时创建的时候也创建数据库db1。在MySQL 的主从复制架构中,每个实例都必须有一个唯一的 server-id
。即使实例位于不同的 IP 地址上,每个实例的 server-id
也必须是唯一的。
1.1.2 启动主从数据库
docker-compose up -d
1.1.3 主数据库用户授权
# 给mater用户授全部权限
GRANT ALL PRIVILEGES ON *.* TO 'master'@'%';
FLUSH PRIVILEGES;
1.1.4 配置主从数据库
1.1.4.1 配置主数据库
# 创建主从配置之间的用户
CREATE USER 'master_slave'@'%' IDENTIFIED BY '123456' REQUIRE SSL;
GRANT REPLICATION SLAVE ON *.* TO 'master_slave'@'%';
FLUSH PRIVILEGES;
# 查看FILE 和 Position 文件
SHOW MASTER STATUS;
说明:FILE 和 Position 是配置从数据库的关键,需要自行查看并修改对应坐标。
1.1.4.2 配置从数据库
CHANGE MASTER TOMASTER_HOST ='192.168.186.77', # 主服务器的 IP 地址MASTER_USER ='master_slave', # 主服务器上配置的复制用户MASTER_PASSWORD ='123456', # 复制用户的密码MASTER_LOG_FILE ='mysql-bin.000003', # 主服务器的日志文件名MASTER_LOG_POS =1358, # 日志文件的位置MASTER_SSL=1;
START SLAVE;
SHOW SLAVE STATUS;
说明:出现 Slave_IO_Running 和 Slave_SQL_Running 都是YES说明主从配置成功。
1.1.4.3 创建数据库表
use db1;
CREATE TABLE t_order_0
(order_id INT PRIMARY KEY,user_id INT,order_date DATE,status VARCHAR(255)
);CREATE TABLE t_order_1
(order_id INT PRIMARY KEY,user_id INT,order_date DATE,status VARCHAR(255)
);
说明 :需要在不同IP的主数据库上执行该数据库语句,创建真实物理表,从数据库会自动同步复制数据库的结构。ShardingSphere会创建逻辑数据库数据表根据分库规则实现分库分表。
注意:主机2和主机3的配置流程几乎相同,如果个人能力允许可以跳过该部分。
1.2 主机2(IP:192.168.186.216)
1.2.1 docker-compose.yml
version: '3.8'services:mysql-master:image: mysql:latestcontainer_name: mysql-masterenvironment:MYSQL_ROOT_PASSWORD: 123456MYSQL_USER: masterMYSQL_PASSWORD: 123456MYSQL_DATABASE: db2 ports:- "3306:3306"volumes:- mysql-master-data:/var/lib/mysqlcommand: --server-id=3 --log-bin=mysql-bin --binlog-format=ROWcap_add:- SYS_NICEsecurity_opt:- seccomp:unconfinedmysql-slave:image: mysql:latestcontainer_name: mysql-slaveenvironment:MYSQL_ROOT_PASSWORD: 123456MYSQL_USER: slaveMYSQL_PASSWORD: 123456MYSQL_DATABASE: db2 ports:- "3307:3306"volumes:- mysql-slave-data:/var/lib/mysqlcommand: --server-id=4 --log-bin=mysql-bin --binlog-format=ROW --relay-log=relay-bin --relay-log-index=relay-bin.indexdepends_on:- mysql-mastercap_add:- SYS_NICEsecurity_opt:- seccomp:unconfinedvolumes:mysql-master-data:mysql-slave-data:
说明:通过server-id
区分不同的主从数据库。
1.2.2 启动主从数据库
docker-compose up -d
1.2.3 主数据库用户授权
# 给mater用户授全部权限
GRANT ALL PRIVILEGES ON *.* TO 'master'@'%';
FLUSH PRIVILEGES;
1.2.4 配置主从数据库
1.2.4.1 配置主数据库
# 创建主从配置之间的用户
CREATE USER 'master_slave'@'%' IDENTIFIED BY '123456' REQUIRE SSL;
GRANT REPLICATION SLAVE ON *.* TO 'master_slave'@'%';
FLUSH PRIVILEGES;
# 查看FILE 和 Position 文件
SHOW MASTER STATUS;
说明:FILE 和 Position 是配置从数据库的关键,需要自行查看并修改对应坐标。
1.2.4.2 配置从数据库
CHANGE MASTER TOMASTER_HOST ='192.168.186.216', # 主服务器的 IP 地址MASTER_USER ='master_slave', # 主服务器上配置的复制用户MASTER_PASSWORD ='123456', # 复制用户的密码MASTER_LOG_FILE ='mysql-bin.000003', # 主服务器的日志文件名MASTER_LOG_POS =1781, # 日志文件的位置MASTER_SSL=1;
START SLAVE;
SHOW SLAVE STATUS;
说明:出现 Slave_IO_Running 和 Slave_SQL_Running 都是YES说明主从配置成功。
1.2.4.3 创建数据库表
use db2;
CREATE TABLE t_order_0
(order_id INT PRIMARY KEY,user_id INT,order_date DATE,status VARCHAR(255)
);CREATE TABLE t_order_1
(order_id INT PRIMARY KEY,user_id INT,order_date DATE,status VARCHAR(255)
);
说明 :需要在不同IP的主数据库上执行该数据库语句,创建真实物理表,从数据库会自动同步复制数据库的结构。ShardingSphere会创建逻辑数据库数据表根据分库规则实现分库分表。
1.3 主机3(IP:192.168.186.18)
1.3.1 docker-compose.yml
version: '3.8'services:mysql-master:image: mysql:latestcontainer_name: mysql-masterenvironment:MYSQL_ROOT_PASSWORD: 123456MYSQL_USER: masterMYSQL_PASSWORD: 123456MYSQL_DATABASE: db3 ports:- "3306:3306"volumes:- mysql-master-data:/var/lib/mysqlcommand: --server-id=5 --log-bin=mysql-bin --binlog-format=ROWcap_add:- SYS_NICEsecurity_opt:- seccomp:unconfinedmysql-slave:image: mysql:latestcontainer_name: mysql-slaveenvironment:MYSQL_ROOT_PASSWORD: 123456MYSQL_USER: slaveMYSQL_PASSWORD: 123456MYSQL_DATABASE: db3 ports:- "3307:3306"volumes:- mysql-slave-data:/var/lib/mysqlcommand: --server-id=6 --log-bin=mysql-bin --binlog-format=ROW --relay-log=relay-bin --relay-log-index=relay-bin.indexdepends_on:- mysql-mastercap_add:- SYS_NICEsecurity_opt:- seccomp:unconfinedvolumes:mysql-master-data:mysql-slave-data:
说明:通过server-id
不同的主从数据库。
1.3.2 启动主从数据库
docker-compose up -d
1.3.3 主数据库用户授权
# 给mater用户授全部权限
GRANT ALL PRIVILEGES ON *.* TO 'master'@'%';
FLUSH PRIVILEGES;
1.3.4 配置主从数据库
1.3.4.1 配置主数据库
# 创建主从配置之间的用户
CREATE USER 'master_slave'@'%' IDENTIFIED BY '123456' REQUIRE SSL;
GRANT REPLICATION SLAVE ON *.* TO 'master_slave'@'%';
FLUSH PRIVILEGES;
# 查看FILE 和 Position 文件
SHOW MASTER STATUS;
说明:FILE 和 Position 是配置从数据库的关键,需要自行查看并修改对应坐标。
1.3.4.2 配置从数据库
说明:出现 Slave_IO_Running 和 Slave_SQL_Running 都是YES说明主从配置成功。
1.3.4.3 创建数据库表
use db3;
CREATE TABLE t_order_0
(order_id INT PRIMARY KEY,user_id INT,order_date DATE,status VARCHAR(255)
);CREATE TABLE t_order_1
(order_id INT PRIMARY KEY,user_id INT,order_date DATE,status VARCHAR(255)
);
1.4 其他
ERROR:
Coordinator stopped because there were error(s) in the worker(s). The most recent failure being: Worker 1 failed executing transaction 'ANONYMOUS' at master log mysql-bin.000003, end_log_pos 3890. See error log and/or performance_schema.replication_applier_status_by_worker table for more details about this failure or others, if any.
如果遇到主从复制错误(我遇到是给用户重新授权的时候出现的),可以尝试以下解决方法:
-- 停止从库复制
STOP SLAVE;-- 跳过一个错误的事务
SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1;-- 重新启动从库复制
START SLAVE;
2. ShardingSphere-JDBC
2.1 项目结构
2.2 Maven依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.3.2</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>org.example</groupId><artifactId>ShardingSphere-JDBC</artifactId><version>0.0.1-SNAPSHOT</version><properties><java.version>17</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope></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></dependency><!-- https://mvnrepository.com/artifact/org.apache.shardingsphere/shardingsphere-jdbc --><dependency><groupId>org.apache.shardingsphere</groupId><artifactId>shardingsphere-jdbc</artifactId><version>5.5.0</version><exclusions><exclusion><groupId>org.apache.shardingsphere</groupId><artifactId>shardingsphere-test-util</artifactId></exclusion></exclusions></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>
2.3 Order.java
package org.example.entity;import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
import java.util.Date;@Entity
@Table(name = "t_order")
@Data
public class Order {@Id@Column(name = "order_id")private int orderId;@Column(name = "user_id")private int userId;@Column(name = "order_date")private Date orderDate;@Column(name = "status")private String status;
}
2.4 orderRepository.java
package org.example.repository;import org.example.entity.Order;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;@Repository
public interface orderRepository extends JpaRepository<Order, Long> {
}
2.5 ShardingSphereConfig.java
package org.example.config;import org.apache.shardingsphere.driver.api.yaml.YamlShardingSphereDataSourceFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;import javax.sql.DataSource;import java.io.IOException;
import java.sql.SQLException;@Configuration
public class ShardingSphereConfig{@Value("shardingsphere.yaml")private ClassPathResource config;@Beanpublic DataSource dataSource() throws SQLException, IOException {return YamlShardingSphereDataSourceFactory.createDataSource(config.getContentAsByteArray());}
}
说明:通过@Value注入shardingsphere.yaml并创建dataSource的Bean。
数据分片 :: ShardingSphere
2.6 application.yaml
spring:application:name: ShardingSphere-JDBC
2.7 logback.xml
<configuration><!-- 定义过滤器,用于过滤出包含 "Logic SQL:" 或 "Actual SQL:" 的日志消息 --><appender name="SQL_FILTER" class="ch.qos.logback.core.ConsoleAppender"><encoder><!-- 只打印消息部分 --><pattern>%msg%n</pattern></encoder><filter class="ch.qos.logback.core.filter.EvaluatorFilter"><evaluator class="ch.qos.logback.classic.boolex.JaninoEventEvaluator"><expression>return message.contains("Logic SQL:") || message.contains("Actual SQL:");</expression></evaluator><OnMismatch>DENY</OnMismatch><OnMatch>ACCEPT</OnMatch></filter></appender><!-- 配置针对 ShardingSphere-SQL 的日志输出 --><logger name="ShardingSphere-SQL" level="INFO" additivity="false"><appender-ref ref="SQL_FILTER" /></logger>
</configuration>
2.8 shardingsphere.yaml
2.8.1 逻辑数据库名称
databaseName: my_database # 数据库配置名称。
2.8.2 数据源配置
dataSources:ds_77_master:# 主数据库服务器的配置,IP 为 192.168.186.77,端口为 3306。dataSourceClassName: com.zaxxer.hikari.HikariDataSource# HikariCP 数据源的完整类名。url: jdbc:mysql://192.168.186.77:3306/db1?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true# 用于连接 MySQL 数据库的 JDBC URL。# serverTimezone=UTC 确保服务器时区设置为 UTC。# useSSL=false 禁用 SSL。# allowPublicKeyRetrieval=true 允许客户端从服务器检索公钥。username: master# 用于连接数据库的用户名。password: 123456# 数据库用户的密码。connectionTimeoutMilliseconds: 30000# 客户端等待从连接池获取连接的最大时间(以毫秒为单位)。# 默认值为 30000 毫秒(30 秒)。idleTimeoutMilliseconds: 60000# 连接在连接池中闲置的最长时间(以毫秒为单位)。# 默认值为 600000 毫秒(10 分钟)。maxLifetimeMilliseconds: 1800000# 连接在连接池中的最长存活时间(以毫秒为单位)。# 默认值为 1800000 毫秒(30 分钟)。maxPoolSize: 50# 连接池将维持的最大连接数。# 默认值为 10。minPoolSize: 1# 连接池将维持的最小闲置连接数。# 默认值为 1。ds_77_slave:# 从数据库服务器的配置,IP 为 192.168.186.77,端口为 3307。dataSourceClassName: com.zaxxer.hikari.HikariDataSourceurl: jdbc:mysql://192.168.186.77:3307/db1?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=trueusername: slavepassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1ds_216_master:# 主数据库服务器的配置,IP 为 192.168.186.216,端口为 3306。dataSourceClassName: com.zaxxer.hikari.HikariDataSourceurl: jdbc:mysql://192.168.186.216:3306/db2?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=trueusername: masterpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1ds_216_slave:# 从数据库服务器的配置,IP 为 192.168.186.216,端口为 3307。dataSourceClassName: com.zaxxer.hikari.HikariDataSourceurl: jdbc:mysql://192.168.186.216:3307/db2?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=trueusername: slavepassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1ds_18_master:# 主数据库服务器的配置,IP 为 192.168.186.18,端口为 3306。dataSourceClassName: com.zaxxer.hikari.HikariDataSourceurl: jdbc:mysql://192.168.186.18:3306/db3?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=trueusername: masterpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1ds_18_slave:# 从数据库服务器的配置,IP 为 192.168.186.18,端口为 3307。dataSourceClassName: com.zaxxer.hikari.HikariDataSourceurl: jdbc:mysql://192.168.186.18:3307/db3?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=trueusername: slavepassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1
2.8.3 读写分离规则
rules:- !READWRITE_SPLITTING# 配置读写分离规则。dataSources:# 定义数据源组。readwrite_77:# 名为 readwrite_77 的读写分离数据源组配置。writeDataSourceName: ds_77_master # 写操作的数据源名称,指向 ds_77_master 数据源。readDataSourceNames: # 读操作的数据源名称列表,这里指定为 ds_77_slave 数据源。- ds_77_slavetransactionalReadQueryStrategy: DYNAMIC # 事务性读查询策略,DYNAMIC 表示根据实际情况动态选择数据源。loadBalancerName: random # 负载均衡器名称,指向名为 random 的负载均衡策略。readwrite_216:# 名为 readwrite_216 的读写分离数据源组配置。writeDataSourceName: ds_216_master # 写操作的数据源名称,指向 ds_216_master 数据源。readDataSourceNames: # 读操作的数据源名称列表,这里指定为 ds_216_slave 数据源。- ds_216_slavetransactionalReadQueryStrategy: DYNAMIC # 事务性读查询策略,DYNAMIC 表示根据实际情况动态选择数据源。loadBalancerName: random # 负载均衡器名称,指向名为 random 的负载均衡策略。readwrite_18:# 名为 readwrite_18 的读写分离数据源组配置。writeDataSourceName: ds_18_master # 写操作的数据源名称,指向 ds_18_master 数据源。readDataSourceNames: # 读操作的数据源名称列表,这里指定为 ds_18_slave 数据源。- ds_18_slavetransactionalReadQueryStrategy: DYNAMIC # 事务性读查询策略,DYNAMIC 表示根据实际情况动态选择数据源。loadBalancerName: random # 负载均衡器名称,指向名为 random 的负载均衡策略。loadBalancers:# 定义负载均衡策略。random:# 名为 random 的负载均衡策略配置。type: RANDOM # 负载均衡策略类型,这里设置为随机(RANDOM)。
说明:transactionalReadQueryStrategy对应参数 PRIMARY
、FIXED
和 DYNAMIC
对应不同的读写分离策略:
-
PRIMARY: 只从主库读取。这通常用于需要确保读取到最新数据的情况。
-
FIXED: 固定从库读取。这种情况下会根据配置的从库列表选择特定的从库读取数据。
-
DYNAMIC: 动态从库读取。根据负载均衡策略动态选择从库读取数据。
如果想让读操作从从库读取,可以配置 FIXED
或 DYNAMIC
策略。
2.8.4 分片规则
- !SHARDING# 配置分片规则。tables:t_order:# 针对表 t_order 的分片配置。actualDataNodes: readwrite_77.t_order_${0..1}, readwrite_216.t_order_${0..1}, readwrite_18.t_order_${0..1}# 实际数据节点,表示 t_order 表在三个数据源组(readwrite_77, readwrite_216, readwrite_18)的分片情况。tableStrategy:standard:shardingColumn: order_id # 分片键为 order_id。shardingAlgorithmName: t_order_inline # 分片算法名称为 t_order_inline。keyGenerateStrategy:column: order_id # 主键生成策略,针对 order_id 列。keyGeneratorName: snowflake # 主键生成器名称为 snowflake。defaultDatabaseStrategy:standard:# 默认数据库策略。shardingColumn: user_id # 分片键为 user_id。shardingAlgorithmName: database_inline # 分片算法名称为 database_inline。defaultTableStrategy:none: # 默认表策略为 none,表示不进行默认表分片。bindingTables:- t_order # 绑定表配置,表示 t_order 是一个绑定表。shardingAlgorithms:database_inline:# 分片算法名称为 database_inline。type: INLINE # 算法类型为 INLINE。props:algorithm-expression: "readwrite_${(user_id % 3 == 0) ? '77' : ((user_id % 3 == 1) ? '216' : '18')}"# 算法表达式,根据 user_id 的值将数据分片到不同的数据源组:# 如果 user_id % 3 == 0,则选择 readwrite_77;# 如果 user_id % 3 == 1,则选择 readwrite_216;# 否则选择 readwrite_18。t_order_inline:# 分片算法名称为 t_order_inline。type: INLINE # 算法类型为 INLINE。props:algorithm-expression: "t_order_${order_id % 2}"# 算法表达式,根据 order_id 的值将数据分片到 t_order_0 或 t_order_1:# 如果 order_id % 2 == 0,则选择 t_order_0;# 否则选择 t_order_1。keyGenerators:snowflake:# 主键生成器名称为 snowflake。type: SNOWFLAKE # 主键生成器类型为 SNOWFLAKE。props:worker-id: 123 # SNOWFLAKE 算法的 worker-id 配置,值为 123。- !ENCRYPT# 配置加密规则。encryptors:aes_encryptor:# 加密器名称为 aes_encryptor。type: AES # 加密器类型为 AES(对称加密算法)。props:aes-key-value: 123456abc # AES 加密的密钥值,必须是长度为 16、24 或 32 字节的密钥。tables:t_order:# 针对表 t_order 的加密配置。columns:status:# 针对 status 列进行加密配置。cipher:name: status # 加密后列的名称仍为 status。encryptorName: aes_encryptor # 使用名称为 aes_encryptor 的加密器进行加密。props:sql-show: true # 显示生成的 SQL 语句,便于调试。
2.8.5 加密规则
- !ENCRYPT# 配置加密规则。encryptors:aes_encryptor:# 加密器名称为 aes_encryptor。type: AES # 加密器类型为 AES(对称加密算法)。props:aes-key-value: 123456abc # AES 加密的密钥值,必须是长度为 16、24 或 32 字节的密钥。tables:t_order:# 针对表 t_order 的加密配置。columns:status:# 针对 status 列进行加密配置。cipher:name: status # 加密后列的名称仍为 status。encryptorName: aes_encryptor # 使用名称为 aes_encryptor 的加密器进行加密。
2.8.6 其他
props:sql-show: true # 显示生成的 SQL 语句,便于调试。
2.8.7 完整文件
databaseName: my_database
dataSources:ds_77_master:dataSourceClassName: com.zaxxer.hikari.HikariDataSourceurl: jdbc:mysql://192.168.186.77:3306/db1?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=trueusername: masterpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1ds_77_slave:dataSourceClassName: com.zaxxer.hikari.HikariDataSourceurl: jdbc:mysql://192.168.186.77:3307/db1?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=trueusername: slavepassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1ds_216_master:dataSourceClassName: com.zaxxer.hikari.HikariDataSourceurl: jdbc:mysql://192.168.186.216:3306/db2?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=trueusername: masterpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1ds_216_slave:dataSourceClassName: com.zaxxer.hikari.HikariDataSourceurl: jdbc:mysql://192.168.186.216:3307/db2?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=trueusername: slavepassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1ds_18_master:dataSourceClassName: com.zaxxer.hikari.HikariDataSourceurl: jdbc:mysql://192.168.186.18:3306/db3?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=trueusername: masterpassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1ds_18_slave:dataSourceClassName: com.zaxxer.hikari.HikariDataSourceurl: jdbc:mysql://192.168.186.18:3307/db3?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=trueusername: slavepassword: 123456connectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 1
rules:- !READWRITE_SPLITTINGdataSources:readwrite_77:writeDataSourceName: ds_77_masterreadDataSourceNames:- ds_77_slavetransactionalReadQueryStrategy: DYNAMICloadBalancerName: randomreadwrite_216:writeDataSourceName: ds_216_masterreadDataSourceNames:- ds_216_slavetransactionalReadQueryStrategy: DYNAMICloadBalancerName: randomreadwrite_18:writeDataSourceName: ds_18_masterreadDataSourceNames:- ds_18_slavetransactionalReadQueryStrategy: DYNAMICloadBalancerName: randomloadBalancers:random:type: RANDOM- !SHARDINGtables:t_order:actualDataNodes: readwrite_77.t_order_${0..1}, readwrite_216.t_order_${0..1}, readwrite_18.t_order_${0..1}tableStrategy:standard:shardingColumn: order_idshardingAlgorithmName: t_order_inlinekeyGenerateStrategy:column: order_idkeyGeneratorName: snowflakedefaultDatabaseStrategy:standard:shardingColumn: user_idshardingAlgorithmName: database_inlinedefaultTableStrategy:none:bindingTables:- t_ordershardingAlgorithms:database_inline:type: INLINEprops:algorithm-expression: "readwrite_${(user_id % 3 == 0) ? '77' : ((user_id % 3 == 1) ? '216' : '18')}"t_order_inline:type: INLINEprops:algorithm-expression: "t_order_${order_id % 2}"keyGenerators:snowflake:type: SNOWFLAKEprops:worker-id: 123- !ENCRYPTencryptors:aes_encryptor:type: AESprops:aes-key-value: 123456abctables:t_order:columns:status:cipher:name: statusencryptorName: aes_encryptor
props:sql-show: true
2.9 ShardingSphereJdbcApplicationTests.java
package org.example;import org.example.entity.Order;
import org.example.repository.orderRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Date;@SpringBootTest
class ShardingSphereJdbcApplicationTests {@AutowiredorderRepository repo;@Testvoid contextLoads() {repo.findAll().forEach(System.out::println);}@Testvoid insert(){Order newOrder = new Order();newOrder.setOrderId(1);newOrder.setUserId(10);newOrder.setOrderDate(new Date());newOrder.setStatus("INSERT");repo.save(newOrder);System.out.println("Inserted Order: " + newOrder);}@Testvoid delete(){repo.deleteById(1L);}
}
2.10 验证结果
2.10.1 分片结果
# 根据 user_id 的值将数据分片到不同的数据源组:
# 如果 user_id % 3 == 0,则选择 readwrite_77;
# 如果 user_id % 3 == 1,则选择 readwrite_216;
# 否则选择 readwrite_18。# 根据 order_id 的值将数据分片到 t_order_0 或 t_order_1:
# 如果 order_id % 2 == 0,则选择 t_order_0;
# 否则选择 t_order_1。Logic SQL: insert into t_order (order_date,status,user_id,order_id) values (?,?,?,?)
Actual SQL: ds_216_master ::: insert into t_order_1 (order_date,status,user_id,order_id) values (?, ?, ?, ?) ::: [2024-07-26 10:05:21.914, 1jyA91B85/gs5gPPkYF3WA==, 10, 1]
Inserted Order: Order(orderId=1, userId=10, orderDate=Fri Jul 26 10:05:21 CST 2024, status=INSERT)解释:10%3=1,1%2=1,所以位于主机2(ds_216_master )的数据库, t_order_1表。
2.10.2 加密结果
Logic SQL: insert into t_order (order_date,status,user_id,order_id) values (?,?,?,?)
Actual SQL: ds_216_master ::: insert into t_order_1 (order_date,status,user_id,order_id) values (?, ?, ?, ?) ::: [2024-07-26 10:05:21.914, 1jyA91B85/gs5gPPkYF3WA==, 10, 1]
Inserted Order: Order(orderId=1, userId=10, orderDate=Fri Jul 26 10:05:21 CST 2024, status=INSERT)
2.10.3 读写分离结果
Logic SQL: select o1_0.order_id,o1_0.order_date,o1_0.status,o1_0.user_id from t_order o1_0
Actual SQL: ds_77_slave ::: select o1_0.order_id,o1_0.order_date,o1_0.status AS status,o1_0.user_id from t_order_0 o1_0 UNION ALL select o1_0.order_id,o1_0.order_date,o1_0.status AS status,o1_0.user_id from t_order_1 o1_0
Actual SQL: ds_216_slave ::: select o1_0.order_id,o1_0.order_date,o1_0.status AS status,o1_0.user_id from t_order_0 o1_0 UNION ALL select o1_0.order_id,o1_0.order_date,o1_0.status AS status,o1_0.user_id from t_order_1 o1_0
Actual SQL: ds_18_slave ::: select o1_0.order_id,o1_0.order_date,o1_0.status AS status,o1_0.user_id from t_order_0 o1_0 UNION ALL select o1_0.order_id,o1_0.order_date,o1_0.status AS status,o1_0.user_id from t_order_1 o1_0
Order(orderId=1, userId=10, orderDate=2024-07-26 00:00:00.0, status=INSERT)
3. 总结
通过Docker-Compose启动3对IP不同的主从数据库,通过JPA和默认的HikariDataSource数据源结合MySQL将ShardingSphere-JDBC整合到SpringBoot框架,实现了读写分离,分库分片,以及加密和加密对特定的字段,仅供学习交流,不具备严谨性。