大家好,我是程序媛雪儿,今天咱们唠唠mysql分库分表。
是什么
mysql分库分表主要是为了应对数据量或访问量过大而导致单个数据库或单个表的性能瓶颈而采取的一种数据库架构设计。通过将数据分布到多个数据库或表中,能有效地提升数据库的性能,提高并发处理能力,减少单点故障的风险。
上面这个解释是文邹邹的官方解释,其实总的来看,这个概念有以下几个点
1、为了解决什么问题
2、采用了什么方法来解决这个问题
3、能达到什么效果
好了,我们带着这三个问题继续深入了解这个知识点。
分库分表时机(为了解决什么问题)
1、前提:项目业务数据逐渐增多,业务发展比较迅猛,单表的数据量达到1000w或20g以后
2、优化已解决不了性能问题(主从读写分离,查询索引都处理了不管用)
3、IO瓶颈、CPU瓶颈
拆分策略(用什么办法解决这个问题)
垂直分库
根据业务将不同的表拆分到不同的库中,如上图,一个电商系统一般包括多个模块,比如用户模块、订单模块、商品模块等等,垂直分库就是把这几个模块拆到不同的数据库里
特点:
1、按业务对数据分级管理、维护、监控、扩展
2、在高并发下,提高磁盘IO和数据量连接数
垂直分表
如上图:一个商品信息表里,包含商品id、商品名称、商品分类、商品品牌、商品标题、商品描述,一般来说,我们看商品的时候,只有商品id、商品名称、商品分类、商品品牌、商品标题这几个数据是常看的,我们可以把它称为热数据,但是只有点击详情,才会看商品描述,那么商品描述便是冷数据,我们可以采用垂直分表,把冷热数据拆分
拆分规则:冷热拆分
1、把不常用的字段单独放到一张表里
2、把text、blob等大字段拆出来放到附表中
特点
1、冷热数据分离
2、减少IO过度争抢,两表互不影响
因为把大字段,且不经常查询的数据分离出来,每次IO都是热数据,冷数据不显示,可以提高性能
水平分库
如上图,我们要把商品订单的数据做水平分库,也就是将一个库的数据拆分到多个库中。
应用怎么知道自己查的数据从哪个库中找呢?
根据路由规则确定
方案一:根据id节点取模,id%3==0的放0库,id%3==1的放1库,id%3==2的放2库
方案二:范围路由,前100w条数据放0库 ,100w-200w的数据放1库,依此类推
特点:
1、解决了单库大数据,高并发的瓶颈问题
2、提高了系统的稳定性和可用性
水平分表
将一个表的数据拆分到多个表中(这个看起来和水平分库很像对不对?水平分表和水平分库最大的不同是水平分表分出来的表格可以在同一个库里,而水平分库一定是在不同的数据库里的)
特点:
1、优化单一表数据量过大产生的性能问题
2、避免IO争抢并减少锁表的几率
分库分表的新问题
1、分布式事务一致性问题
2、跨节点关联查询
3、跨节点分页、排序函数
4、主键避重
分库分表的中间件
1、sharding-sphere
这个中间件我是使用过的,很简单,基本操作是maven引入中间件,直接在application.yml配置就可以使用了。
导包
<!-- 实现分库分表-->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.2.0</version>
</dependency>
application.yml配置
# 分库分表配置
shardingsphere:
# 数据源配置
datasource:
# 多数据源以逗号隔开即可
names: yudada
yudada:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://xx.xx.xx.xx:xx/xx
username: root
password: xxxx
# 规则配置
rules:
sharding:
# 分片算法配置
sharding-algorithms:
# 自定义分片规则名
answer-table-inline:
type: INLINE
props:
algorithm-expression: user_answer_$->{appId % 2}
tables:
user_answer:
actual-data-nodes: yudada.user_answer_$->{0..1}
# 分表策略
table-strategy:
standard:
sharding-column: appId
sharding-algorithm-name: answer-table-inline
测试demo
@SpringBootTest
public class UserAnswerShardingTest {
@Resource
private UserAnswerService userAnswerService;
@Test
public void test(){
UserAnswer userAnswer1 = new UserAnswer();
// 我们设置的是根据AppId分表的
userAnswer1.setAppId(1L);
userAnswer1.setUserId(1809119229891362817L);
userAnswer1.setChoices("1");
userAnswerService.save(userAnswer1);
UserAnswer userAnswer2 = new UserAnswer();
userAnswer2.setAppId(2L);
userAnswer2.setUserId(1809119229891362817L);
userAnswer2.setChoices("2");
userAnswerService.save(userAnswer2);
UserAnswer userAnswerServiceOne = userAnswerService.getOne(Wrappers.lambdaQuery(UserAnswer.class).eq(UserAnswer::getAppId, 1L));
System.out.println(JSONUtil.toJsonStr(userAnswerServiceOne));
UserAnswer userAnswerServiceTwo = userAnswerService.getOne(Wrappers.lambdaQuery(UserAnswer.class).eq(UserAnswer::getAppId, 2L));
System.out.println(JSONUtil.toJsonStr(userAnswerServiceTwo));
}
可以看出来使用是无痕的,代码中的写法不变
2、mycat
欢迎大家关注我的微信公众号,程序媛雪儿,雪儿会在上面发布编程的知识碎片,也有雪儿博客地址,上面有详细系统的笔记,雪儿是全栈,但是公众号目前主要还是发后端的技术,以后可能也会涉及到一些前端的知识,我们下期见,拜拜~