大纲
前言
学习资源
ShardingSphere-Proxy 使用
广播表使用案例
提示
本节将演示 SpringBoot + MyBatis-Plus 如何使用 ShardingSphere-Proxy 的广播表。完整的案例代码可以直接从 GitHub 下载对应章节 shardingsphere-proxy-lesson-06
核心概念
广播表的简介:
- 广播表是指所有的分片数据源中都存在的表,且表结构及其表数据(包括主键值)在每个数据库中均完全一致。
- 适用于数据量不大,且需要与海量数据的表进行关联查询(
JOIN)的场景,例如:字典表。
广播表的特性:
- 数据变更会同步到所有分片节点
- 对广播表执行
INSERT、UPDATE、DELETE 操作时,ShardingSphere-Proxy 会将变更操作同时广播到所有数据源,确保各节点的数据完全一致。
- 查询操作可在任意节点完成
- 广播表的数据在所有节点中一致,因此查询时可从任意一个数据源获取结果(通常基于负载均衡策略选择节点)。
- 可与任意表进行
JOIN- 广播表在所有节点都有一份完整数据,不涉及跨节点聚合,因此可以安全、方便地与任意分片表或非分片表进行
JOIN 操作。
准备工作
版本说明
本案例所使用的组件版本如下表所示:
| 组件 | 版本说明 |
|---|
| JDK | 11 |
| MySQL | 8.0.29 |
| SpringBoot | 2.7.18 |
| MyBatis-Plus | 3.3.1 |
| ShardingSphere-Proxy | 5.1.1 |
数据库规划
本案例是在两个 MySQL 数据库上实现的,数据库的规划如下图所示:
![]()
| 数据库服务器 | IP | 端口 | 库的名称 | 表的名称 |
|---|
订单数据库服务器一(server_order0) | 192.168.2.191 | 3310 | db_order | t_dict |
订单数据库服务器二(server_order1) | 192.168.2.191 | 3311 | db_order | t_dict |
| ShardingSphere-Proxy 服务器 | 192.168.2.191 | 3309 | sharding_db(逻辑库) | t_dict |
数据库部署
- 部署订单数据库服务器一(
server_order0)
1 2 3 4 5 6 7 8 9 10
| docker run -d \ -p 3310:3306 \ -v /data/server/order0/conf:/etc/mysql/conf.d \ -v /data/server/order0/data:/var/lib/mysql \ -e MYSQL_ROOT_PASSWORD=123456 \ -e TZ=Asia/Shanghai \ --name server_order0 \ --restart always \ mysql:8.0.29
|
- 更改订单数据库服务器一(
server_order0)的默认密码校验方式
1 2 3 4 5 6 7 8
| docker exec -it server_order0 env LANG=C.UTF-8 /bin/bash
mysql -uroot -p
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
|
- 部署订单数据库服务器二(
server_order1)
1 2 3 4 5 6 7 8 9 10
| docker run -d \ -p 3311:3306 \ -v /data/server/order1/conf:/etc/mysql/conf.d \ -v /data/server/order1/data:/var/lib/mysql \ -e MYSQL_ROOT_PASSWORD=123456 \ -e TZ=Asia/Shanghai \ --name server_order1 \ --restart always \ mysql:8.0.29
|
- 更改订单数据库服务器二(
server_order1)的默认密码校验方式
1 2 3 4 5 6 7 8
| docker exec -it server_order1 env LANG=C.UTF-8 /bin/bash
mysql -uroot -p
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
|
数据库初始化
- 在订单数据库服务器一(
server_order0)中,执行以下 SQL 语句:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| CREATE DATABASE db_order;
USE db_order;
CREATE TABLE t_dict( id BIGINT, dict_type VARCHAR(200), dict_code VARCHAR(100), dict_value VARCHAR(200), PRIMARY KEY(id) );
|
- 在订单数据库服务器二(
server_order1)中,执行以下 SQL 语句:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| CREATE DATABASE db_order;
USE db_order;
CREATE TABLE t_dict( id BIGINT, dict_type VARCHAR(200), dict_code VARCHAR(100), dict_value VARCHAR(200), PRIMARY KEY(id) );
|
ShardingSphere-Proxy 配置
特别注意
无论 ShardingSphere-Proxy 是使用 YAML 配置 还是 DistSQL 配置,都必须注意 ShardingSphere-Proxy 的版本差异。不同版本之间支持的 DistSQL 命令、YAML 配置语法以及参数格式可能存在较大差异,同一份配置在不同版本下可能无法直接执行或行为不一致,因此在编写和迁移配置时必须结合 ShardingSphere-Proxy 具体版本进行验证。
YAML 配置
- 创建或编辑 ShardingSphere-Proxy 的
conf/config-sharding.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
| schemaName: sharding_db
dataSources: server_order0: url: jdbc:mysql://192.168.2.191:3310/db_order?serverTimezone=UTC&useSSL=false username: root password: 123456 connectionTimeoutMilliseconds: 30000 idleTimeoutMilliseconds: 60000 maxLifetimeMilliseconds: 1800000 maxPoolSize: 50 minPoolSize: 1
server_order1: url: jdbc:mysql://192.168.2.191: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_dict: actualDataNodes: server_order$->{0..1}.t_dict
broadcastTables: - t_dict
|
- ShardingSphere-Proxy 水平分库的配置内容添加完成后,需要重启 ShardingSphere-Proxy 服务(假设这里是通过 Docker 部署 ShardingSphere-Proxy 服务),否则配置不会生效
1 2
| docker restart server-proxy
|
- 验证 ShardingSphere-Proxy 中的配置规则是否生效(ShardingSphere-Proxy 启动时会从 YAML 配置文件中加载规则)
1 2
| mysql -h192.168.2.191 -P3309 -uroot -p
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| SHOW DATABASES;
USE sharding_db;
SHOW SCHEMA RESOURCES;
SHOW SHARDING TABLE RULES;
SHOW SHARDING BROADCAST TABLE RULES;
|
DistSQL 配置
1 2 3 4 5
| CREATE DATABASE sharding_db;
USE sharding_db;
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| ADD RESOURCE server_order0 ( URL="jdbc:mysql://192.168.2.191:3310/db_order?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 server_order1 ( URL="jdbc:mysql://192.168.2.191:3311/db_order?serverTimezone=UTC&useSSL=false", USER=root, PASSWORD=123456, PROPERTIES( "connectionTimeoutMilliseconds"="30000", "idleTimeoutMilliseconds"="60000", "maxLifetimeMilliseconds"="1800000", "maxPoolSize"="50", "minPoolSize"="1" ) );
|
1 2 3
| CREATE SHARDING TABLE RULE t_dict ( DATANODES("server_order$->{0..1}.t_dict") );
|
1
| CREATE SHARDING BROADCAST TABLE RULES (t_dict);
|
- 验证配置规则是否生效(使用 DistSQL 定义数据源和数据分片规则后,不需要重启 ShardingSphere-Proxy 服务,默认会自动生效)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| SHOW DATABASES;
USE sharding_db;
SHOW SCHEMA RESOURCES;
SHOW SHARDING TABLE RULES;
SHOW SHARDING BROADCAST TABLE RULES;
|
查看日志信息
- 查看 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 输出详细的日志信息,需要在其 conf/server.yml 配置文件中添加 sql-show: true 配置项。
远程访问测试
- 远程连接 ShardingSphere-Proxy
1
| mysql -h192.168.2.191 -P3309 -uroot -p
|
1 2 3 4 5 6 7 8 9 10
| mysql> show databases; + | schema_name | + | sharding_db | | mysql | | information_schema | | performance_schema | | sys | +
|
1 2 3 4 5 6
| mysql> show tables; +-----------------------+------------+ | Tables_in_sharding_db | Table_type | +-----------------------+------------+ | t_dict | BASE TABLE | +-----------------------+------------+
|
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>
|
创建实体类
- 字典实体类(特别注意:由于广播表的表结构及其表数据(包括主键值)在每个数据库中均完全一致,因此可以使用 MyBatis-Plus 内置的
ASSIGN_ID(基于雪花算法)主键生成策略来生成 ID)
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
| import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data;
@TableName("t_dict") @Data public class Dict {
@TableId(type = IdType.ASSIGN_ID) private Long id;
private String dictType;
private String dictCode;
private String dictValue;
}
|
创建 Mapper
1 2 3 4 5 6 7 8
| import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.clay.shardingjdbc.entity.Dict; import org.apache.ibatis.annotations.Mapper;
@Mapper public interface DictMapper extends BaseMapper<Dict> {
}
|
配置数据源
- 创建配置文件(
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.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://192.168.2.191:3309/sharding_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 20
| @SpringBootTest class ShardingTest {
@Autowired DictMapper dictMapper;
@Test public void testInsertBroadcast() { Dict dict = new Dict(); dict.setDictType("gender"); dict.setDictCode("male"); dict.setDictValue("男"); dictMapper.insert(dict); }
}
|
测试代码运行后,ShardingSphere-Proxy 运行输出的日志信息如下(可以发现,往不同数据库服务器中的 t_dict 表插入数据时,该数据的主键(ID)值都是相同的):
1 2 3 4
| 22:45:31.425 [ShardingSphere-Command-3] INFO ShardingSphere-SQL - Logic SQL: INSERT INTO t_dict ( id, dict_type, dict_code, dict_value ) VALUES ( 2002057716207280130, 'gender', 'male', '男' ) 22:45:31.426 [ShardingSphere-Command-3] INFO ShardingSphere-SQL - SQLStatement: MySQLInsertStatement(setAssignment=Optional.empty, onDuplicateKeyColumns=Optional.empty) 22:45:31.426 [ShardingSphere-Command-3] INFO ShardingSphere-SQL - Actual SQL: server_order0 ::: INSERT INTO t_dict ( id, dict_type, dict_code, dict_value ) VALUES (2002057716207280130, 'gender', 'male', '男') 22:45:31.426 [ShardingSphere-Command-3] INFO ShardingSphere-SQL - Actual SQL: server_order1 ::: INSERT INTO t_dict ( id, dict_type, dict_code, dict_value ) VALUES (2002057716207280130, 'gender', 'male', '男')
|
查询数据测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @SpringBootTest class ShardingTest {
@Autowired DictMapper dictMapper;
@Test public void testSelectBroadcast() { List<Dict> dicts = dictMapper.selectList(null); }
}
|
测试代码运行后,ShardingSphere-Proxy 运行输出的日志信息如下(可以发现,最终只会从任意一个节点的 t_dict 表查询数据,默认是基于随机负载均衡规则):
1 2 3
| 22:51:40.491 [ShardingSphere-Command-8] INFO ShardingSphere-SQL - Logic SQL: SELECT id,dict_type,dict_code,dict_value FROM t_dict 22:51:40.491 [ShardingSphere-Command-8] INFO ShardingSphere-SQL - SQLStatement: MySQLSelectStatement(table=Optional.empty, limit=Optional.empty, lock=Optional.empty, window=Optional.empty) 22:51:40.491 [ShardingSphere-Command-8] INFO ShardingSphere-SQL - Actual SQL: server_order0 ::: SELECT id,dict_type,dict_code,dict_value FROM t_dict
|