深圳专门做兼职的网站,o2o分销系统网站建设,企业网站建设因素分析,兰陵建设局网站项目背景 以一个积分系统为例#xff0c;积分系统最核心的有积分账户表和积分明细表#xff1a;
积分账户表#xff1a;每个用户在一个品牌下有一个积分账户记录#xff0c;记录了用户的积分余额#xff0c;数据量在千万级积分明细表#xff1a;用户每次积分发放、积分扣…项目背景 以一个积分系统为例积分系统最核心的有积分账户表和积分明细表
积分账户表每个用户在一个品牌下有一个积分账户记录记录了用户的积分余额数据量在千万级积分明细表用户每次积分发放、积分扣减、积分退款、积分过期都会产生一条积分明细积分明细的数据量很大在亿级别 随着业务的增长、时间的推移积分明细的数据量越来越多单表数据量太大产生很多问题慢SQL加字段、索引比较耗时虽然可以用MySQL8.0的新特性INSTANT算法加字段统计查询很慢数据库CPU压力大等
一、为什么要选择分表 目前积分系统的积分明细数据现存几亿条每日新增积分明细数据大几十万并且随着活动的接入还在逐渐递增慢慢的带来了很多问题需要进行优化处理。 历史数据归档不行吗 因为系统积分规则规定积分有效期两年并且积分扣减、退款、过期等操作需要对积分明细溯源最近两年的积分明细数据也有几个亿即使将两年前的冷数据归档也无法解决积分明细单表几亿数据量的问题。 根据积分现有数据量以及日增长量进行评估未来3-5年内单数据库实例能够满足积分数据的存储并且积分系统的写入TPS单实例可以支撑本次积分决定只分表不分库。 什么情况适合用数据归档 如果能把历史冷数据归档热数据一两亿左右、读写并发不太高的情况下利用好数据库索引、数据库配置高一些完全可以不用分表采用历史数据归档也可以解决很多问题。但是大表还是有一些其他的问题比如加字段比较耗时。 分库分表有很多问题 网上动不动就说超过2000万数据就要分库分表并不太对在单表数据一两亿、并发不是很高、利用好数据库索引的情况下MySQL数据库配置高一点是完全可以扛得住的。 而且能不分库分表就不要分库分表分库分表会产生很多问题没有其他优化手段了再进行分库分表。分库分表可能会产生的问题如下
分布式ID问题分片键选择问题分库分表算法问题容量不够了扩容问题分布式事物问题统计查询分析问题数据迁移问题灰度验证问题数据校验问题................
二、分库分表组件 分库分表组件选择Sharding-JDBC因为目前该项目文档较为丰富、社区活跃度高、无中心化、性能相较于proxy方式性能更好对于开发来说使用更为灵活可控。官网地址Apache ShardingSphere
三、分多少张表分片键和分片算法 需要拆分的表point_info积分明细表 根据目前的数据量、单日新增的数据量来进行分析计划分为128张表未来3-5年内单表数据尽量不要超过2000万后期分表数据量大了可以进行历史数据归档。 拆分后的表为point_info[0-127]采用user_id作为分片键选取user_id后四位取模定位到具体的分表 table后缀 (user_id后四位) % 128 如果怕user_id的尾数不均匀可以将hashcode(user_id)%128来计算分表下标 分表数量为啥是128不是127或者100 我自己的理解如下
设置为2的次幂是程序员的习惯好处是求余运算可以用 num (128-1)按位与运算求余数比除法速度快。 如果还有其他的好处欢迎指正
四、分布式事务 因为只进行了分表没有进行分库所以没有分布式事务问题。 假如后面进行了分库分表可以通过将相同用户的积分账户表和积分明细表分到同一个分库中来避免同一个用户操作账户和明细的分布式事务问题。 shardingsphere里面也有一些分布式事务的支持比如XASeata框架的AT模式等
五、分布式ID 原来单表时主键采用MySQL自增id分表之后再使用自增id会导致不同表主键值重复可以使用雪花算法、美团的Leaf等生成分布式ID也可以自定义实现。 注意原生雪花算法有时钟回拨问题、低频场景下生成的id都是偶数的问题需要进行优化一下感兴趣的话可以看下我的另一篇博客 雪花算法生成分布式ID源码分析及低频场景下全是偶数的解决办法
六、总体计划 简要描述一下整个流程
线上库新的分表创建配置完成然后按照下面的步骤执行
改造双写代码预发测试多种case跑一下双写开关等校验没问题发布上线上线时双写开关默认关闭可以通过配置中心动态开启打开双写开关新表写入失败先忽略因为更新和删除操作会因为新表数据不存在而失败记录双写开始时间点A将老表的积分明细的createTime小于等于双写开始时间点A5分钟防止时间不同步导致少迁移数据预留一些缓冲时间的数据进行全量迁移到分表新老数据全量数据校验查看数据是否一致同时定时任务每隔一小段时间进行增量校验增量数据因为读取新老数据存在短暂时间差可能会瞬时不一致这种数据隔一段时间再次校验多次校验还不一致的数据进行数据订正老表数据覆盖到新表数据改造代码添加双读的逻辑上线读新表的开关默认关闭低流量节点(凌晨过后)进行白名单、灰度切流userId%10000进行验证逐步流量打开持续观察双写开关切到新表保证只写新表也可以继续写老表一段时间或者创建一个新表往老表同步的canal任务方便回滚完成数据迁移方案系统稳定运行一段时间迁移双写代码下线老表进行资源释放 为什么没有采用全量同步canal增量同步方式这种方式不是比双写实现更简单吗怎么进行双写双写有什么好处后面会继续写一篇单独介绍。
七、双写 双写改造点增、删、改 双写主要是为了避免数据延迟问题但是双写无法保证两个数据源的事物一致性问题极端情况下数据不一致数据校验订正任务解决
双写开关有两个通过配置中心实时切换
写老表开关默认开启新表写入没有问题时可以进行关闭也可以继续写一段时间老表写新表开关默认关闭需要开启时打开 新老表的开关同时打开时表示要进行双写
通过配置中心动态进行切换双写期间需要注意的问题如下
对写新表操作需要记录日志
新表不要求一定写成功不影响服务记录错误日志告警通知等有数据校验订正任务兜底
双写事物回滚极端情况下可能新表写成功了、老表数据事物回滚了数据双向核对校验 八、数据迁移 首先我们程序会打开双写然后需要将我们数据库已经存在的数据进行批量的全量迁移在这一过程中我们需要不断的进行数据校验。当我们校验基本问题不大的时候然后进行切流操作直到完全切流之后我们就可以不用再进行数据校验。
1全量同步
全量迁移将老表的积分明细的createTime小于等于双写开始时间点A5分钟防止时间不同步导致少迁移数据预留一些缓冲时间的数据进行全量迁移
全量默认走备库查询目标端写入的是主库防止影响主库读写这里需要注意一下读写的qps以免对线上服务造成影响。 注意一下存量数据迁移完成的时间存量数据迁移可以多线程执行计算公式如下
消耗小时数 数据总量 / (3600*写入tps*任务线程数)
2增量同步 因为我们代码使用了双写双写自动做了增量同步所以不需要单独做增量同步的操作了
九、数据校验 验证老的单表和新的分表的数据是否一致也是全量订正服务必须的前置操作
全量校验增量校验抽样校验
全量校验历史全量数据校验增量校验定时任务每隔一段时间校验该段时间内的增量数据校验是否一致抽样校验可以按照user_id或者业务类型等自定义校验
校验过程 需要注意校验任务注意不要影响线上运行的服务通常校验任务会写很多批查询的语句会出现批量扫表的情况如果代码没有写好很容易导致数据库挂掉。 对账标准老的单表和新的分表中数据保持一致所有字段或者核心字段 对账流程通过定时任务轮询监听数据库binlog执行已经完成迁移的用户在新老积分明细表的数据一致性。需要注意的是由于读取新老库有先后顺序所以产生瞬时的数据不一致对于这种问题可以采用对账重试只要保证最终一致即可。
数据双向核对 单表核对多表多表核对单表。原因是双写时无法完全控制事物一致性问题所以要对单表和分表进行双向核对具体怎么核对后面会再写一篇进行详细介绍。 校验结果 校验结果差异、缺失、多出
数据订正
通过订正服务可以将不一致的老的单表数据和新的分表数据进行数据订正保证一致性。
使用订正服务前必须进行校验服务。
十、切流量
切流代码编写切流量是针对的积分明细的查询操作
对所有查询接口进行整理
读取新老数据配置开关可以通过配置中心实时修改
添加验证白名单列表userId
根据白名单、userId后4位取模进行灰度动态获取查询时用的数据源。命中灰度时查询走新分表数据源查新表数据同时也查询老表数据比对新老表数据是否一致如果一致返回新表数据如果不一致返回老表数据同时发送MQ信息对该用户数据进行数据校验、比对、订正。 切流过程采用逐步放量的形式灰度方式很多我们采用的是先白名单验证然后用户ID取模10000逐步放量的方式。灰度切流验证万分之1-1%-5%-10%-50%-100%切流 关于灰度验证、切流的过程后续会再单独写一篇细化介绍一些。
十一、收尾 直到切流到100%继续运行一段时间然后观察各个业务后续工单反馈情况和各个系统预警日志 切写开关到新的分表上面可以继续写入老表一段时间如果出现问题可以切回老表 对新表进行性能压测确保新表的稳定性
十二、OLAP数据统计分析 对于积分的数据报表、统计查询分析等需求由于分表后数据分散到了多个分表上面如果要再进行统计查询的话需要查询所有的分表效率太低了可以使用一些非关系型数据库比如ES、ClickHouse等。 比如可以使用ClickHouse将多个分表的数据同步聚合到ClickHouse上面利用ClickHouse进行统计分析、出一些数据报表等。 本篇博客从整体上介绍了一个完整的积分明细分表的实施方案里面有一些方面介绍的不是很详细后面会单独的针对一些关键点进行介绍
怎么进行代码双写双写有什么好处为什么没有使用数据库binlog增量同步怎么进行数据双向校验怎么进行灰度验证怎么进行切流......... 如果有写得不对的地方或者您有更好的方案欢迎讨论指正