1. 主从配置
1.1 主机1(IP:192.168.186.77)
1.1.1 docker-compose.yml
version: '3.8'
services:
mysql-master:
image: mysql:latest
container_name: mysql-master
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_USER: master
MYSQL_PASSWORD: 123456
MYSQL_DATABASE: db1
ports:
- "3306:3306"
volumes:
- mysql-master-data:/var/lib/mysql
command: --server-id=1 --log-bin=mysql-bin --binlog-format=ROW
cap_add:
- SYS_NICE
security_opt:
- seccomp:unconfined
mysql-slave:
image: mysql:latest
container_name: mysql-slave
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_USER: slave
MYSQL_PASSWORD: 123456
MYSQL_DATABASE: db1
ports:
- "3307:3306"
volumes:
- mysql-slave-data:/var/lib/mysql
command: --server-id=2 --log-bin=mysql-bin --binlog-format=ROW --relay-log=relay-bin --relay-log-index=relay-bin.index
depends_on:
- mysql-master
cap_add:
- SYS_NICE
security_opt:
- seccomp:unconfined
volumes:
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 TO
MASTER_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:latest
container_name: mysql-master
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_USER: master
MYSQL_PASSWORD: 123456
MYSQL_DATABASE: db2
ports:
- "3306:3306"
volumes:
- mysql-master-data:/var/lib/mysql
command: --server-id=3 --log-bin=mysql-bin --binlog-format=ROW
cap_add:
- SYS_NICE
security_opt:
- seccomp:unconfined
mysql-slave:
image: mysql:latest
container_name: mysql-slave
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_USER: slave
MYSQL_PASSWORD: 123456
MYSQL_DATABASE: db2
ports:
- "3307:3306"
volumes:
- mysql-slave-data:/var/lib/mysql
command: --server-id=4 --log-bin=mysql-bin --binlog-format=ROW --relay-log=relay-bin --relay-log-index=relay-bin.index
depends_on:
- mysql-master
cap_add:
- SYS_NICE
security_opt:
- seccomp:unconfined
volumes:
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 TO
MASTER_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:latest
container_name: mysql-master
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_USER: master
MYSQL_PASSWORD: 123456
MYSQL_DATABASE: db3
ports:
- "3306:3306"
volumes:
- mysql-master-data:/var/lib/mysql
command: --server-id=5 --log-bin=mysql-bin --binlog-format=ROW
cap_add:
- SYS_NICE
security_opt:
- seccomp:unconfined
mysql-slave:
image: mysql:latest
container_name: mysql-slave
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_USER: slave
MYSQL_PASSWORD: 123456
MYSQL_DATABASE: db3
ports:
- "3307:3306"
volumes:
- mysql-slave-data:/var/lib/mysql
command: --server-id=6 --log-bin=mysql-bin --binlog-format=ROW --relay-log=relay-bin --relay-log-index=relay-bin.index
depends_on:
- mysql-master
cap_add:
- SYS_NICE
security_opt:
- seccomp:unconfined
volumes:
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;
@Bean
public DataSource dataSource() throws SQLException, IOException {
return YamlShardingSphereDataSourceFactory.createDataSource(config.getContentAsByteArray());
}
}
说明:通过@Value注入shardingsphere.yaml并创建dataSource的Bean。
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.HikariDataSource
url: jdbc:mysql://192.168.186.77:3307/db1?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
username: slave
password: 123456
connectionTimeoutMilliseconds: 30000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
minPoolSize: 1
ds_216_master:
# 主数据库服务器的配置,IP 为 192.168.186.216,端口为 3306。
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
url: jdbc:mysql://192.168.186.216:3306/db2?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
username: master
password: 123456
connectionTimeoutMilliseconds: 30000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
minPoolSize: 1
ds_216_slave:
# 从数据库服务器的配置,IP 为 192.168.186.216,端口为 3307。
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
url: jdbc:mysql://192.168.186.216:3307/db2?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
username: slave
password: 123456
connectionTimeoutMilliseconds: 30000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
minPoolSize: 1
ds_18_master:
# 主数据库服务器的配置,IP 为 192.168.186.18,端口为 3306。
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
url: jdbc:mysql://192.168.186.18:3306/db3?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
username: master
password: 123456
connectionTimeoutMilliseconds: 30000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
minPoolSize: 1
ds_18_slave:
# 从数据库服务器的配置,IP 为 192.168.186.18,端口为 3307。
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
url: jdbc:mysql://192.168.186.18:3307/db3?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
username: slave
password: 123456
connectionTimeoutMilliseconds: 30000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
minPoolSize: 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_slave
transactionalReadQueryStrategy: DYNAMIC # 事务性读查询策略,DYNAMIC 表示根据实际情况动态选择数据源。
loadBalancerName: random # 负载均衡器名称,指向名为 random 的负载均衡策略。
readwrite_216:
# 名为 readwrite_216 的读写分离数据源组配置。
writeDataSourceName: ds_216_master # 写操作的数据源名称,指向 ds_216_master 数据源。
readDataSourceNames: # 读操作的数据源名称列表,这里指定为 ds_216_slave 数据源。
- ds_216_slave
transactionalReadQueryStrategy: DYNAMIC # 事务性读查询策略,DYNAMIC 表示根据实际情况动态选择数据源。
loadBalancerName: random # 负载均衡器名称,指向名为 random 的负载均衡策略。
readwrite_18:
# 名为 readwrite_18 的读写分离数据源组配置。
writeDataSourceName: ds_18_master # 写操作的数据源名称,指向 ds_18_master 数据源。
readDataSourceNames: # 读操作的数据源名称列表,这里指定为 ds_18_slave 数据源。
- ds_18_slave
transactionalReadQueryStrategy: 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.HikariDataSource
url: jdbc:mysql://192.168.186.77:3306/db1?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
username: master
password: 123456
connectionTimeoutMilliseconds: 30000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
minPoolSize: 1
ds_77_slave:
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
url: jdbc:mysql://192.168.186.77:3307/db1?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
username: slave
password: 123456
connectionTimeoutMilliseconds: 30000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
minPoolSize: 1
ds_216_master:
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
url: jdbc:mysql://192.168.186.216:3306/db2?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
username: master
password: 123456
connectionTimeoutMilliseconds: 30000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
minPoolSize: 1
ds_216_slave:
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
url: jdbc:mysql://192.168.186.216:3307/db2?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
username: slave
password: 123456
connectionTimeoutMilliseconds: 30000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
minPoolSize: 1
ds_18_master:
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
url: jdbc:mysql://192.168.186.18:3306/db3?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
username: master
password: 123456
connectionTimeoutMilliseconds: 30000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
minPoolSize: 1
ds_18_slave:
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
url: jdbc:mysql://192.168.186.18:3307/db3?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
username: slave
password: 123456
connectionTimeoutMilliseconds: 30000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
minPoolSize: 1
rules:
- !READWRITE_SPLITTING
dataSources:
readwrite_77:
writeDataSourceName: ds_77_master
readDataSourceNames:
- ds_77_slave
transactionalReadQueryStrategy: DYNAMIC
loadBalancerName: random
readwrite_216:
writeDataSourceName: ds_216_master
readDataSourceNames:
- ds_216_slave
transactionalReadQueryStrategy: DYNAMIC
loadBalancerName: random
readwrite_18:
writeDataSourceName: ds_18_master
readDataSourceNames:
- ds_18_slave
transactionalReadQueryStrategy: DYNAMIC
loadBalancerName: random
loadBalancers:
random:
type: RANDOM
- !SHARDING
tables:
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_id
shardingAlgorithmName: t_order_inline
keyGenerateStrategy:
column: order_id
keyGeneratorName: snowflake
defaultDatabaseStrategy:
standard:
shardingColumn: user_id
shardingAlgorithmName: database_inline
defaultTableStrategy:
none:
bindingTables:
- t_order
shardingAlgorithms:
database_inline:
type: INLINE
props:
algorithm-expression: "readwrite_${(user_id % 3 == 0) ? '77' : ((user_id % 3 == 1) ? '216' : '18')}"
t_order_inline:
type: INLINE
props:
algorithm-expression: "t_order_${order_id % 2}"
keyGenerators:
snowflake:
type: SNOWFLAKE
props:
worker-id: 123
- !ENCRYPT
encryptors:
aes_encryptor:
type: AES
props:
aes-key-value: 123456abc
tables:
t_order:
columns:
status:
cipher:
name: status
encryptorName: 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 {
@Autowired
orderRepository repo;
@Test
void contextLoads() {
repo.findAll().forEach(System.out::println);
}
@Test
void 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);
}
@Test
void 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框架,实现了读写分离,分库分片,以及加密和加密对特定的字段,仅供学习交流,不具备严谨性。