目录
一、广播表与绑定表实战
1、广播表
指所有的分片数据源中都存在的表,表结构及其数据在每个数据库中均完全一致。适用于数据量不大且需要与海量数据的表进行关联查询的场景,例如:字典表。
示例如下:
建表:
CREATE TABLE dict (
`dictId` bigint NOT NULL,
`dictKey` varchar(32) NULL,
`dictVal` varchar(32) NULL,
PRIMARY KEY (`dictId`)
);
创建实体:
@TableName("dict")
public class Dict {
private Long dictid;
private String dictkey;
private String dictval;
// getter ... setter
}
创建mapper
public interface DictMapper extends BaseMapper<Dict> {
}
配置广播规则: 配置方式很简单。 直接配置broadcast-tables就可以了。
# 指定广播表。广播表会忽略分表的逻辑,只往多个库的同一个表中插入数据。
spring.shardingsphere.rules.sharding.broadcast-tables=dict (可以加,多个)
# 打印SQL
spring.shardingsphere.props.sql-show = true
spring.main.allow-bean-definition-overriding = true
# ----------------数据源配置
# 指定对应的库
spring.shardingsphere.datasource.names=m0,m1
spring.shardingsphere.datasource.m0.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m0.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m0.url=jdbc:mysql://localhost:3306/coursedb?serverTimezone=UTC
spring.shardingsphere.datasource.m0.username=root
spring.shardingsphere.datasource.m0.password=root
spring.shardingsphere.datasource.m1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m1.url=jdbc:mysql://localhost:3306/coursedb2?serverTimezone=UTC
spring.shardingsphere.datasource.m1.username=root
spring.shardingsphere.datasource.m1.password=root
#------------------------分布式序列算法配置
# 生成字符串类型分布式主键。
spring.shardingsphere.rules.sharding.key-generators.dict_keygen.type=SNOWFLAKE
# 指定分布式主键生成策略
spring.shardingsphere.rules.sharding.tables.dict.key-generate-strategy.column=dictId
spring.shardingsphere.rules.sharding.tables.dict.key-generate-strategy.key-generator-name=dict_keygen
#-----------------------配置读写分离
# 要配置成读写分离的虚拟库
#spring.shardingsphere.rules.sharding.tables.dict.actual-data-nodes=m$->{0..1}.dict_$->{1..2}
spring.shardingsphere.rules.sharding.tables.dict.actual-data-nodes=m$->{0..1}.dict
# 指定广播表。广播表会忽略分表的逻辑,只往多个库的同一个表中插入数据。
spring.shardingsphere.rules.sharding.broadcast-tables=dict
测试示例
@Test
public void addDict() {
Dict dict = new Dict();
dict.setDictkey("F");
dict.setDictval("女");
dictMapper.insert(dict);
Dict dict2 = new Dict();
dict2.setDictkey("M");
dict2.setDictval("男");
dictMapper.insert(dict2);
}
这样,对于dict字段表的操作就会被同时插入到两个库当中。
2、绑定表
指分片规则一致的一组分片表。使用绑定表进行多表关联查询时,必须使用分片键进行关联,否则会出现笛卡尔积关联或跨库关联,从而影响查询效率。
比如我们另外创建一张用户信息表,与用户表一起来演示这种情况
建表语句:老规矩,自己进行分片
CREATE TABLE user_course_info (
`infoid` bigint NOT NULL,
`userid` varchar(32) NULL,
`courseid` bigint NULL,
PRIMARY KEY (`infoid`)
);
接下来同样增加映射实体以及Mapper。这里就略过了。
然后配置分片规则:
- 数据源配置:指定对应的库
- 分布式序列算法配置:生成字符串类型分布式主键、指定分布式主键生成策略
- 配置真实表分布
- 配置分片(两个表通过相同分片键进行关联:"select uci.* from user_course_info uci ,user u where uci.userid = u.userid")
- 配置分表策略
- 指定绑定表
# 打印SQL
spring.shardingsphere.props.sql-show = true
spring.main.allow-bean-definition-overriding = true
# ----------------数据源配置
# 指定对应的库
spring.shardingsphere.datasource.names=m0
spring.shardingsphere.datasource.m0.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m0.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m0.url=jdbc:mysql://localhost:3306/coursedb?serverTimezone=UTC
spring.shardingsphere.datasource.m0.username=root
spring.shardingsphere.datasource.m0.password=root
#------------------------分布式序列算法配置
# 生成字符串类型分布式主键。
spring.shardingsphere.rules.sharding.key-generators.usercourse_keygen.type=SNOWFLAKE
# 指定分布式主键生成策略
spring.shardingsphere.rules.sharding.tables.user_course_info.key-generate-strategy.column=infoid
spring.shardingsphere.rules.sharding.tables.user_course_info.key-generate-strategy.key-generator-name=usercourse_keygen
# ----------------------配置真实表分布
spring.shardingsphere.rules.sharding.tables.user.actual-data-nodes=m0.user_$->{1..2}
spring.shardingsphere.rules.sharding.tables.user_course_info.actual-data-nodes=m0.user_course_info_$->{1..2}
# ----------------------配置分片
spring.shardingsphere.rules.sharding.tables.user.table-strategy.standard.sharding-column=userid
spring.shardingsphere.rules.sharding.tables.user.table-strategy.standard.sharding-algorithm-name=user_tbl_alg
spring.shardingsphere.rules.sharding.tables.user_course_info.table-strategy.standard.sharding-column=userid
spring.shardingsphere.rules.sharding.tables.user_course_info.table-strategy.standard.sharding-algorithm-name=usercourse_tbl_alg
# ----------------------配置分表策略
spring.shardingsphere.rules.sharding.sharding-algorithms.user_tbl_alg.type=INLINE
spring.shardingsphere.rules.sharding.sharding-algorithms.user_tbl_alg.props.algorithm-expression=user_$->{Math.abs(userid.hashCode()%4).intdiv(2) +1}
spring.shardingsphere.rules.sharding.sharding-algorithms.usercourse_tbl_alg.type=INLINE
spring.shardingsphere.rules.sharding.sharding-algorithms.usercourse_tbl_alg.props.algorithm-expression=user_course_info_$->{Math.abs(userid.hashCode()%4).intdiv(2) +1}
# 指定绑定表
spring.shardingsphere.rules.sharding.binding-tables[0]=user,user_course_info
然后把user表的数据都清空,重新插入一些有对应关系的用户和用户信息表。
@Test
public void addUserCourseInfo(){
for (int i = 0; i < 10; i++) {
String userId = NanoIdUtils.randomNanoId();
User user = new User();
user.setUserid(userId);
user.setUsername("user"+i);
user.setPassword("123qweasd");
user.setUserstatus("NORMAL");
user.setAge(30+i);
user.setSex(i%2==0?"F":"M");
userMapper.insert(user);
for (int j = 0; j < 5; j++) {
UserCourseInfo userCourseInfo = new UserCourseInfo();
userCourseInfo.setInfoid(System.currentTimeMillis()+j);
userCourseInfo.setUserid(userId);
userCourseInfo.setCourseid(10000+j);
userCourseInfoMapper.insert(userCourseInfo);
}
}
}
接下来按照用户ID进行一次关联查询。在UserCourseInfoMapper中配置SQL语句
public interface UserCourseInfoMapper extends BaseMapper<UserCourseInfo> {
@Select("select uci.* from user_course_info uci ,user u where uci.userid = u.userid")
List<UserCourseInfo> queryUserCourse();
}
查询案例:
@Test
public void queryUserCourseInfo(){
List<UserCourseInfo> userCourseInfos = userCourseInfoMapper.queryUserCourse();
for (UserCourseInfo userCourseInfo : userCourseInfos) {
System.out.println(userCourseInfo);
}
}
注意:在进行查询时,可以先把application.properties文件中最后一行,绑定表的配置注释掉。此时两张表的关联查询将要进行笛卡尔查询。
如果没有指定绑定表就会造成笛卡尔积关联(中间两条在当前场景下无效)
Actual SQL: m0 ::: select uci.* from user_course_info_1 uci ,user_1 u where uci.userid = u.userid
Actual SQL: m0 ::: select uci.* from user_course_info_1 uci ,user_2 u where uci.userid = u.userid
Actual SQL: m0 ::: select uci.* from user_course_info_2 uci ,user_1 u where uci.userid = u.userid
Actual SQL: m0 ::: select uci.* from user_course_info_2 uci ,user_2 u where uci.userid = u.userid
这种查询明显性能是非常低的,如果两张表的分片数更多,执行的SQL也会更多。而实际上,用户表和用户信息表,他们都是按照userid进行分片的,他们的分片规则是一致的。
这样,再把绑定关系的注释加上,此时查询,就会按照相同的userid分片进行查询。
Actual SQL: m0 ::: select uci.* from user_course_info_1 uci ,user_1 u where uci.userid = u.userid
Actual SQL: m0 ::: select uci.* from user_course_info_2 uci ,user_2 u where uci.userid = u.userid
在进行多表关联查询时,绑定表是一个非常重要的标准。
二、分片审计
针对数据库分片场景下对执行的 SQL 语句进行审计操作。分片审计既可以进行拦截操作,拦截系统配置的非法 SQL 语句,也可以是对 SQL 语句进行统计操作。
目前ShardingSphere内置的分片审计算法只有一个,DML_SHARDING_CONDITIONS。他的功能是要求对逻辑表查询时,必须带上分片键。
例如在之前的示例中,给course表配置一个分片审计策略
# 分片审计规则: SQL查询必须带上分片键
spring.shardingsphere.rules.sharding.tables.course.audit-strategy.auditor-names[0]=course_auditor
spring.shardingsphere.rules.sharding.tables.course.audit-strategy.allow-hint-disable=true
spring.shardingsphere.rules.sharding.auditors.course_auditor.type=DML_SHARDING_CONDITIONS
这样,再次执行之前HINT策略的示例,就会报错。
当前这个策略看起来好像用处不是很大。但是,别忘了ShardingSphere可插拔的设计。这是一个扩展点,可以自行扩展出很多有用的功能。