背景
我们要分库分表?
随着我们系统业务量增加,数据量变大,导致查询效率变低
一般单表超过1000万左右,就该考虑分库分表(这个不是绝对的)
在考虑分库分表前,我需要考虑以下方面:
当查询效率下降,我们是否可以用缓存来解决
我们是否可以用读写分离解决
这些都不能来解决了,再考虑是否分库分表可以解决
分库分表解决方案
- shareding-jdbc
- mycat
Sharding-JDBC:Sharding-JDBC部署相对较简单,只需要在应用程序中引入相关依赖并配置分片规则即可。它与现有的JDBC驱动程序兼容,不需要额外的服务器组件。
Mycat:Mycat的部署相对复杂,需要单独配置和管理Mycat服务器,并与MySQL数据库进行集成。此外,Mycat还支持数据分片的自动扩展和缩放。
- 主要概念
- 逻辑表
在对表进行分片后,一张表分成了n个表,比如订单表t_order分成如下三张表:t_order_1,t_order_2,t_order_3。
此时订单表的逻辑表就是t_order,Sharding-JDBC在进行分片规则配置时针对的就是这张逻辑表
- 真实表
上述t_ordr_1,t_order_2,t_order_3 称之为 真实表
- 数据节点
数据分片的最小单元,由数据源名称和表名称组成,比如:ds1.t_order_1
- 分片键
用于分片的数据库字段,是将数据库(表)水平拆分的关键字段。例:将订单表中的订单主键的尾数取模分片,则订
单主键为分片字段。 SQL中如果无分片字段,将执行全路由,性能较差。 除了对单分片字段的支持,Sharding- Jdbc也支持根据多个字段进行分片。
- 分片算法
通过分片算法将数据分片,支持通过 =
、 BETWEEN
和 IN
分片。
分片算法需要应用方开发者自行实现,可实现的灵 活度非常高。包括:精确分片算法 、范围分片算法 ,复合分片算法 等。例如:where order_id = ?
将采用精确分片算法,where order_id in (?,?,?)
将采用精确分片算法,where order_id BETWEEN ? and ?
将采用范围分片算 法,复合分片算法用于分片键有多个复杂情况。
Sharding-JDBC 中的分片算法需要开发者根据业务自定义
- 分片策略
包含分片键和分片算法,由于分片算法的独立性,将其独立抽离。真正可用于分片操作的是分片键 + 分片算法,也 就是分片策略。
内置的分片策略大致可分为尾数取模、哈希、范围、标签、时间等。由用户方配置的分片策略则更加灵活,常用的使用行表达式配置分片策略,它采用Groovy表达式表示,如: t_user_$->{u_id % 8} 表示t_user 表根据u_id模8,而分成8张表,表名称为 t_user_0 到 t_user_7 。
1、标准分片策略
标准分片策略适用于单分片键,此策略支持 PreciseShardingAlgorithm
和 RangeShardingAlgorithm
两个分片算法。
其中 PreciseShardingAlgorithm
是必选的,用于处理 =
和 IN
的分片。RangeShardingAlgorithm
是可选的,用于处理BETWEEN AND
, >
, <
,>=
,<=
条件分片,如果不配置RangeShardingAlgorithm
,SQL中的条件等将按照全库路由处理。
2、复合分片策略
复合分片策略,同样支持对 SQL语句中的 =
,>
, <
, >=
, <=
,IN
和 BETWEEN AND
的分片操作。不同的是它支持多分片键,具体分配片细节完全由应用开发者实现。
3、行表达式分片策略
行表达式分片策略,支持对 SQL语句中的 =
和 IN
的分片操作,但只支持单分片键。这种策略通常用于简单的分片,不需要自定义分片算法,可以直接在配置文件中接着写规则。
t_order_$->{t_order_id % 4}
代表 t_order
对其字段 t_order_id
取模,拆分成4张表,而表名分别是t_order_0
到 t_order_3
。
4、Hint分片策略
Hint分片策略,对应上边的Hint分片算法,通过指定分片健而非从 SQL
中提取分片健的方式进行分片的策略。
- 分布式主键生成策略
通过在客户端生成自增主键替换以数据库原生自增主键的方式,做到分布式主键无重复。
Sharding-JDBC 内部支持UUID和Snowflake生成分布式主键
shareding-jdbc 实战
- 简介
分布式SQL事务和查询引擎,用于任何数据库上的数据分片,扩展,加密等。
- 应用示例
第一步:引入依赖
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.0.0-RC1</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.13</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
第二步:配置文件
server.port=8888
spring.main.allow-bean-definition-overriding=true
spring.shardingsphere.datasource.names=test
spring.shardingsphere.datasource.test.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.test.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.test.url=jdbc:mysql://192.168.10.10:3306/test
spring.shardingsphere.datasource.test.username=root
spring.shardingsphere.datasource.test.password=123456
spring.shardingsphere.sharding.tables.order.actual-data-nodes=test.order_$->{1..2}
spring.shardingsphere.sharding.tables.order.table-strategy.inline.sharding-column=id
spring.shardingsphere.sharding.tables.order.table-strategy.inline.algorithm-expression=order_$->{id % 2}
spring.shardingsphere.sharding.tables.order.key-generator.column=id
spring.shardingsphere.sharding.tables.order.key-generator.type=SNOWFLAKE
spring.shardingsphere.props.sql.show=true
第三步:controller、entity、mapper
@RestController
@RequiredArgsConstructor
public class ControllerJdbc {
private final OrderMapper orderMapper;
@GetMapping(value = "test-jdbc")
public String testJdbc() {
for(int i=1;i<11;i++){
Order order=new Order();
order.setNum(i);
orderMapper.insertUser(order);
}
return "ok";
}
}
public class Order {
private Integer id;
private Integer num;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getNum() {
return num;
}
public void setNum(Integer num) {
this.num = num;
}
}
@Mapper
public interface OrderMapper {
int insertUser(Order order);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gz.sharding.jdbc.mapper.OrderMapper">
<resultMap id="BaseResultMap"
type="com.gz.sharding.jdbc.entity.Order">
<id column="id" jdbcType="INTEGER" property="id"/>
<id column="num" jdbcType="INTEGER" property="num"/>
</resultMap>
<insert id="insertUser" parameterType="com.gz.sharding.jdbc.entity.Order">
insert into `order` (num) VALUES (#{num})
</insert>
</mapper>
DROP TABLE IF EXISTS `order_1`;
CREATE TABLE `order_1` (
`id` bigint(0) NOT NULL AUTO_INCREMENT,
`num` bigint(0) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
分库分表问题
- 需要对原有代码进行改造
- 需要引入分布式事务
- 对于复杂sql查询如何解决
如果查询条件不是分片键如何解决? 建议与es结合进行查询,把不是分表键数据同步到es,先从es查询,再根据分片键去查询。
代码地址
https://gitee.com/GZ-jelly/microservice-sample