概念
什么是分库分表
当我们使用读写分离、索引、缓存后,数据库的压力还是很大的时候,这就需要使用到数据库拆分了。
分库:从单个数据库拆分成多个数据库的过程,将数据散落在多个数据库中。
分表:从单张表拆分成多张表的过程,将数据散落在多张表内。
为什么要分库分表
分库分表可以提升系统的稳定性跟负载能力,不存在单库/单表大数据。没有高并发的性能瓶颈,增加系统可用性。缺点是分库表无法join,只能通过接口方式解决,提高了系统复杂度。
Sharding-JDBC 实现分库分表
Sharding-JDBC最早是当当网内部使用的一款分库分表框架,到2017年的时候才开始对外开源,这几年在大量社区贡献者的不断迭代下,功能也逐渐完善,现已更名为ShardingSphere, 2020年4月16日正式成为Apache软件基金会的顶级项目,同时兼容多种数据库,通过可插拔架构,理想情况下,可以做到对业务代码无感知。
使用手册
引入依赖
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.1.1</version>
</dependency>
配置分库分表规则
1:标准分片策略
2:复合分片策略
3:行表达式分片策略
4:Hint分片策略
本实例介绍了3:行表达式分片策略 和 1:标准分片策略
1:标准分片策略
/**
* 数据库精准分片
* @author zbm
* @date 2024/3/1417:08
*/
public class AccurateDBShardingConfig implements PreciseShardingAlgorithm<Long> {
/**
*
* @param dataSourceNames 数据源集合
* 在分库时值为所有分片库的集合 databaseNames
* 分表时为对应分片库中所有分片表的集合 tablesNames
*
* @param shardingValue 分片属性,
* logicTableName 为逻辑表,
* columnName 分片健(字段),
* value 为从 SQL 中解析出的分片健的值
* @return
*/
@Override
public String doSharding(Collection<String> dataSourceNames, PreciseShardingValue<Long> shardingValue) {
for (String databaseName : dataSourceNames) {
String value = shardingValue.getValue().hashCode()%2+"";
//value是0,则进入0库表,1则进入1库表
if (databaseName.endsWith(value)) {
return databaseName;
}
}
throw new IllegalArgumentException();
}
/**
* @author zbm
* @date 2024/3/1417:20
*/
public class PreciseTablesShardingConfig implements PreciseShardingAlgorithm<Long> {
@Override
public String doSharding(Collection<String> tableNames, PreciseShardingValue<Long> preciseShardingValue) {
// 获取分片键的值
Long shardingValue = preciseShardingValue.getValue();
// 取模分表(取模都是从0到collection.size())
long index = shardingValue.hashCode() % tableNames.size();
// 判断逻辑表名
String logicTableName = preciseShardingValue.getLogicTableName();
// 物理表名
String PhysicalTableName = logicTableName + "_" + (index);
// 判断是否存在该表
if (tableNames.contains(PhysicalTableName)) {
return PhysicalTableName;
}
// 不存在则抛出异常
throw new UnsupportedOperationException();
}
}
3:行表达式分片策略
application.properties
#配置数据源的名字
server.port=8085
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/db1?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.m0.username=root
spring.shardingsphere.datasource.m0.password=12345678
#配置第二个数据源具体内容,包含连接池,驱动,地址,用户名和密码
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/db2?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.m1.username=root
spring.shardingsphere.datasource.m1.password=12345678
# 一个实体类对应两张表,覆盖a
spring.main.allow-bean-definition-overriding=true
##指定查询到库的那个表
spring.shardingsphere.sharding.tables.product_order.actual-data-nodes=m$->{0..1}.product_order_$->{0..1}
##定义主键的规则
spring.shardingsphere.sharding.tables.product_order.key-generator.column=id
spring.shardingsphere.sharding.tables.product_order.key-generator.type=snowflake
##指定表分片的策略 约定cid值,奇数在
#spring.shardingsphere.sharding.tables.product_order.table-strategy.inline.sharding-column=id
#spring.shardingsphere.sharding.tables.product_order.table-strategy.inline.algorithm-expression=product_order_$->{id % 2}
#配置文件精准分片
spring.shardingsphere.sharding.tables.product_order.table-strategy.standard.sharding-column=id
spring.shardingsphere.sharding.tables.product_order.table-strategy.standard.precise-algorithm-class-name=com.example.shardingjdbc.config.PreciseTablesShardingConfig
##指定数据库的分片规则 约定user_id 奇数在库2 偶数在库1
#spring.shardingsphere.sharding.default-database-strategy.inline.sharding-column=id
#spring.shardingsphere.sharding.default-database-strategy.inline.algorithm-expression=db$->{id % 2 + 1}
#spring.shardingsphere.sharding.tables.product_order.database-strategy.inline.sharding-column=id
#spring.shardingsphere.sharding.tables.product_order.database-strategy.inline.algorithm-expression=m$->{id % 2}
#配置文件数据库分片
spring.shardingsphere.sharding.tables.product_order.database-strategy.standard.sharding-column=id
spring.shardingsphere.sharding.tables.product_order.database-strategy.standard.precise-algorithm-class-name=com.example.shardingjdbc.config.AccurateDBShardingConfig
###打开sql日志
spring.shardingsphere.props.sql.show=true
问题
数据倾斜问题
行表达式分片算法、复合行表达式分片算法、Hint 行表达式分片算法的取模形式的行表达式算法也会存在这个问题。比方说,我们按5库20表进行发分库分表,只有分片键是0或是5的倍数才会落到0库,但是又因为是20表,所以落到0库的数据只会分布在0或5或10或15这4个表中,导致路由到0库的数据永远不会落到除上述4个表的其他16个表中
数据倾斜问题解决
个人是不太推荐单纯的根据分片键取模分片算法的,在实际的项目中一直使用的是自定义类分片算法,实质是根据分片键的最后一位数字对数据库数取模,分片键的倒数二三位数字对表数取模。这种方式可以尽量避免数据倾斜问题,不会存在某些库表永远不会有数据的情况。