分库分表方案
垂直切分
垂直分库
根据业务耦合度,将数据库表进行分类,不同的业务表放入不同的数据库中
垂直分表
对于表字段列非常多的表进行字段切分,将不常用的字段单独放入辅助表,提升行数据查询效率
优点:根据业务耦合度切分,业务清晰,高并发情况下,一定程度的提升IO效率、解决单机资源瓶颈
缺点:部分表无法join,只能通过业务层做聚合;依然存在单表数据量过大的情况
水平切分
库内分表:解决单表数据量大问题,但解决不了单机资源瓶颈问题
分库分表:解决单表数据大,单机性能瓶颈;但引入分布式事务的复杂性,跨库join性能差、一些聚合操作复杂性能低;
数据分片的策略:
根据数值范围
按时间区间或者数值区间进行切分,可以按日、月切分到不同的数据库,或者按数值区间进行切分
优点:单表大小可控,水平扩展方便
缺点:热点数据可能成为瓶颈,比如新增的数据访问频繁集中于一个数据库表
根据数值取模
根据指定数据列进行hash取模,切分到不同的数据库中
优点:数据分布均匀,解决热点数据瓶颈问题
缺点:扩容时需要重新分片迁移数据;跨分片查询复杂,如果对于非分片列的查询,不能确定数据在哪个库,需要有分片数据列的映射关系才能确定数据库
分库分表引入的一些问题
事务一致性问题(2PC(两阶段提交)\全局事务处理器(springcloud txmanage):若要保证节点的事务性,需要牺牲一定的高可用,延长事务的执行时间;最终一致性,提高可用性,通过事务补偿机制,对数据进行最终检查,保证最终一致性
跨库join问题:一些策略,全局映射关系表,每个库都存在,减少表的join,存放不经常变化的元数据;字段冗余,冗余一些需要join查询得到的字段;系统层面分别查询后再次组装;
跨库分页、排序、函数问题:通过多库查询之后再次聚合,性能影响较大
全局主键唯一问题:保证主键唯一性,需要引入高可用的ID生成系统
分布式ID生成方案
UUID:32个16进制的数组,性能非常高、本地生成,没有网络消耗
缺点:占用16字节的内存,不利于存储;在mysql Innodb中不适用于主键,B+数的每个节点存放的数据少导致树深度大,索引查询效率低;uuid无序,在插入时需要寻找插入位置移动数据多,效率慢
DB自增:使用DB主键自增的特性,每次插入数据前先查询最新的ID,再进行插入
缺点:DB单机性能问题;对于一些业务场景不适用,如订单,会泄露信息
雪花算法:使用64位long数值存储(1+41+10+12);41位时间戳在高位,自增序列在低位,整个ID是自增的趋势;不依赖第三方数据库;以服务的方式部署,稳定性高;
缺点:依赖于机器时钟,如果机器时钟回拨,可能导致ID重复
美团leaf方案(https://tech.meituan.com/2017/04/21/mt-leaf.html)
优化DB主键自增方案:
解决单机问题:通过多机部署,分别设置不容的主键自增步长保证id自增且唯一
解决数据库查询效率问题:每次生成一定范围的id放入缓存中给业务使用
解决缓存id使用完后大并发查db问题:双buffer,每次在缓存id使用一定量时,通过更新线程获取下一段id,放入缓存
缺点:同样的使用db自增的不适用于某些业务,如订单;数据库压力只能通过堆机器解决;
优化雪花算法法案:
解决workId生成问题:通过zookeepr的持久顺序节点的特性生成workId,缓存本地使用
解决时钟回拨问题:每次获取Id时判断当前时间与上一次ID生成时间的大小,若小于则等待或者生成失败发送告警;定时检查最新ID的时间戳和当前时间做对比;