1. 为什么要分库分表? 什么场景下需要分库分表?
单表数据上亿,查询性能下降;
解决大数据存储,提高访问性能;
超大容量问题
性能问题:单一数据库,服务器的CPU、磁盘、内存、IO都有极限
如何去做
垂直切分、水平切分
垂直切分
1. 垂直分库:按模块分库,每个功能模块按照业务领域区分,解决表过多的问题
关联度低的不同表存储在不同的数据库
2. 垂直分表:解决单表列过多的问题
水平切分
单表数据量过大,影响查询性能;解决办法:大数据表拆分成小表
mysql单表性能在500W-1000W,超过1000W性能降低
2. 分库分表原理
拆分策略
- 范围切分:按照日期、userId等,如userId 19999的记录分到第一个库,1000020000的分到第二个库,以此类推
- 根据数值驱魔:一般采用hash取模mod的切分方式,后期分片集群扩容时,需要迁移旧的数据(使用一致性hash算法能较好的避免这个问题)
垂直拆分(er分片)
拆分带来的问题
-
跨库join问题:拆分时键的问题,对原业务的sql数据进行统计,合理选择;后续尽量避免join查询
- 设计时考虑到应用层的join问题
- 在服务层去做调用
- 全局表:字典、省市区
- 数据变更比较少的基于全局应用的表
- 做字段冗余(空间换时间)
- 订单表: 商家id,冗余商家名称,商家名称变更--定时任务、任务通知对订单表的商家名称进行更新
跨分片数据排序分页:在应用层做拼接(不推荐)
-
唯一主键问题:用自增id做主键
解决方案
- UUID 性能比较低;
- 用算法生成主键:snowflake、MongoDB、zookeeper、数据库全局唯一表
分布式事务问题:多个数据库表之间保证原子性(2PC),只要达到最终一致性即可
分库分表最难的在于业务的复杂度
3. 应用
如何权衡当前公司的存储需要优化
- 提前规划,提前防范(主键问题解决、join问题)
- 当前数据单表超过1000W,每天的增长量持续上升
最佳实践
订单号索引表
根据用户编号进行哈希分库分表,可以满足创建订单和通过用户编号维度进行查询操作的需求,但是根据统计,按订单号进行查询的占比达到80%以上,所以需要解决通过订单号进行订单的CURD等操作,所以需要建立订单号索引表。
订单号索引表是用于用户编号与订单号的对应关系表,根据订单号进行哈希取模,放到分库里面。根据订单号进行查询时,先查出订单号对应的用户编号,再根据用户编号取模查询去对应的库查询订单数据。
订单号与用户编号的关系在创建订单后是不会更改的,为了进一步提高性能,引入缓存,把订单号与用户编号的关系存放到缓存里面,减少查表操作,提升性能,索引不命中时再去查表,并把查询结果更新到缓存中。