分库分表
分库分表是为了解决单库单表数据量太大,导致的数据库压力过大
分库分表有两种方式,分为垂直拆分和水平拆分
垂直拆分
垂直分表和垂直分库
- 垂直分表 把一张大表中的字段拆分为两张表
- 垂直分库 由于垂直分表之后,两个表还是存储在同一个数据库中,而如果数据库中表过多,数据库的压力过大,可以按照业务来对数据库进行拆分
带来的问题
- 单机的ACID被打破,要么放弃原本的单机事务,修改实现,要么引入分布式事务
- join操作很不方便,不能很方便地利用数据库自身的join,需要应用或者其他方式来解决
- 靠外键去进行约束的场景会受到影响
垂直拆分的规则简单,尤其适合各业务之间的耦合度较低且业务清晰的系统
水平拆分
水平分表和水平分库
- 水平分表
- 水平分库 如果对于同一业务中的表,依然是数据量特别大,可以对数据库进行水平拆分,其中的结构完全相同
带来的问题
- 单机的ACID同样可能被打破
- Join操作同样可能被影响
- 靠外键去进行约束的场景会被影响
- 依赖单库的自增序列生成唯一ID会受影响
- 针对单个逻辑意义上的表的查询要跨库
几种典型的分片规则为:
- 根据日期来分片
- 根据某个特定的字段,如用户id来进行取模分片
问题解决
单机ACID问题
单机事务问题可以使用分布式事务 https://zhhll.icu/2022/分布式/分布式问题/2.分布式事务/,或者最终一致性 https://zhhll.icu/2022/分布式/分布式问题/3.数据一致性/ 来解决
自增序列问题
之前在单机时我们经常使用mysql的autoIncrement来生成id,但是在进行分库分表后,这种方式不能满足我们的要求了,根据id的特性:唯一性和连续性,我们来寻找解决方案
- 唯一性,如果只考虑唯一性的话,那么可以参考UUID来生成,但是连续性不好
- 连续性,考虑到整体全局的连续性的话,我们只能采用独立的系统来解决这个问题
将所有的id生成交给一个单独的系统去处理,所有的机器都是用id生成服务来生成,这个方式有几点不足
- 性能问题,每次都远程取id会有性能损耗,改进方案是每次取一段,剩下的缓存在本地,这样就不需要每次都去远程获取了,但是这样如果机器宕机了,可能会有部分id的浪费
- id生成服务的稳定性,这里使用了一个新的服务,无疑又增加了一个宕机的风险点
- 存储问题
join问题
由于数据已经分布到多个数据库中了,join操作可能会出现跨库的情况,这个如何操作呢?这里说几个思路
- 将需要进行数据库join的操作分为多次数据库操作
- 数据冗余,对一些常用的信息进行冗余,将原本需要join的操作变成单表查询
- 借助elasticsearch来做宽表解决跨库