MySQL分库分表篇
一、切分方式
1. 垂直切分
1. 垂直分库:将不同业务数据存放到不同的库。如订单库,商品库
2. 垂直分表:将一个表的大字段且不常访问字段,划分出来放到其他的表。内存中的数据页可以存放更多的热点数据。增加查询效率,减少I/O
优点:业务解偶,不同业务数据独立维护;一定程度缓解库的压力
缺点:多表/多库访问,需要在接口层聚合数据;分布式事务管理难度增加;依然有单表数据量多大问题
2. 水平切分
1. 库内分表:降低单一表的数据量,对数据库的CPU,内存和网络I/O的竞争没变、
2. 分库分表:将切分的子表分散到不同数据库,降低单表数据量,也降低单库的访问压力
优点:解决高并发下单库访问量,提升系统稳定性和负载
缺点:跨库的join关联查询性能差;扩容难度和维护量大;跨分片事务一致性难保证
二、分库分表规则
1. 根据取值范围
按照时间区间或者ID区间。
优点:
1. 单表数据量可控
2. 水平扩展只需要添加新节点,无需对其他分片数据进行迁移
3. 能快速定位要查询的数据在哪个库
4. 范围查找,不需要跨分片查询
缺点:
1. 连续分片存在数据热点,比如某个范围查询频率很高
2. hash取模
对分库字段的hash结果取模,得到余数即数据库编号。假如共有N个数据库,编号从0到N-1。则 hash(user_id) mod N 得到的结果为数据库编号
优点:
1. 数据分片相对均匀,不容易出现热点和并发访问的瓶颈
缺点:
1. 当集群中的数据库宕机,数据库总数减少,计算数据库算法 hash(user_id) mod N-1 ,同一个用户的信息不在一个库中。(考虑一致性哈希)
2. 集群扩容时,需要迁移旧数据。(一致性hash算法可以避免)
3. 如果查询条件不带分库字段,则无法定位到具体的库,将遍历所有库查询
三、分库分表带来的问题
1. 事务一致性问题
保证强一致性:使用强一致性的分布式事务比如2PC,最大限度保障一致性。但是延长了事务提交时间,进而导致加锁时间延长,降低并发量。
保证最终一致性:对性能要求高,对一致性要求不高的,采用事后补偿机制。比如定期和标准数据源同步。
2. 跨节点关联join问题
1. 全局表:需要关联查询的表类似于配置表,那么在每个节点都配置一份表。
2. 字段冗余:将需要的字段额外添加一份到表中
3. 数据组装:在上层分两次查询,将结果组装
4. ER分片:
5. 数据异构:按照查询需求将数据重新导出到Redis/ES/DB
3. 跨节点分页、排序、函数问题
如果排序、分页字段恰好是分片字段,按照分片规则容易定位需要的分片。如果不是分片字段,需要在所有节点都执行一下同等的分页、排序等操作,然后在业务层进行合并和二次处理。
4. 全局主键避重问题
表中数据分布在不同分页中,分散的表将无法使用各自的自增ID作为主键,需要设置新的主键规则,一般有如下几种:
1. UUID
(生成机制参考:https://blog.csdn.net/qq_40950903/article/details/108589837)
UUID是随机的,作为主键
1. 插入时会造成索引页的频繁分裂,移动大量数据
2. 频繁分裂导致每个索引页上的数据是稀疏的,并容易产生碎片(大小不合适无法被再次被利用的内存)
3. 写入时需要加载磁盘中的页到缓存,造成大量随机I/O。跟顺序ID写完一页才写下一页不一样。
2. 主键生成表
多个节点公用一张生成表,生成主键并发插入时通过数据库锁控制获得自增主键。并发限制在单台数据库的性能,当数据库宕机时,整个系统不可用。使用主从备份,从服务器不一定跟主服务器是完全一致的,存在延迟写的问题。
改进:部署两台以上的数据库,自增的步长设置为节点的数量。每台初始值设置从1到N。保证了每台生成的自增ID不同,当其中一台数据库宕机时,可以从其他数据库获取。
3. 雪花ID
生成规则:1bit(符号位) + 41bit(时间戳) + 10bit(工作机器id) + 12bit(序列号) 生成64位的Long型数字
依赖时钟,如果服务器时钟同步导致回拨,会产生重复ID。并且字段长导致索引页增多。
5. 数据迁移和扩容问题
如果采用取模计算分片,扩容时需要读取历史数据,再按照新的分片规则写入到不同分片。
参考:https://zhuanlan.zhihu.com/p/99396275