草在结它的种子,风在摇她的叶子,我们就这样坐着不说话,就十分美好。
完整代码已上传Gitee
文章目录
- 1、准备服务器
- 1.1、创建server-order0容器
- 1.2、创建server-order1容器
- 1.3、创建server-user容器
- 2、基本水平分片
- 2.1、基本配置
- 2.2、数据源配置
- 2.3、标椎分片表配置
- 2.4、行表达式
- 2.5、分片算法配置
- 2.6、分布式序列算法
- 3、多表关联
- 3.1、创建关联表
- 3.2、创建实体类
- 3.3、创建Mapper
- 3.4、配置关联表
- 3.5、测试插入数据
- 4、绑定表
- 4.1、创建VO对象
- 4.2、添加Mapper方法
- 4.3、测试关联查询
- 4.4、配置绑定表
- 5、广播表
- 4.1、什么是广播表
- 4.2、创建广播表
- 4.3、程序实现
- 4.3.1、创建实体类
- 4.3.2、创建Mapper
- 4.3.3、配置广播表
- 4.4、测试广播表
1、准备服务器
服务器规划:使用docker
方式创建如下容器
- 服务器:容器名
server-order0
,端口3310
- 服务器:容器名
server-order1
,端口3311
- 服务器:容器名
server-user
,端口3301
1.1、创建server-order0容器
- step1:创建容器:
1
2
3
4
5
6
7
8docker run -d -p 3310: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服务器:
1
2
3
4
5
6
7#进入容器: 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需要在业务层实现,不能依赖数据库的主键自增
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17CREATE DATABASE db_order; USE db_order; CREATE TABLE t_order0 ( id BIGINT, order_no VARCHAR(30), user_id BIGINT, amount DECIMAL(10,2), PRIMARY KEY(id) ); CREATE TABLE t_order1 ( id BIGINT, order_no VARCHAR(30), user_id BIGINT, amount DECIMAL(10,2), PRIMARY KEY(id) );
1.2、创建server-order1容器
- step1:创建容器:
1
2
3
4
5
6
7
8docker run -d -p 3311: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服务器:
1
2
3
4
5
6
7#进入容器: 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需要在业务层实现,不能依赖数据库的主键自增
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17CREATE DATABASE db_order; USE db_order; CREATE TABLE t_order0 ( id BIGINT, order_no VARCHAR(30), user_id BIGINT, amount DECIMAL(10,2), PRIMARY KEY(id) ); CREATE TABLE t_order1 ( id BIGINT, order_no VARCHAR(30), user_id BIGINT, amount DECIMAL(10,2), PRIMARY KEY(id) );
1.3、创建server-user容器
- step1:创建容器:
1
2
3
4
5
6
7
8docker 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
。。。。
1
2
3
4
5
6CREATE TABLE `t_user` ( `id` bigint NOT NULL AUTO_INCREMENT, `uname` varchar(30) DEFAULT NULL, PRIMARY KEY (`id`) )
2、基本水平分片
2.1、基本配置
1
2
3
4
5
6
7
8
9
10
11
12#========================基本配置 spring: application: name: sharging-jdbc-demo shardingsphere: # 内存模式 mode: type: Memory # 打印SQl props: sql-show: true
2.2、数据源配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24spring: shardingsphere: # 配置三个数据源 datasource: names: server-user,server-order0,server-order1 server-order0: driver-class-name: com.mysql.jdbc.Driver jdbc-url: jdbc:mysql://192.168.1.1:3310/db_order password: 123456 type: com.zaxxer.hikari.HikariDataSource username: root server-order1: driver-class-name: com.mysql.jdbc.Driver jdbc-url: jdbc:mysql://192.168.1.1:3311/db_order password: 123456 type: com.zaxxer.hikari.HikariDataSource username: root server-user: driver-class-name: com.mysql.jdbc.Driver jdbc-url: jdbc:mysql://192.168.1.1:3301/db_user password: 123456 type: com.zaxxer.hikari.HikariDataSource username: root
2.3、标椎分片表配置
为了方便查看就不贴yaml
格式了
1
2
3
4
5
6
7#========================标准分片表配置(数据节点配置) # spring.shardingsphere.rules.sharding.tables.<table-name>.actual-data-nodes=值 # 值由数据源名 + 表名组成,以小数点分隔。多个表以逗号分隔,支持 inline 表达式。 # <table-name>:逻辑表名 spring.shardingsphere.rules.sharding.tables.t_user.actual-data-nodes=server-user.t_user spring.shardingsphere.rules.sharding.tables.t_order.actual-data-nodes=server-order0.t_order0,server-order0.t_order1,server-order1.t_order0,server-order1.t_order1
Order实体类的主键策略:
1
2@TableId(type = IdType.ASSIGN_ID)//分布式id
测试:保留上面配置中的一个分片表节点分别进行测试,检查每个分片节点是否可用
1
2
3
4
5
6
7
8
9
10
11
12/** * 水平分片:插入数据测试 */ @Test public void testInsertOrder(){ Order order = new Order(); order.setOrderNo("ShardingSphere001"); order.setUserId(1L); order.setAmount(new BigDecimal(100)); orderMapper.insert(order); }
2.4、行表达式
优化上一步的分片表配置
https://shardingsphere.apache.org/document/5.1.1/cn/features/sharding/concept/inline-expression/
1
2
3
4
5
6
7#========================标准分片表配置(数据节点配置) # spring.shardingsphere.rules.sharding.tables.<table-name>.actual-data-nodes=值 # 值由数据源名 + 表名组成,以小数点分隔。多个表以逗号分隔,支持 inline 表达式。 # <table-name>:逻辑表名 spring.shardingsphere.rules.sharding.tables.t_user.actual-data-nodes=server-user.t_user spring.shardingsphere.rules.sharding.tables.t_order.actual-data-nodes=server-order$->{0..1}.t_order$->{0..1}
2.5、分片算法配置
水平分库:
分片规则:order表中user_id
为偶数时,数据插入server-order0服务器
,user_id
为奇数时,数据插入server-order1服务器
。这样分片的好处是,同一个用户的订单数据,一定会被插入到同一台服务器上,查询一个用户的订单时效率较高。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19#------------------------分库策略 # 分片列名称 spring.shardingsphere.rules.sharding.tables.t_order.database-strategy.standard.sharding-column=user_id # 分片算法名称 spring.shardingsphere.rules.sharding.tables.t_order.database-strategy.standard.sharding-algorithm-name=alg_inline_userid #------------------------分片算法配置 # 行表达式分片算法 # 分片算法类型 spring.shardingsphere.rules.sharding.sharding-algorithms.alg_inline_userid.type=INLINE # 分片算法属性配置 spring.shardingsphere.rules.sharding.sharding-algorithms.alg_inline_userid.props.algorithm-expression=server-order$->{user_id % 2} # 取模分片算法 # 分片算法类型 spring.shardingsphere.rules.sharding.sharding-algorithms.alg_mod.type=MOD # 分片算法属性配置,分片数量 spring.shardingsphere.rules.sharding.sharding-algorithms.alg_mod.props.sharding-count=2
为了方便测试,先设置只在 t_order0
表上进行测试(目前还没做分表配置
)
1
2xxx.actual-data-nodes=server-order$->{0..1}.t_order0
测试:可以分别测试行表达式分片算法和取模分片算法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16/** * 水平分片:分库插入数据测试 */ @Test public void testInsertOrderDatabaseStrategy(){ for (long i = 0; i < 4; i++) { Order order = new Order(); order.setOrderNo("ShardingSphere001"); order.setUserId(i + 1); order.setAmount(new BigDecimal(100)); orderMapper.insert(order); } }
水平分表:
分片规则:order表中order_no的哈希值为偶数时
,数据插入对应服务器的t_order0表
,order_no的哈希值为奇数时
,数据插入对应服务器的t_order1表
。因为order_no是字符串形式,因此不能直接取模。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15#------------------------分表策略 # 分片列名称 spring.shardingsphere.rules.sharding.tables.t_order.table-strategy.standard.sharding-column=order_no # 分片算法名称 spring.shardingsphere.rules.sharding.tables.t_order.table-strategy.standard.sharding-algorithm-name=alg_hash_mod #------------------------分片算法配置 # 哈希取模分片算法 # 分片算法类型 spring.shardingsphere.rules.sharding.sharding-algorithms.alg_hash_mod.type=HASH_MOD # 分片算法属性配置 spring.shardingsphere.rules.sharding.sharding-algorithms.alg_hash_mod.props.sharding-count=2
测试前不要忘记将如下节点改回原来的状态(上边配置了分片+分表策略,这里就可以用四个节点
)
1
2xxx.actual-data-nodes=server-order$->{0..1}.t_order$->{0..1}
测试:
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/** * 水平分片:分表插入数据测试 */ @Test public void testInsertOrderTableStrategy(){ for (long i = 1; i < 5; i++) { Order order = new Order(); order.setOrderNo("ShardingSphere" + i); order.setUserId(1L); order.setAmount(new BigDecimal(100)); orderMapper.insert(order); } for (long i = 5; i < 9; i++) { Order order = new Order(); order.setOrderNo("ShardingSphere" + i); order.setUserId(2L); order.setAmount(new BigDecimal(100)); orderMapper.insert(order); } } /** * 测试哈希取模 */ @Test public void testHash(){ //注意hash取模的结果是整个字符串hash后再取模,和数值后缀是奇数还是偶数无关 System.out.println("ShardingSphere001".hashCode() % 2); System.out.println("ShardingSphere0011".hashCode() % 2); }
查询测试:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24/** * 水平分片:查询所有记录 * 查询了两个数据源,每个数据源中使用UNION ALL连接两个表 */ @Test public void testShardingSelectAll(){ List<Order> orders = orderMapper.selectList(null); orders.forEach(System.out::println); } /** * 水平分片:根据user_id查询记录 * 查询了一个数据源,每个数据源中使用UNION ALL连接两个表 */ @Test public void testShardingSelectByUserId(){ QueryWrapper<Order> orderQueryWrapper = new QueryWrapper<>(); orderQueryWrapper.eq("user_id", 1L); List<Order> orders = orderMapper.selectList(orderQueryWrapper); orders.forEach(System.out::println); }
2.6、分布式序列算法
雪花算法:
https://shardingsphere.apache.org/document/5.1.1/cn/features/sharding/concept/key-generator/
水平分片需要关注全局序列,因为不能简单的使用基于数据库的主键自增。
这里有两种方案:一种是基于MyBatisPlus的id策略;一种是ShardingSphere-JDBC的全局序列配置。
基于MyBatisPlus的id策略:
将Order类的id设置成如下形式
1
2
3@TableId(type = IdType.ASSIGN_ID) private Long id;
基于ShardingSphere-JDBC的全局序列配置
:和前面的MyBatisPlus的策略二选一
1
2
3
4
5
6
7
8
9
10
11
12#------------------------分布式序列策略配置 # 分布式序列列名称 spring.shardingsphere.rules.sharding.tables.t_order.key-generate-strategy.column=id # 分布式序列算法名称 spring.shardingsphere.rules.sharding.tables.t_order.key-generate-strategy.key-generator-name=alg_snowflake # 分布式序列算法配置 # 分布式序列算法类型 spring.shardingsphere.rules.sharding.key-generators.alg_snowflake.type=SNOWFLAKE # 分布式序列算法属性配置,如果指定了分布式序列为分片id需要配置,这里没有指定所以不配 #spring.shardingsphere.rules.sharding.key-generators.alg_snowflake.props.xxx=
此时,需要将实体类中的id策略修改成以下形式:
1
2
3
4//当配置了shardingsphere-jdbc的分布式序列时,自动使用shardingsphere-jdbc的分布式序列 //当没有配置shardingsphere-jdbc的分布式序列时,自动依赖数据库的主键自增策略 @TableId(type = IdType.AUTO)
3、多表关联
3.1、创建关联表
在server-order0、server-order1
服务器中分别创建两张订单详情表t_order_item0、t_order_item1
我们希望同一个用户的订单表和订单详情表中的数据都在同一个数据源中,避免跨库关联
,因此这两张表我们使用相同的分片策略。
那么在t_order_item
中我们也需要创建order_no
和user_id
这两个分片键
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18CREATE TABLE t_order_item0( id BIGINT, order_no VARCHAR(30), user_id BIGINT, price DECIMAL(10,2), `count` INT, PRIMARY KEY(id) ); CREATE TABLE t_order_item1( id BIGINT, order_no VARCHAR(30), user_id BIGINT, price DECIMAL(10,2), `count` INT, PRIMARY KEY(id) );
3.2、创建实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
14package com.ShardingSphere.shardingjdbcdemo.entity; @TableName("t_order_item") @Data public class OrderItem { //当配置了shardingsphere-jdbc的分布式序列时,自动使用shardingsphere-jdbc的分布式序列 @TableId(type = IdType.AUTO) private Long id; private String orderNo; private Long userId; private BigDecimal price; private Integer count; }
3.3、创建Mapper
1
2
3
4
5
6
7package com.ShardingSphere.shargingjdbcdemo.mapper; @Mapper public interface OrderItemMapper extends BaseMapper<OrderItem> { }
3.4、配置关联表
t_order_item
的分片表、分片策略、分布式序列策略和t_order一致
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21#------------------------标准分片表配置(数据节点配置) spring.shardingsphere.rules.sharding.tables.t_order_item.actual-data-nodes=server-order$->{0..1}.t_order_item$->{0..1} #------------------------分库策略 # 分片列名称 spring.shardingsphere.rules.sharding.tables.t_order_item.database-strategy.standard.sharding-column=user_id # 分片算法名称 spring.shardingsphere.rules.sharding.tables.t_order_item.database-strategy.standard.sharding-algorithm-name=alg_mod #------------------------分表策略 # 分片列名称 spring.shardingsphere.rules.sharding.tables.t_order_item.table-strategy.standard.sharding-column=order_no # 分片算法名称 spring.shardingsphere.rules.sharding.tables.t_order_item.table-strategy.standard.sharding-algorithm-name=alg_hash_mod #------------------------分布式序列策略配置 # 分布式序列列名称 spring.shardingsphere.rules.sharding.tables.t_order_item.key-generate-strategy.column=id # 分布式序列算法名称 spring.shardingsphere.rules.sharding.tables.t_order_item.key-generate-strategy.key-generator-name=alg_snowflake
3.5、测试插入数据
同一个用户的订单表和订单详情表中的数据都在同一个数据源中,避免跨库关联
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/** * 测试关联表插入 */ @Test public void testInsertOrderAndOrderItem(){ for (long i = 1; i < 3; i++) { Order order = new Order(); order.setOrderNo("ShardingSphere" + i); order.setUserId(1L); orderMapper.insert(order); for (long j = 1; j < 3; j++) { OrderItem orderItem = new OrderItem(); orderItem.setOrderNo("ShardingSphere" + i); orderItem.setUserId(1L); orderItem.setPrice(new BigDecimal(10)); orderItem.setCount(2); orderItemMapper.insert(orderItem); } } for (long i = 5; i < 7; i++) { Order order = new Order(); order.setOrderNo("ShardingSphere" + i); order.setUserId(2L); orderMapper.insert(order); for (long j = 1; j < 3; j++) { OrderItem orderItem = new OrderItem(); orderItem.setOrderNo("ShardingSphere" + i); orderItem.setUserId(2L); orderItem.setPrice(new BigDecimal(1)); orderItem.setCount(3); orderItemMapper.insert(orderItem); } } }
4、绑定表
需求: 查询每个订单的订单号和总订单金额
4.1、创建VO对象
1
2
3
4
5
6
7
8package com.ShardingSphere.shardingjdbcdemo.entity; @Data public class OrderVo { private String orderNo; private BigDecimal amount; }
4.2、添加Mapper方法
1
2
3
4
5
6
7
8
9
10
11
12package com.ShardingSphere.shardingjdbcdemo.mapper; @Mapper public interface OrderMapper extends BaseMapper<Order> { @Select({"SELECT o.order_no, SUM(i.price * i.count) AS amount", "FROM t_order o JOIN t_order_item i ON o.order_no = i.order_no", "GROUP BY o.order_no"}) List<OrderVo> getOrderAmount(); }
4.3、测试关联查询
1
2
3
4
5
6
7
8
9
10/** * 测试关联表查询 */ @Test public void testGetOrderAmount(){ List<OrderVo> orderAmountList = orderMapper.getOrderAmount(); orderAmountList.forEach(System.out::println); }
可以看到同一个数据源中,查询的次数是t_order
和t_order_item
的笛卡尔积数量,但是t_order0
中的订单数据只会在对应数据源中t_order_item0
,不会在t_order_item1
中,所以有些关联查询是没有意义的。
4.4、配置绑定表
在原来水平分片配置的基础上添加如下配置:
1
2
3#------------------------绑定表 spring.shardingsphere.rules.sharding.binding-tables[0]=t_order,t_order_item
配置完绑定表后再次进行关联查询的测试:
-
如果不配置绑定表:测试的结果为8个SQL。 多表关联查询会出现笛卡尔积关联。
-
如果配置绑定表:测试的结果为4个SQL。 多表关联查询不会出现笛卡尔积关联,关联查询效率将大大提升。
绑定表:
指分片规则一致的一组分片表。 使用绑定表进行多表关联查询时,必须使用分片键进行关联,否则会出现笛卡尔积关联或跨库关联,从而影响查询效率。
5、广播表
4.1、什么是广播表
指所有的分片数据源中都存在的表,表结构及其数据在每个数据库中均完全一致。 适用于数据量不大且需要与海量数据的表进行关联查询的场景,例如:字典表。
广播具有以下特性:
(1)插入、更新操作会实时在所有节点上执行,保持各个分片的数据一致性
(2)查询操作,只从一个节点获取
(3)可以跟任何一个表进行 JOIN 操作
4.2、创建广播表
在server-order0
、server-order1
和server-user
服务器中分别创建t_dict
表
1
2
3
4
5
6CREATE TABLE t_dict( id BIGINT, dict_type VARCHAR(200), PRIMARY KEY(id) );
4.3、程序实现
4.3.1、创建实体类
1
2
3
4
5
6
7
8
9@TableName("t_dict") @Data public class Dict { //可以使用MyBatisPlus的雪花算法 @TableId(type = IdType.ASSIGN_ID) private Long id; private String dictType; }
4.3.2、创建Mapper
1
2
3
4@Mapper public interface DictMapper extends BaseMapper<Dict> { }
4.3.3、配置广播表
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# 广播表 spring: shardingsphere: # 配置三个数据源 datasource: names: server-user,server-order0,server-order1 server-order0: driver-class-name: com.mysql.jdbc.Driver jdbc-url: jdbc:mysql://192.168.1.1:3310/db_order password: 123456 type: com.zaxxer.hikari.HikariDataSource username: root server-order1: driver-class-name: com.mysql.jdbc.Driver jdbc-url: jdbc:mysql://192.168.1.1:3311/db_order password: 123456 type: com.zaxxer.hikari.HikariDataSource username: root server-user: driver-class-name: com.mysql.jdbc.Driver jdbc-url: jdbc:mysql://192.168.1.1:3301/db_user password: 123456 type: com.zaxxer.hikari.HikariDataSource username: root mode: type: Memory props: sql-show: true rules: sharding: # # 广播表 broadcast-tables: - t_dict # #数据节点可不配置,默认情况下,向所有数据源广播 tables: t_dict: actual-data-nodes: server-user.t_dict,server-order$->{0..1}.t_dict
4.4、测试广播表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23@Autowired private DictMapper dictMapper; /** * 广播表:每个服务器中的t_dict同时添加了新数据 */ @Test public void testBroadcast(){ Dict dict = new Dict(); dict.setDictType("type1"); dictMapper.insert(dict); } /** * 查询操作,只从一个节点获取数据 * 随机负载均衡规则 */ @Test public void testSelectBroadcast(){ List<Dict> dicts = dictMapper.selectList(null); dicts.forEach(System.out::println); }
笔记出处
完整代码已上传 Gitee Spring整合常用组件
到此,本章内容就介绍完啦,如果有帮助到你 欢迎点个赞????????吧!!您的鼓励是博主的最大动力! 有问题评论区交流。
最后
以上就是沉默棒棒糖最近收集整理的关于ShardingSphere水平分片、多表关联、绑定表、广播表的全部内容,更多相关ShardingSphere水平分片、多表关联、绑定表、广播表内容请搜索靠谱客的其他文章。
发表评论 取消回复