赞
踩
shardingSphere主要包含3个重要产品,shardingJDBC,sharding Proxy以及shardingsideCar(服务网格相关).
简单来说shardingsphere主要就是用来做分库分表的, 分库分表就是之前说的,select * from logs()//当mysql单表查询 500w以上的数据量
因为当表的数据量大于500w的时候 我做查询 或者我做过滤 的话查询效率就会很慢
我们此时可以吧数据进行分库分表,这个时候就用到分库分表 就是数据的分片存储,分库分表分垂直分和水平分,垂直分就是将表按照类似于微服务的思想划分为userLog,orderLog之类的 水品分就是 比方说user表 按照id取模 奇数在一个表中 偶数在一个表(这样的画 以前单表数据量大 划分到多个表后数据量减少,提高查询效率)
使用shardingjdbc 和shardproxy都可以帮助我们做分库分表 ,为什么会独立2个产品,者2个产品又有什么不同?
这个是sharding-jdbc的结构图
sharding-jdbc 作为一个jar 放在java的app中 目前只支持java
我们的业务代码 ----->sharding-jdbc的api就可以把我们的请求进行分库分表,原来访问一个库现在可以通过sharding-jdbc来帮助我们访问多个库,也可以访问多个库中的多个表
shardingProxy 相当于数据库的代理 他脱离了Java的应用,单独部署了一个服务。我们的应用就像访问mysql一样访问 shardingProxy就可以,sharding-proxy 帮我们完成分库分表的逻辑
这里有一个register-center(注册中心)的概念,我们通常说的注册中心就会想到微服务中的eureka,nacos,zk这些,这里是shardingsphere想打造一个自己的注册中心 将所有服务都放在他的注册中心上集中管理,吧分库分表 像类似于微服务一样集中管理 我的应用就不需要管分库分表的配置
呢么Sharding-jdbc和Sharding-proxy有什么优缺点,分别应用于什么样子的场景?
sharding-jdbc 是一个jar嵌到java代码中的,侵入性高,但是他灵活性高,我可以想怎么配置分库分表规则 就怎么配置,类似于一个jdbc的加强版
jdbc可以连接什么数据库,我sharding-jdbc就可以连什么数据库
sharding-proxy 独立部署的服务,让sharding-proxy 和下面的数据库打交道,都得提前定义好分库分表得规则,这样做就不能达到灵活配置,
sharding-proxy不能对接所有数据库,目前只支持mysql,postGresql,其他产品不支持
接下来我们看一下sharding-jdbc,sharding-jdbc的核心功能就是数据分片以及读写分离(mysql做),读写分离就是我们搭建一个mysql的 master,slave的主从集群,有一个master,slave,slave会同步master的数据,这个时候我们的app就会吧所有对数据库写的操作放在master上,所有对数据库读的操作放在slave上,
就类似于上述的图一样,读写分离的好处就是以前一个数据库承担的访问压力现在分担到2个数据库了,你读的请求放在从库,甚至你的从库还可以具备扩展的能力
可以搭建更多的从库来提升你集群的性能
呢么 数据分片该怎么做 首先shardingSphere有这几个核心概念
大致就是这样子的
shardingSphere 实战
shardingsphere是用来做分库分表的
我们此时用mybatisplus+jdbc+shardingSphere来测试下
# 1.pom 父parent <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>org.example</groupId> <artifactId>MyShardingSphere</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> # sharding jdbc的pom包 <dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>sharding-jdbc-spring-boot-starter</artifactId> <version>4.1.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> # springboot的test包 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> # 数据库连接druid druid连接池 <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.22</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> # mpplus <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.0.5</version> </dependency> </dependencies>
2.实体类以及mapper
# t_user实体类 @TableName("t_user") public class User { private Long userId; private String username; private String ustatus; private int uage; public Long getUserId() { return userId; } public void setUserId(Long userId) { this.userId = userId; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getUstatus() { return ustatus; } public void setUstatus(String ustatus) { this.ustatus = ustatus; } public int getUage() { return uage; } public void setUage(int uage) { this.uage = uage; } @Override public String toString() { return "User{" + "userId=" + userId + ", username='" + username + '\'' + ", ustatus='" + ustatus + '\'' + ", uage=" + uage + '}'; } } 3. mapper
4.yaml配置文件 这里用分库分表的配置
5. 我们注入mapper @RunWith(SpringRunner.class) @SpringBootTest public class ShardingJDBCTest { @Resource UserMapper userMapper; @Test public void adduser() { for (int i = 0; i < 10; i++) { User c = new User(); c.setUserId(Long.valueOf(i)); c.setUage(1000+i); c.setUsername("zhangsan.." + i); c.setUstatus("0"); userMapper.insert(c); } } }
插入进来了 这就代表我们的配置起作用了,我们定义了一个m1数据库
----------------------------------------------------------------->>>>>>>>>>>>>>>>>
我们现在只是定义一个数据源,并且吧数据插入到一个表中了,如果现在插入10条数据,将数据分片到2个user表中,将数据均匀分布在2个表中 该怎么去做
我这里有2个表 t_user_1和t_user_2
首先看user对象
新增加 数据 取消 userId我们此时采用雪花算法 实现id的自动生成
@Test
public void adduser() {
for (int i = 0; i < 10; i++) {
User c = new User();
// c.setUserId(Long.valueOf(i));
c.setUage(1000+i);
c.setUsername("zhangsan.." + i);
c.setUstatus("0");
userMapper.insert(c);
}
}
#配置数据源 定义一个m1 数据源 spring.shardingsphere.datasource.names=m1 # m1 数据库的连接配置 m1的数据源名称 type driver user 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://127.0.0.1:3306/user?serverTimezone=GMT%2B8 spring.shardingsphere.datasource.m1.username=root spring.shardingsphere.datasource.m1.password=123456 #配置真实表分布 数据的真实分布在 在 { m1.t_user_1 和 m1.t_user_2} spring.shardingsphere.sharding.tables.user.actual-data-nodes=m1.t_user_$->{1..2} #(mysql)主键生成策略 数据库的Id 拆分策略 数据库 生成策略 spring.shardingsphere.sharding.tables.user.key-generator.column=user_id {mysql 数据库主键生成策略} spring.shardingsphere.sharding.tables.user.key-generator.type=SNOWFLAKE{雪花算法} spring.shardingsphere.sharding.tables.user.key-generator.props.worker.id=1 #配置分表策略 inline 模式 分片键 基于请求进行分 spring.shardingsphere.sharding.tables.user.table-strategy.inline.sharding-column=user_id{基于user_id来拆分} #分片算法 按照分片键取模加1 计算在那个表中 {按照取模来拆分} spring.shardingsphere.sharding.tables.user.table-strategy.inline.algorithm-expression=t_user_$->{user_id%2+1} # 展示sql spring.shardingsphere.props.sql.show = true
db
看这样的配置就把我们的insert 插入到不同的表中了(取模均匀) 这样当我们进行查询的时候 原本一个表的数据分担到两个表中 之后 数据量降低 自然我们的查询效率就高
shardingphere 会根据我们进行分库分表
m1 代表数据库,对user_id进行一个主键生成,通过雪花算法
根据user_id 的奇偶性来拆分
现在我只是分了1个库中的2个表 ,如果我现在要分到2个库中该怎么处理
就类似于这样 我们的yaml怎么配置呢
#配置数据源 定义一个m1和m2数据源 spring.shardingsphere.datasource.names=m1,m2 # m1 数据库的连接配置 m1的数据源名称 type driver user 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://127.0.0.1:3306/user?serverTimezone=GMT%2B8 spring.shardingsphere.datasource.m1.username=root spring.shardingsphere.datasource.m1.password=123456 # m2 数据库的连接配置 m1的数据源名称 type driver user spring.shardingsphere.datasource.m2.type=com.alibaba.druid.pool.DruidDataSource spring.shardingsphere.datasource.m2.driver-class-name=com.mysql.cj.jdbc.Driver spring.shardingsphere.datasource.m2.url=jdbc:mysql://127.0.0.1:3306/user_1 ?serverTimezone=GMT%2B8 spring.shardingsphere.datasource.m2.username=root spring.shardingsphere.datasource.m2.password=123456 #配置真实表分布 数据的真实分布在 在 先定义那个库 再定义哪一个表{ m1.t_user_1 和 m1.t_user_2} spring.shardingsphere.sharding.tables.user.actual-data-nodes=m$->{1..2}.t_user_$->{1..2} #主键生成策略 数据库的Id 拆分策略 数据库 生成策略 spring.shardingsphere.sharding.tables.user.key-generator.column=user_id spring.shardingsphere.sharding.tables.user.key-generator.type=SNOWFLAKE spring.shardingsphere.sharding.tables.user.key-generator.props.worker.id=1 #配置分表策略 inline 模式 分片键 基于请求进行分 spring.shardingsphere.sharding.tables.user.table-strategy.inline.sharding-column=user_id #分片算法 按照分片键取模加1 计算在那个表中 spring.shardingsphere.sharding.tables.user.table-strategy.inline.algorithm-expression=t_user_$->{user_id%2+1} #分库算法 和上面的分表算法一致 spring.shardingsphere.sharding.tables.user.database-strategy.inline.sharding-column=user_id spring.shardingsphere.sharding.tables.user.database-strategy.inline.algorithm-expression=m$->{user_id%2+1} spring.shardingsphere.props.sql.show = true 因为我们的分库分表算法配置的是如果user_id 为奇数 就会落 入到 m2.t_user2 ,如果user_id为偶数 就会落入到中m1.t_user1 我们吧之前的数据删除了直接跑一边效果 就可以看到 #我们可以指定我们的分片算法以及分库算法来实现我们的请求具体落在拿哪一个库中或者哪一个表中
现在我吧数据insert到2个库存中了 我能不能查询出来呢
#
@Test
public void queryUser(){
//select * from user
QueryWrapper<User> wrapper = new QueryWrapper<>();
// wrapper.orderByDesc("userId");
// wrapper.eq("userId",553684818806706177L);
// wrapper.in()
List<User> userList = userMapper.selectList(wrapper);
userList.forEach(course -> System.out.println(course));
}
看上面逻辑sql : Logic SQL: SELECT user_id,username,ustatus,uage FROM user
# 实际sql
2022-06-27 08:10:45.854 INFO 17308 --- [ main] ShardingSphere-SQL : Actual SQL: m1 ::: SELECT user_id,username,ustatus,uage FROM t_user_1
2022-06-27 08:10:45.854 INFO 17308 --- [ main] ShardingSphere-SQL : Actual SQL: m1 ::: SELECT user_id,username,ustatus,uage FROM t_user_2
2022-06-27 08:10:45.854 INFO 17308 --- [ main] ShardingSphere-SQL : Actual SQL: m2 ::: SELECT user_id,username,ustatus,uage FROM t_user_1
2022-06-27 08:10:45.854 INFO 17308 --- [ main] ShardingSphere-SQL : Actual SQL: m2 ::: SELECT user_id,username,ustatus,uage FROM t_user_2
就可以证明他查询了2个库的4个表
# 当我们使用 //select * from user where user_id='748089358543753216L' 或者in的话 @Test public void queryCourse(){ //select * from user where user_id='748089358543753216L' QueryWrapper<User> wrapper = new QueryWrapper<>(); // wrapper.orderByDesc("userId"); wrapper.eq("user_id",748089358543753216L); // wrapper.in() System.out.println("--------------------------------"); System.out.println("--------------------------------"); System.out.println("--------------------------------"); List<User> userList = userMapper.selectList(wrapper); userList.forEach(course -> System.out.println(course)); } }
可以看到我们的目标sql 以及实际sql直接去m1表的t_user_1 来查询了 因为它会首先根据你分库分表的规则计算你的user_id 从那个库中的那个表中获取
他已经按照你分库分表的规则找到这一条数据 也就是说他查询的效率很高,分库分表的意义 我如果不用分库分表user表的数据太多是不是就会影响我查询效率,我现在分库分表之后 比如说我现在把它分成了2个表,如果我的user表有1000W条数据,吧他分成2个表 每个表只有500w 条数据,500 w条数据, 我按照id 来查询 他扫描的数据就少了 这就是分库分表的意义
# 当我们使用between and 的时候此时的这种规则就不适合我们查询了
@Test
public void queryCourseRange(){
//select * from user where user_id between 553684818806706177L and 553684819184193537L
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.between("user_id",553684818806706177L,553684819184193537L);
List<User> userList = userMapper.selectList(wrapper);
userList.forEach(user -> System.out.println(user));
}
}
报错异常当前的策略不支持我们的查询
INLINE精确分表 对user_Id已知的情况 比如说user_Id 等于 . user_Id in 做一个精确的分片.
因为between and () 中间很多数据 使用inline这种只能匹配 等于 或者in 类型
# 这时候我们要使用第二种策略了 standard #配置数据源 定义一个m1和m2数据源 spring.shardingsphere.datasource.names=m1,m2 # m1 数据库的连接配置 m1的数据源名称 type driver user 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://127.0.0.1:3306/user?serverTimezone=GMT%2B8 spring.shardingsphere.datasource.m1.username=root spring.shardingsphere.datasource.m1.password=123456 # m2 数据库的连接配置 m1的数据源名称 type driver user spring.shardingsphere.datasource.m2.type=com.alibaba.druid.pool.DruidDataSource spring.shardingsphere.datasource.m2.driver-class-name=com.mysql.cj.jdbc.Driver spring.shardingsphere.datasource.m2.url=jdbc:mysql://127.0.0.1:3306/user_1 ?serverTimezone=GMT%2B8 spring.shardingsphere.datasource.m2.username=root spring.shardingsphere.datasource.m2.password=123456 #配置真实表分布 数据的真实分布在 在 先定义那个库 再定义哪一个表{ m1.t_user_1 和 m1.t_user_2} spring.shardingsphere.sharding.tables.user.actual-data-nodes=m$->{1..2}.t_user_$->{1..2} #主键生成策略 数据库的Id 拆分策略 数据库 生成策略 spring.shardingsphere.sharding.tables.user.key-generator.column=user_id spring.shardingsphere.sharding.tables.user.key-generator.type=SNOWFLAKE spring.shardingsphere.sharding.tables.user.key-generator.props.worker.id=1 # 基于standard 这种模式进行分片 表分片键 spring.shardingsphere.sharding.tables.user.table-strategy.standard.sharding-column=user_id # 精确匹配 spring.shardingsphere.sharding.tables.user.table-strategy.standard.precise-algorithm-class-name=com.lvhao.standard.MyPreciseTableShardingAlgorithm # between and 范围匹配 spring.shardingsphere.sharding.tables.user.table-strategy.standard.range-algorithm-class-name=com.lvhao.standard.MyRangeTableShardingAlgorithm # 基于standard 这种模式进行分片 库分片键 spring.shardingsphere.sharding.tables.user.database-strategy.standard.sharding-column=user_id # 精确匹配 spring.shardingsphere.sharding.tables.user.database-strategy.standard.precise-algorithm-class-name=com.lvhao.standard.MyPreciseDSShardingAlgorithm # between and 范围匹配 spring.shardingsphere.sharding.tables.user.database-strategy.standard.range-algorithm-class-name=com.lvhao.standard.MyRangeDSShardingAlgorithm spring.shardingsphere.props.sql.show=true
//精确分库 策略 public class MyPreciseDSShardingAlgorithm implements PreciseShardingAlgorithm<Long> { //select * from user where user_Id = ? or cid in (?,?) // 根据具体的value 找具体的库存 @Override public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> shardingValue) { String logicTableName = shardingValue.getLogicTableName(); String cid = shardingValue.getColumnName(); Long userIdvalue = shardingValue.getValue(); //实现 user_Id$->{cid%2+1) BigInteger shardingValueB = BigInteger.valueOf(userIdvalue); BigInteger resB = (shardingValueB.mod(new BigInteger("2"))).add(new BigInteger("1")); String key = "m"+resB; System.out.println(userIdvalue+".使用.."+key+"..库"); if(availableTargetNames.contains(key)){ return key; } // throw new UnsupportedOperationException("route "+ key +" is not supported ,please check your config"); } }
/* * * standard 的精确匹配 表 * */ public class MyPreciseTableShardingAlgorithm implements PreciseShardingAlgorithm<Long> { //select * from user where user_Id = ? or cid in (?,?) @Override public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> shardingValue) { // 目标表 String logicTableName = shardingValue.getLogicTableName(); //user_Id String user_Id = shardingValue.getColumnName(); 根据具体的值实现 取模算法 找到对应的表 //具体的值 Long userIdvalue = shardingValue.getValue(); //实现 user_id_$->{user_Id%2+1) BigInteger userIdvalueB = BigInteger.valueOf(userIdvalue); BigInteger resB = (userIdvalueB.mod(new BigInteger("2"))).add(new BigInteger("1")); String key = "t_"+logicTableName+"_"+resB; / System.out.println(userIdvalue+".使用.."+key+"..表"); if(availableTargetNames.contains(key)){ return key; } throw new UnsupportedOperationException("route "+ key +" is not supported ,please check your config"); } }
# 分库策略 between and
public class MyRangeDSShardingAlgorithm implements RangeShardingAlgorithm<Long> {
@Override
public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Long> shardingValue) {
//select * from user where user_id between 1 and 100;
Long upperVal = shardingValue.getValueRange().upperEndpoint();//100
Long lowerVal = shardingValue.getValueRange().lowerEndpoint();//1
String logicTableName = shardingValue.getLogicTableName();
/// 2个库都用
return Arrays.asList("m1","m2");
}
}
# 分表策略 between and
public class MyRangeTableShardingAlgorithm implements RangeShardingAlgorithm<Long> {
@Override
public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Long> shardingValue) {
//select * from user where user_id between 1 and 100;
Long upperVal = shardingValue.getValueRange().upperEndpoint();//100
Long lowerVal = shardingValue.getValueRange().lowerEndpoint();//1
String logicTableName = "t_"+shardingValue.getLogicTableName();
/// 2个表都用
return Arrays.asList(logicTableName+"_1",logicTableName+"_2");
}
}
看日志 就能看出来
(逻辑表) Logic SQL: SELECT user_id,username,ustatus,uage FROM user
WHERE user_id BETWEEN ? AND ?
(实际表) Actual SQL: m1 ::: SELECT user_id,username,ustatus,uage FROM t_user_1
WHERE user_id BETWEEN ? AND ? ::: [553684818806706177, 553684819184193537]
2022-06-28 00:11:06.213 INFO 9668 --- [ main] ShardingSphere-SQL : Actual SQL: m1 ::: SELECT user_id,username,ustatus,uage FROM t_user_2
WHERE user_id BETWEEN ? AND ? ::: [553684818806706177, 553684819184193537]
2022-06-28 00:11:06.213 INFO 9668 --- [ main] ShardingSphere-SQL : Actual SQL: m2 ::: SELECT user_id,username,ustatus,uage FROM t_user_1
WHERE user_id BETWEEN ? AND ? ::: [553684818806706177, 553684819184193537]
2022-06-28 00:11:06.213 INFO 9668 --- [ main] ShardingSphere-SQL : Actual SQL: m2 ::: SELECT user_id,username,ustatus,uage FROM t_user_2
WHERE user_id BETWEEN ? AND ? ::: [553684818806706177, 553684819184193537]
shandred策略 支持 Precise查询和Range查询,我现在是按照userId 进行分片策略 我现在加了一个uage字段,看我user表结构
你看 现在 我不仅仅要对user_Id进行between and 查询 也要对uage进行查询,我们的uage也是按照就划分的,如果使用shandred 的话 的要查询4次数据库,现在我如何做到查询一次数据库呢 因为uage也是按照奇偶划分的
@Test
public void queryCourseComplex(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.between("user_id",553684818806706177L,553684819184193537L);
wrapper.eq("uage",1008);
List<User> courses = userMapper.selectList(wrapper);
courses.forEach(course -> System.out.println(course));
}
看sql 我们查询了4个表 因为现在userId也是根据奇偶数划分的 我们如何只查询一次表
Logic SQL: SELECT user_id,username,ustatus,uage FROM user
WHERE user_id BETWEEN ? AND ? AND uage = ? AND uage = ?
Actual SQL: m1 ::: SELECT user_id,username,ustatus,uage FROM t_user_1
WHERE user_id BETWEEN ? AND ? AND uage = ? AND uage = ? ::: [553684818806706177, 553684819184193537, 1009, 1009]
2022-06-28 07:46:18.963 INFO 7952 --- [ main] ShardingSphere-SQL : Actual SQL: m1 ::: SELECT user_id,username,ustatus,uage FROM t_user_2
WHERE user_id BETWEEN ? AND ? AND uage = ? AND uage = ? ::: [553684818806706177, 553684819184193537, 1009, 1009]
2022-06-28 07:46:18.963 INFO 7952 --- [ main] ShardingSphere-SQL : Actual SQL: m2 ::: SELECT user_id,username,ustatus,uage FROM t_user_1
WHERE user_id BETWEEN ? AND ? AND uage = ? AND uage = ? ::: [553684818806706177, 553684819184193537, 1009, 1009]
2022-06-28 07:46:18.963 INFO 7952 --- [ main] ShardingSphere-SQL : Actual SQL: m2 ::: SELECT user_id,username,ustatus,uage FROM t_user_2
WHERE user_id BETWEEN ? AND ? AND uage = ? AND uage = ? ::: [553684818806706177, 553684819184193537, 1009, 1009]
# 基于complex 的分片策略 对多字段进行分库分表 #配置数据源 定义一个m1和m2数据源 spring.shardingsphere.datasource.names=m1,m2 # m1 数据库的连接配置 m1的数据源名称 type driver user 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://127.0.0.1:3306/user?serverTimezone=GMT%2B8 spring.shardingsphere.datasource.m1.username=root spring.shardingsphere.datasource.m1.password=123456 # m2 数据库的连接配置 m1的数据源名称 type driver user spring.shardingsphere.datasource.m2.type=com.alibaba.druid.pool.DruidDataSource spring.shardingsphere.datasource.m2.driver-class-name=com.mysql.cj.jdbc.Driver spring.shardingsphere.datasource.m2.url=jdbc:mysql://127.0.0.1:3306/user_1 ?serverTimezone=GMT%2B8 spring.shardingsphere.datasource.m2.username=root spring.shardingsphere.datasource.m2.password=123456 #配置真实表分布 数据的真实分布在 在 先定义那个库 再定义哪一个表{ m1.t_user_1 和 m1.t_user_2} spring.shardingsphere.sharding.tables.user.actual-data-nodes=m$->{1..2}.t_user_$->{1..2} #主键生成策略 数据库的Id 拆分策略 数据库 生成策略 spring.shardingsphere.sharding.tables.user.key-generator.column=user_id spring.shardingsphere.sharding.tables.user.key-generator.type=SNOWFLAKE spring.shardingsphere.sharding.tables.user.key-generator.props.worker.id=1 #complex复杂分片策略 spring.shardingsphere.sharding.tables.user.table-strategy.complex.sharding-columns= user_id, uage spring.shardingsphere.sharding.tables.user.table-strategy.complex.algorithm-class-name=com.lvhao.complex.MyComplexTableShardingAlgorithm #分库策略 spring.shardingsphere.sharding.tables.user.database-strategy.complex.sharding-columns=user_id, uage spring.shardingsphere.sharding.tables.user.database-strategy.complex.algorithm-class-name=com.lvhao.complex.MyComplexDSShardingAlgorithm spring.shardingsphere.props.sql.show=true
基于complex的分库策略 public class MyComplexDSShardingAlgorithm implements ComplexKeysShardingAlgorithm<Long> { // SELECT * FROM t_user // WHERE user_id BETWEEN ? AND ? AND uage = ? @Override public Collection<String> doSharding(Collection<String> availableTargetNames, ComplexKeysShardingValue<Long> shardingValue) { // 获取对应参数 Range<Long> cidRange = shardingValue.getColumnNameAndRangeValuesMap().get("user_id"); // uage Collection<Long> userAgeValue = shardingValue.getColumnNameAndShardingValuesMap().get("uage"); // user_id 的上下线 Long upperVal = cidRange.upperEndpoint(); Long lowerVal = cidRange.lowerEndpoint(); List<String> res = new ArrayList<>(); for(Long userAge: userAgeValue){ BigInteger userAgeB = BigInteger.valueOf(userAge); BigInteger target = (userAgeB.mod(new BigInteger("2"))).add(new BigInteger("1")); // 获取哪一个表 根据userAge 取模 res.add("m"+target); } return res; } }
基于complex的分表策略 public class MyComplexTableShardingAlgorithm implements ComplexKeysShardingAlgorithm<Long> { @Override public Collection<String> doSharding(Collection<String> availableTargetNames, ComplexKeysShardingValue<Long> shardingValue) { // 获取对应参数 Range<Long> cidRange = shardingValue.getColumnNameAndRangeValuesMap().get("user_id"); // uage Collection<Long> userAgeValue = shardingValue.getColumnNameAndShardingValuesMap().get("uage"); Long upperVal = cidRange.upperEndpoint(); Long lowerVal = cidRange.lowerEndpoint(); List<String> res = new ArrayList<>(); for(Long uage: userAgeValue){ BigInteger uageB = BigInteger.valueOf(uage); BigInteger target = (uageB.mod(new BigInteger("2"))).add(new BigInteger("1")); // 获取哪一个表 根据userAge 取模 获取那一个库 res.add("t_"+shardingValue.getLogicTableName()+"_"+target); } return res; } }
这样的话 我们的实际sql就是一条 ,之前查询age 查 询了4次 我现在只需要查询1次,通过这样的查询,可以提高我们sql的查询效率
Actual SQL: m2 ::: SELECT user_id,username,ustatus,uage FROM t_user_2
WHERE user_id BETWEEN ? AND ? AND uage = ? AND uage = ? ::: [553684818806706177, 553684819184193537, 1009, 1009]
最后一种策略就是强制路由 (和路由键没有关系) #配置数据源 定义一个m1和m2数据源 spring.shardingsphere.datasource.names=m1,m2 # m1 数据库的连接配置 m1的数据源名称 type driver user 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://127.0.0.1:3306/user?serverTimezone=GMT%2B8 spring.shardingsphere.datasource.m1.username=root spring.shardingsphere.datasource.m1.password=123456 # m2 数据库的连接配置 m1的数据源名称 type driver user spring.shardingsphere.datasource.m2.type=com.alibaba.druid.pool.DruidDataSource spring.shardingsphere.datasource.m2.driver-class-name=com.mysql.cj.jdbc.Driver spring.shardingsphere.datasource.m2.url=jdbc:mysql://127.0.0.1:3306/user_1 ?serverTimezone=GMT%2B8 spring.shardingsphere.datasource.m2.username=root spring.shardingsphere.datasource.m2.password=123456 #配置真实表分布 数据的真实分布在 在 先定义那个库 再定义哪一个表{ m1.t_user_1 和 m1.t_user_2} spring.shardingsphere.sharding.tables.user.actual-data-nodes=m$->{1..2}.t_user_$->{1..2} #主键生成策略 数据库的Id 拆分策略 数据库 生成策略 spring.shardingsphere.sharding.tables.user.key-generator.column=user_id spring.shardingsphere.sharding.tables.user.key-generator.type=SNOWFLAKE spring.shardingsphere.sharding.tables.user.key-generator.props.worker.id=1 # 基于hint 强制路由 spring.shardingsphere.sharding.tables.user.table-strategy.hint.algorithm-class-name=com.lvhao.hint.MyHintTableShardingAlgorithm spring.shardingsphere.props.sql.show=true
..路由键路由规则
public class MyHintTableShardingAlgorithm implements HintShardingAlgorithm<Integer> {
@Override
public Collection<String> doSharding(Collection<String> availableTargetNames, HintShardingValue<Integer> shardingValue) {
String key = "t_" + shardingValue.getLogicTableName() + "_" + shardingValue.getValues().toArray()[0];
if (availableTargetNames.contains(key)) {
System.out.println("---------key" + key);
return Arrays.asList(key);
}
throw new UnsupportedOperationException("route " + key + " is not supported ,please check your config");
}
}
@Test
public void queryCourseByHint(){
HintManager hintManager = HintManager.getInstance();
// 定义表
hintManager.addTableShardingValue("user",2);
List<User> courses = userMapper.selectList(null);
courses.forEach(course -> System.out.println(course));
hintManager.close();
}
# 这样日志就可以看到了 我们此时只是查询t_user_2的表
2022-06-28 08:17:05.099 INFO 9648 --- [ main] ShardingSphere-SQL : Actual SQL: m1 ::: SELECT user_id,username,ustatus,uage FROM t_user_2
2022-06-28 08:17:05.099 INFO 9648 --- [ main] ShardingSphere-SQL : Actual SQL: m2 ::: SELECT user_id,username,ustatus,uage FROM t_user_2
还可以配置广播表,广播表中就是每个库中都会保留全量的数据,比如说字典表(性别 男女 或者状态 什么的)
Logic SQL: INSERT INTO t_dict ( ustatus,uvalue ) VALUES ( ?,? )
# 看这样就有我们的全量的数据
Actual SQL: m1 ::: INSERT INTO t_dict ( ustatus,
uvalue , dict_id) VALUES (?, ?, ?) ::: [1, 正常, 749758279579598848]
2022-07-01 22:31:43.720 INFO 21300 --- [ main] ShardingSphere-SQL Actual SQL: m2 ::: INSERT INTO t_dict ( ustatus,
uvalue , dict_id) VALUES (?, ?, ?) ::: [1, 正常, 749758279579598848]
shardingjdbc的sql限制 shardingJDBC 不支持一些sql
mysql 主从的效果 就是在主库的写操作会同步到从库上,主从同步做的就是吧所有的读的操作放在从库上,吧所有写的操作放在主库上 我们现在要配置读写分离 一个读库 一个写库 需要吧主从规则交给sharding-jdbc 他来帮我们做读写分离 #读写分离 spring.shardingsphere.datasource.names=m0,s0 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:3307/masterdemo?serverTimezone=GMT%2B8 spring.shardingsphere.datasource.m0.username=root spring.shardingsphere.datasource.m0.password=root spring.shardingsphere.datasource.s0.type=com.alibaba.druid.pool.DruidDataSource spring.shardingsphere.datasource.s0.driver-class-name=com.mysql.cj.jdbc.Driver spring.shardingsphere.datasource.s0.url=jdbc:mysql://localhost:3308/masterdemo?serverTimezone=GMT%2B8 spring.shardingsphere.datasource.s0.username=root spring.shardingsphere.datasource.s0.password=root #读写分离规则, m0 主库,s0 从库 主库 ds0(规则名字) spring.shardingsphere.sharding.master-slave-rules.ds0.master-data-source-name=m0 从库 spring.shardingsphere.sharding.master-slave-rules.ds0.slave-data-source-names[0]=s0 #基于读写分离的表分片 拆分那个表 spring.shardingsphere.sharding.tables.t_dict.actual-data-nodes=ds0.t_dict # 分片算法 spring.shardingsphere.sharding.tables.t_dict.key-generator.column=dict_id spring.shardingsphere.sharding.tables.t_dict.key-generator.type=SNOWFLAKE spring.shardingsphere.sharding.tables.t_dict.key-generator.props.worker.id=1 spring.shardingsphere.props.sql.show = true spring.main.allow-bean-definition-overriding=true
分库分表带来的问题,一旦你使用了分库分表,你的sql 就不能像以前呢样写的随心所欲了,你的sql会收到很多限制 因为在shardingjdbc中 有的sql不支持
我们使用分库分表的核心问题 就是单机容量的问题, 是不是可以不分 我们可以优先考虑缓存或者ES这些技术,如果缓存过后我们的数据量还是很大,且数据业务增长很快(我i们的考虑数据的业务增长)最后才考虑分库分表,
我们应该分析数据增长量 对持续增长的业务数据优先考虑分库分表,业务预估多少年 阿里建议预估三年
shardingproxy
shardingproxy 和shardingjdbc 功能都是一样的 都是做分库分表的,呢么和shardingjdbc有什么去区别
sharingjdbc 是一个应用程序的扩展包,分库分表代码写在业务程序中,他来帮我们做分库分表
shardingproxy 是一个服务.独立部署成一个服务, 但是proxy 本身不存数据库 通过分库分表逻辑 将你的数据存储在实际的表中,应用人员只需要连接mysql一样就可以使用了
shardingjdbc 是给开发人员用的。开发人员可以在应用程序当中写自己的代码 简化分库分表逻辑 可以定制化自己的分库分表策略。
shardingproxy 是给运维人员用的。让应用不需要感知分库分表的存在, 应用只需要像连接Mysql一样连接就可以了,当mysql如果存储不下,运维这边可以部署一个shardingproxy服务,吧数据在shardingproxy背后进行分库分表就可以了,目前shardingproxy 只支持 postgreSql和mysql
shardingProxy 是一个服务他的核心思想就是让应用无感知 ,你应用不需要知道我后面分库分表怎么分的。查询自动会查询真实表,比shardingjdbc方便多了。对应用无感知,就像用单机数据库一样呢么用
如果只是简单的sql 只做 一些insert update shardingproxy就够用了。因为对你应用 无感知
同时sherdingProxy 也支持吧自己的节点配置到zk上,做高可用的时候 就像kafka一样 多个节点连接同一个zk上就可以
就像这样一样 对于主键生成规则 可以用uuid 也可以用雪花算法,也可以用redis的自增加1 ,y也可以通过spi机制 自定义
https://blog.csdn.net/qq_43631716/article/details/120400972 具体可以看看这个大佬的博客
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。