一、为什么需要分库分表?
由于数据库的承载能力是有限的,当业务增长量达到一定规模后,数据库的性能就会达到瓶颈。就拿使用最多的 MySQL 来进行说明。
MySQL 的默认引擎是 InnoDB,数据是存在聚簇索引中,而聚簇索引的底层数据结构是 B+ Tree,即使 B+Tree 已经在 B Tree 的基础上优化了空间利用率,但是随着数据规模的增大,树的高度也会达到一定规模,高度能够决定磁盘IO的次数,磁盘IO次数越多,对于性能就会越低。
二、数据库多大才推荐分库分表?
根据阿里开发手册中描述,单表超 500 万行或容量超 2GB 时推荐分库分表。但是具体的情况还是需要根据业务需求、数据库硬件配置、CPU 性能等方面综合评估。
除此之外,要想详细的估算出数据量的瓶颈,还需要根据表的结构以及索引情况,表的字段不同的类型占用的大小也不同,故每条数据占用的大小也不同, B+Tree 每一层的存储数据量的大小也会发生变化。
综上,数据量多大需要根据自身情况去评估,并不一定是网上所说的 1000 万,也不一定是阿里所说的大于 500 万,这就是有些企业单表数据即使超过几千万仍可稳定运行的原因。
三、如何实现分库分表?
3.1 垂直分表
垂直分表指将存在一张表中的不同字段拆分为多张表,是一种大表拆小表的模式。拆分后的表字段、结构均不同,组合一起则是原表。
一般是将经常修改的、数据较大的、不常查询的字段拆分到“扩展表”中,这样拆分以后核心表大多是访问频率较高的字段,而且字段长度也都较短,减少了查询时的磁盘 IO,提升数据库效率
3.2 水平分表
水平分库是将数据库中的表数据按照某种规则拆分到多个库中,以实现水平扩展,提升数据库读写性能。
一般切分到不同的库中的表结构是相同的,表中数据不同,且伴随水平分表。
3.3 实现垂直分表
讲述完分表概念后咱们就动手实现了,本文着重讲述下怎么实现垂直分表,对水平分表感兴趣的伙伴们也可后台留言,下期讲述如何手把手实现水平分表
3.3.1 pom引入shardingsphere依赖
<!-- ShardingSphere JDBC Starter -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.0.0-RC1</version>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-namespace</artifactId>
<version>4.0.0-RC1</version>
</dependency>
3.3.2 配置分片策略
StandardPreciseDateTimeShardingAlgorithmConfig.class
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;
import org.springframework.stereotype.Component;
import java.util.Collection;
/**
* @author Larkin.Long
* @description 根据单个指定时间字段分片策略
* @date 2024/6/7 12:44
**/
@Component
public class StandardPreciseDateTimeShardingAlgorithmConfig implements PreciseShardingAlgorithm {
@Override
public String doSharding(Collection collection, PreciseShardingValue preciseShardingValue) {
String tableNamePrefix = preciseShardingValue.getLogicTableName();
return tableNamePrefix+"_"+preciseShardingValue.getValue();
}
}
StandardRangeDateTimeShardingAlgorithmConfig.class
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.google.common.collect.Range;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingValue;
import org.springframework.stereotype.Component;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
/**
* @author Larkin.Long
* @description 根据时间范围分片
* @date 2024/6/7 12:42
**/
@Component
public class StandardRangeDateTimeShardingAlgorithmConfig implements RangeShardingAlgorithm {
@Override
public Collection<String> doSharding(Collection collection, RangeShardingValue rangeShardingValue) {
String tableName = rangeShardingValue.getLogicTableName();
Set<String> set = new HashSet<>();
Range rangeValue = rangeShardingValue.getValueRange();
if (rangeValue.hasUpperBound() && rangeValue.hasLowerBound()) {
String minValue = String.valueOf(rangeValue.lowerEndpoint());
String maxValue = String.valueOf(rangeValue.upperEndpoint());
long range = DateUtil.between(DateUtil.parse(minValue, DatePattern.PURE_DATE_PATTERN), DateUtil.parse(maxValue, DatePattern.PURE_DATE_PATTERN), DateUnit.DAY);
for (int i = 0; i <= range; i++) {
set.add(tableName+"_"+DateUtil.format(DateUtil.offsetDay(DateUtil.parse(minValue, DatePattern.PURE_DATE_PATTERN),i),DatePattern.PURE_DATE_PATTERN));
}
}
return set;
}
}
3.3.3 yml配置垂直分表
shardingsphere:
datasource:
names: ds0
ds0:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://127.0.0.1:9011/console?useUnicode=true&characterEncoding=utf8&allowPublicKeyRetrieval=true&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8
username: postgres
password: console1234
sharding:
tables:
rc_calllog_total:
actual-data-nodes: ds0.rc_calllog_total_$->{0..0}
table-strategy:
standard:
sharding-column: date_time
precise-algorithm-class-name: com.rainnytech.dataapi.common.standard.StandardPreciseDateTimeShardingAlgorithmConfig
range-algorithm-class-name: com.rainnytech.dataapi.common.standard.StandardRangeDateTimeShardingAlgorithmConfig
props:
sql:
show: true
shardingsphere需要配置在spring层级下
datasource:定义需要实现分表的数据源
sharding:定义需要走分表策略的表(其中
sharding-column对应所需分表的表中依赖字段
)
props:定义是否开启sql日志打印
sharding-column
3.3.4 定义需要分的表(如下图)
这里我是按月实现分表,伙伴们也可根据现实业务需要定义按年还是月进行分表
表结构必须包含刚才我们yml定义的分表字段,即sharding-column对应的字段
3.3.5 验证分表
启动项目,写个接口查询我们的分表,查看sql打印日志,已经成功查询我们需要查询的表
好了,今天的垂直分表就完成了
四、特别注意!!!
date_time为我们的分表依据,举个例子,当我们想查询3月份里面的数据,查询条件必须包含date_time = 202403,CRUD同理,当date_time参数值为几月份时就查询几月份的表数据,只要涉及到我们配置的表,sql语句都需要添加date_time条件!!!伙伴们一定要注意!!!