大纲
前言
学习资源
ShardingSphere-Proxy 安装
安装方式
目前 ShardingSphere-Proxy 官方提供了三种安装方式:
安装步骤
ShardingSphere-Proxy 配置
两种配置方式
- ShardingSphere-Proxy 支持两种规则配置与治理方式:
- YAML 配置文件,适用于启动期静态配置。
- DistSQL(Distributed SQL),适用于运行期动态治理。
DistSQL 的介绍
DistSQL 的三大语法
RDL(Resource Definition Language,资源定义语法)
- 作用:
- 特点:
- 面向 ShardingSphere-Proxy 运行时的资源管理
- 对应 YAML 中的
dataSources 配置 - 可在线添加、修改、删除资源,无需重启 ShardingSphere-Proxy
- 用途:
- 创建 / 删除数据源
- 修改数据源属性(如连接池大小)
- 创建 / 修改分片规则或读写分离规则
RQL(Resource Query Language,资源查询语法)
- 作用:
- 查询 ShardingSphere-Proxy 内部资源、规则和状态信息
- 特点:
- 类似 SQL 查询语法,但不操作业务数据
- 返回 ShardingSphere-Proxy 内部元数据
- 用于监控和验证配置是否生效
- 用途:
- 查询已配置的数据源
- 查询读写分离规则
- 查询分片规则
- 查看负载均衡算法
RAL(Resource Administration Language,资源管理语法)
- 作用:
- 管理 ShardingSphere-Proxy 运行时状态和执行操作
- 特点:
- 类似运维命令
- 可动态调整 ShardingSphere-Proxy 规则和资源
- 不影响业务 SQL 执行,不访问后端数据
- 用途:
- 启用 / 禁用数据源
- 刷新表或分片规则元数据
- 修改读写分离规则的负载均衡算法
- 在线调整分片表规则
| 语法 | 作用 | 核心命令示例 | 对应 YAML 配置 |
|---|
| RDL 语法 | 定义 / 修改资源(数据源、分片表、规则) | ADD RESOURCE / CREATE SHARDING TABLE RULE | dataSources / rules |
| RQL 语法 | 查询资源 / 规则状态 | SHOW RESOURCES / SHOW READWRITE_SPLITTING RULES | 无直接 YAML 对应,用于验证 |
| RAL 语法 | 管理运行时状态 | ENABLE/DISABLE RESOURCE / REFRESH TABLE METADATA | 对应 YAML 中规则的动态修改 |
DistSQL 与 YAML 的关系
| 场景 | 推荐方式 |
|---|
| 初始启动 | YAML |
| 运行期调整 | DistSQL |
| 生产环境 | YAML + DistSQL |
| 动态治理 | DistSQL |
DistSQL 与普通 SQL 的区别
| 对比项 | 普通 SQL | DistSQL |
|---|
| 操作对象 | 表 / 行 | 规则 / 数据源 / 算法 |
| 执行位置 | 数据库 | ShardingSphere-Proxy 内部 |
| 是否影响业务数据 | 不影响 | 不影响 |
| 是否持久化 | 数据表 | ShardingSphere-Proxy 元数据 |
| 是否需要重启 | 不需要重启 | 不需要重启 |
ShardingSphere-Proxy 使用
读写分离使用案例
提示
本节将演示 SpringBoot + MyBatis Plus 如何基于 ShardingSphere-Proxy 实现 读写分离,整体架构如下图所示。完整的案例代码可以直接从 GitHub 下载对应章节 sharding-sphere-lesson-07。
![]()
准备工作
版本说明
本案例所使用的组件版本如下表所示:
| 组件 | 版本说明 |
|---|
| JDK | 11 |
| MySQL | 8.0.29 |
| SpringBoot | 2.7.18 |
| ShardingSphere-Proxy | 5.1.1 |
数据库规划
本案例是在 MySQL 一主二从的架构(请自行搭建 MySQL 主从复制环境)上实现的,数据库的规划如下图所示:
![]()
| 数据库服务器 | 主从角色 | IP | 端口 | 库的名称 | 表的名称 |
|---|
| 主服务器 | 主库(master) | 192.168.2.191 | 3306 | db_user | t_user |
| 从服务器一 | 从库(slave1) | 192.168.2.191 | 3307 | db_user | t_user |
| 从服务器二 | 从库(slave2) | 192.168.2.191 | 3308 | db_user | t_user |
| ShardingSphere-Proxy 服务器 | | 192.168.2.191 | 3309 | readwrite_splitting_db | t_user |
MySQL 主从同步环境搭建
MySQL 基于日志点实现主从复制的教程可以看 这里。
数据库初始化
- 在 MySQL 主库(
master)中,执行以下 SQL 语句:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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);
|
ShardingSphere-Proxy 配置
读写分离配置
ShardingSphere-Proxy 支持两种配置模式,包括 YAML 和 DistSQL,任意选择一种即可。
YAML 模式
- 创建或编辑 ShardingSphere-Proxy 的
conf/config-readwrite-splitting.yaml 配置文件,添加以下读写分离的配置内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| schemaName: readwrite_splitting_db
dataSources: write_ds: url: jdbc:mysql://192.168.2.191: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.2.191: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.2.191: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
|
- ShardingSphere-Proxy 读写分离的配置内容添加完成后,需要重启 ShardingSphere-Proxy 服务(假设这里是通过 Docker 部署 ShardingSphere-Proxy 服务),否则配置不会生效
1 2
| docker restart server-proxy
|
DistSQL 模式
1 2 3 4 5
| CREATE DATABASE readwrite_splitting_db;
USE readwrite_splitting_db;
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| ADD RESOURCE write_ds ( URL="jdbc:mysql://192.168.2.191:3306/db_user?serverTimezone=UTC&useSSL=false", USER=root, PASSWORD=123456, PROPERTIES( "connectionTimeoutMilliseconds"="30000", "idleTimeoutMilliseconds"="60000", "maxLifetimeMilliseconds"="1800000", "maxPoolSize"="50", "minPoolSize"="1" ) );
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| ADD RESOURCE read_ds_0 ( URL="jdbc:mysql://192.168.2.191:3307/db_user?serverTimezone=UTC&useSSL=false", USER=root, PASSWORD=123456, PROPERTIES( "connectionTimeoutMilliseconds"="30000", "idleTimeoutMilliseconds"="60000", "maxLifetimeMilliseconds"="1800000", "maxPoolSize"="50", "minPoolSize"="1" ) );
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| ADD RESOURCE read_ds_1 ( URL="jdbc:mysql://192.168.2.191:3308/db_user?serverTimezone=UTC&useSSL=false", USER=root, PASSWORD=123456, PROPERTIES( "connectionTimeoutMilliseconds"="30000", "idleTimeoutMilliseconds"="60000", "maxLifetimeMilliseconds"="1800000", "maxPoolSize"="50", "minPoolSize"="1" ) );
|
- 创建读写分离规则(
TYPE 用于指定负载均衡算法,可选:random、round_robin、weight)
1 2 3 4 5
| CREATE READWRITE_SPLITTING RULE readwrite_ds ( WRITE_RESOURCE=write_ds, READ_RESOURCES(read_ds_0, read_ds_1), TYPE(NAME=random) );
|
1 2 3 4 5
| SHOW SCHEMA RESOURCES;
SHOW READWRITE_SPLITTING RULES;
|
- 使用 DistSQL 定义数据源和读写分离规则后,不需要重启 ShardingSphere-Proxy 服务,默认会自动生效
查看日志信息
- 查看 ShardingSphere-Proxy 运行的实时日志信息
1 2 3 4 5
| docker exec -it server-proxy env LANG=C.UTF-8 /bin/bash
tail -f /opt/shardingsphere-proxy/logs/stdout.log
|
远程访问测试
- 远程连接 ShardingSphere-Proxy
1
| mysql -h192.168.2.191 -P3309 -uroot -p
|
1 2 3 4 5 6 7 8 9 10 11
| mysql> show databases; + | schema_name | + | readwrite_splitting_db | | mysql | | information_schema | | performance_schema | | sys | + 5 rows in set (0.01 sec)
|
1
| mysql> use readwrite_splitting_db;
|
1 2 3 4 5 6 7
| mysql> show tables; +----------------------------------+------------+ | Tables_in_readwrite_splitting_db | Table_type | +----------------------------------+------------+ | t_user | BASE TABLE | +----------------------------------+------------+ 1 row in set (0.01 sec)
|
1
| mysql> insert into t_user(uname) values('wang5');
|
1
| mysql> select * from t_user;
|
1
| mysql> select * from t_user;
|
提示
执行完上述 insert、select 操作后,可以在 ShardingSphere-Proxy 的日志信息中,查看对应的逻辑 SQL 和真实 SQL,以此判断读写分离是否生效。
SpringBoot 实战使用案例代码
案例代码
添加依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| <properties> <spring-boot.version>2.7.18</spring-boot.version> <mysql-connector.version>8.2.0</mysql-connector.version> <mybatis-plus.version>3.3.1</mybatis-plus.version> </properties>
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <version>${mysql-connector.version}</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>${mybatis-plus.version}</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 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data;
@TableName("t_user") @Data public class User {
@TableId(type = IdType.AUTO) private Long id;
private String uname;
}
|
创建 Mapper
1 2 3 4 5 6 7 8
| import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.clay.shardingjdbc.entity.User; import org.apache.ibatis.annotations.Mapper;
@Mapper public interface UserMapper extends BaseMapper<User> {
}
|
配置数据源
- 创建配置文件(
application.properties),配置数据源
1 2 3 4 5 6 7 8 9 10 11
| spring.application.name=sharding-proxy-demo
spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://192.168.2.191:3309/readwrite_splitting_db?serverTimezone=GMT%2B8&useSSL=false spring.datasource.username=root spring.datasource.password=root
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
|
测试代码
读写分离测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @SpringBootTest class ReadWriteTest {
@Autowired private UserMapper userMapper;
@Test public void testInsert() { User user = new User(); user.setUname("张三丰"); userMapper.insert(user);
List<User> list = userMapper.selectList(null); }
}
|
测试代码运行后,ShardingSphere-Proxy 运行输出的日志信息如下:
1 2 3 4 5 6 7
| 16:00:07.139 [ShardingSphere-Command-17] INFO ShardingSphere-SQL - Logic SQL: INSERT INTO t_user ( uname ) VALUES ( '张三丰' ) 16:00:07.139 [ShardingSphere-Command-17] INFO ShardingSphere-SQL - SQLStatement: MySQLInsertStatement(setAssignment=Optional.empty, onDuplicateKeyColumns=Optional.empty) 16:00:07.139 [ShardingSphere-Command-17] INFO ShardingSphere-SQL - Actual SQL: write_ds ::: INSERT INTO t_user ( uname ) VALUES ( '张三丰' )
16:00:07.903 [ShardingSphere-Command-10] INFO ShardingSphere-SQL - Logic SQL: SELECT id,uname FROM t_user 16:00:07.903 [ShardingSphere-Command-10] INFO ShardingSphere-SQL - SQLStatement: MySQLSelectStatement(table=Optional.empty, limit=Optional.empty, lock=Optional.empty, window=Optional.empty) 16:00:07.903 [ShardingSphere-Command-10] INFO ShardingSphere-SQL - Actual SQL: read_ds_0 ::: SELECT id,uname FROM t_user
|
事务一致性测试
为了保证主从库间的事务一致性,避免跨库的分布式事务, 在 ShardingSphere-Proxy 的主从模型中,事务内的数据读写均使用主库。
- 不添加
@Transactional 注解时:insert 语句对主库操作,select 语句对从库操作 - 添加
@Transactional 注解时:insert 语句和 select 语句均对主库操作 - 注意: 在 JUnit 环境下使用
@Transactional 注解,默认情况下会对事务进行回滚(即使在没有使用 @Rollback 注解的情况下,也会对事务进行回滚);如果不希望回滚事务,可以额外使用 @Rollback(false)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @SpringBootTest class ReadWriteTest {
@Autowired private UserMapper userMapper;
@Test @Transactional public void testTransaction() { User user = new User(); user.setUname("李思思"); userMapper.insert(user);
List<User> list = userMapper.selectList(null); }
}
|
测试代码运行后,ShardingSphere-Proxy 运行输出的日志信息如下:
1 2 3 4 5 6 7
| 15:57:23.174 [ShardingSphere-Command-3] INFO ShardingSphere-SQL - Logic SQL: INSERT INTO t_user ( uname ) VALUES ( '李思思' ) 15:57:23.174 [ShardingSphere-Command-3] INFO ShardingSphere-SQL - SQLStatement: MySQLInsertStatement(setAssignment=Optional.empty, onDuplicateKeyColumns=Optional.empty) 15:57:23.175 [ShardingSphere-Command-3] INFO ShardingSphere-SQL - Actual SQL: write_ds ::: INSERT INTO t_user ( uname ) VALUES ( '李思思' )
15:57:23.217 [ShardingSphere-Command-3] INFO ShardingSphere-SQL - Logic SQL: SELECT id,uname FROM t_user 15:57:23.217 [ShardingSphere-Command-3] INFO ShardingSphere-SQL - SQLStatement: MySQLSelectStatement(table=Optional.empty, limit=Optional.empty, lock=Optional.empty, window=Optional.empty) 15:57:23.217 [ShardingSphere-Command-3] INFO ShardingSphere-SQL - Actual SQL: write_ds ::: SELECT id,uname FROM t_user
|
负载均衡测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @SpringBootTest class ReadWriteTest {
@Autowired private UserMapper userMapper;
@Test public void testLoadBalance() { List<User> users1 = userMapper.selectList(null); List<User> users2 = userMapper.selectList(null); }
}
|
测试代码运行后,ShardingSphere-Proxy 运行输出的日志信息如下:
1 2 3 4 5 6 7
| 16:48:01.373 [ShardingSphere-Command-2] INFO ShardingSphere-SQL - Logic SQL: SELECT id,uname FROM t_user 16:48:01.374 [ShardingSphere-Command-2] INFO ShardingSphere-SQL - SQLStatement: MySQLSelectStatement(table=Optional.empty, limit=Optional.empty, lock=Optional.empty, window=Optional.empty) 16:48:01.374 [ShardingSphere-Command-2] INFO ShardingSphere-SQL - Actual SQL: read_ds_0 ::: SELECT id,uname FROM t_user
16:48:01.448 [ShardingSphere-Command-3] INFO ShardingSphere-SQL - Logic SQL: SELECT id,uname FROM t_user 16:48:01.449 [ShardingSphere-Command-3] INFO ShardingSphere-SQL - SQLStatement: MySQLSelectStatement(table=Optional.empty, limit=Optional.empty, lock=Optional.empty, window=Optional.empty) 16:48:01.449 [ShardingSphere-Command-3] INFO ShardingSphere-SQL - Actual SQL: read_ds_1 ::: SELECT id,uname FROM t_user
|