1.亿级积分数据分库分表:总体方案设计

项目背景

        以一个积分系统为例,积分系统最核心的有积分账户表和积分明细表:

  • 积分账户表:每个用户在一个品牌下有一个积分账户记录,记录了用户的积分余额,数据量在千万级
  • 积分明细表:用户每次积分发放、积分扣减、积分退款、积分过期都会产生一条积分明细,积分明细的数据量很大在亿级别

        随着业务的增长、时间的推移,积分明细的数据量越来越多,单表数据量太大,产生很多问题:慢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里面也有一些分布式事务的支持,比如XA,Seata框架的AT模式等

五、分布式ID

        原来单表时主键采用MySQL自增id,分表之后再使用自增id会导致不同表主键值重复,可以使用雪花算法、美团的Leaf等生成分布式ID,也可以自定义实现。

        注意:原生雪花算法有时钟回拨问题、低频场景下生成的id都是偶数的问题,需要进行优化一下,感兴趣的话可以看下我的另一篇博客 雪花算法生成分布式ID源码分析及低频场景下全是偶数的解决办法

六、总体计划

简要描述一下整个流程:

线上库新的分表创建配置完成,然后按照下面的步骤执行:

  1. 改造双写代码预发测试(多种case跑一下,双写开关等校验),没问题发布上线,上线时双写开关默认关闭,可以通过配置中心动态开启,打开双写开关(新表写入失败先忽略,因为更新和删除操作会因为新表数据不存在而失败),记录双写开始时间点A
  2. 将老表的积分明细的createTime小于等于双写开始时间点A+5分钟(防止时间不同步导致少迁移数据,预留一些缓冲时间)的数据进行全量迁移到分表
  3. 新老数据全量数据校验,查看数据是否一致;同时定时任务每隔一小段时间进行增量校验,增量数据因为读取新老数据存在短暂时间差可能会瞬时不一致,这种数据隔一段时间再次校验,多次校验还不一致的数据进行数据订正(老表数据覆盖到新表数据)
  4. 改造代码,添加双读的逻辑上线(读新表的开关默认关闭)
  5. 低流量节点(凌晨过后)进行白名单、灰度切流userId%10000,进行验证,逐步流量打开,持续观察
  6. 双写开关切到新表,保证只写新表(也可以继续写老表一段时间,或者创建一个新表往老表同步的canal任务,方便回滚),完成数据迁移方案
  7. 系统稳定运行一段时间,迁移&双写代码下线,老表进行资源释放

        为什么没有采用全量同步+canal增量同步方式,这种方式不是比双写实现更简单吗?怎么进行双写?双写有什么好处?后面会继续写一篇单独介绍。

七、双写

        双写改造点:增、删、改

        双写主要是为了避免数据延迟问题,但是双写无法保证两个数据源的事物一致性问题,极端情况下数据不一致(数据校验订正任务解决)

双写开关有两个(通过配置中心实时切换):

  1. 写老表开关:默认开启,新表写入没有问题时可以进行关闭,也可以继续写一段时间老表
  2. 写新表开关:默认关闭,需要开启时打开

        新老表的开关同时打开时,表示要进行双写

通过配置中心动态进行切换,双写期间需要注意的问题如下:

  • 对写新表操作需要记录日志
  • 新表不要求一定写成功(不影响服务,记录错误日志告警通知等,有数据校验订正任务兜底)
  • 双写事物回滚:极端情况下可能新表写成功了、老表数据事物回滚了(数据双向核对校验)

八、数据迁移

        首先我们程序会打开双写,然后需要将我们数据库已经存在的数据进行批量的全量迁移,在这一过程中我们需要不断的进行数据校验。当我们校验基本问题不大的时候,然后进行切流操作,直到完全切流之后,我们就可以不用再进行数据校验。

1)全量同步

  • 全量迁移:将老表的积分明细的createTime小于等于双写开始时间点A+5分钟(防止时间不同步导致少迁移数据,预留一些缓冲时间)的数据进行全量迁移
  • 全量默认走备库查询,目标端写入的是主库,防止影响主库读写,这里需要注意一下读写的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增量同步
  • 怎么进行数据双向校验
  • 怎么进行灰度验证,怎么进行切流
  • .........

        如果有写得不对的地方,或者您有更好的方案,欢迎讨论指正

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/267228.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

gitlab添加ssh公钥

一:生成公钥 桌面鼠标右击打开 Open Git Bash here (前提是安装了Git); 2.输入命令 ssh-keygen -t rsa -C "123*****90qq.com"来生成新的密钥对,将其中的"123*****90qq.com"替换为你自己的电子邮件地址。 命令:ssh-keyg…

MWC 2024丨美格智能推出5G RedCap系列FWA解决方案,开启5G轻量化新天地

2月27日,在MWC 2024世界移动通信大会上,美格智能正式推出5G RedCap系列FWA解决方案。此系列解决方案具有低功耗、低成本等优势,可以显著降低5G应用复杂度,快速实现5G网络接入,提升FWA部署的经济效益。 RedCap技术带来了…

Linux之嫁衣神功

前言:此博客内容全部转载他人,无一原创,初衷转播优质内容 1 挂载的作用 扩展存储空间 将额外的存储设备连接到Linux系统中,扩展系统的存储容量。 实现数据共享 不同计算机之间可以共享文件和数据,实现更高效的协作…

强大!信息安全技术导图全汇总!共200多张(附下载)

从网络上搜集整理了200多张信息安全技术导图,文末有免费领取方式。 详细文件目录 APT 攻击/ APT 攻击.png APT攻防指南基本思路v1.0-SecQuan.png Red Teaming Mind Map.png Windows常见持久控制.png 发现与影响评估.jpg …

Unity的相机跟随和第三人称视角

Unity相机跟随和第三人称视角 介绍镜头视角跟随人物方向进行旋转的镜头视角固定球和人的镜头视角 思路跟随人物方向进行旋转的镜头视角固定球和人的镜头视角 镜头旋转代码人物移动的参考代码注意 介绍 最近足球项目的镜头在做改动,观察了一下实况足球的视角&#x…

html基本标签

<h1></h1> <p></p> h是标签从h1~h6&#xff0c;没用h7,h8 p是段落 <a href"https://www.educoder.net">Educoder平台</a> href可以指定链接进行跳转 <img src"https://www.educoder.net/attachments/download/2078…

跨境知识分享:什么是动态IP?和静态IP有什么区别?

对于我们跨境人来说&#xff0c;清楚地了解IP地址、代理IP等这些基础知识&#xff0c;并学会正确地使用IP地址对于保障店铺的安全性和稳定性至关重要&#xff0c;尤其是理解动态IP和静态IP之间的区别&#xff0c;以及如何利用这些知识来防止账号关联&#xff0c;对于每个电商卖…

深入理解分库、分表、分库分表

前言 分库分表&#xff0c;是企业里面比较常见的针对高并发、数据量大的场景下的一种技术优化方案&#xff0c;所谓"分库分表"&#xff0c;根本就不是一件事儿&#xff0c;而是三件事儿&#xff0c;他们要解决的问题也都不一样&#xff0c;这三个事儿分别是"只…

Nodejs 第四十二章(jwt)

什么是jwt? JWT&#xff08;JSON Web Token&#xff09;是一种开放的标准&#xff08;RFC 7519&#xff09;&#xff0c;用于在网络应用间传递信息的一种方式。它是一种基于JSON的安全令牌&#xff0c;用于在客户端和服务器之间传输信息。 https://jwt.io/ JWT由三部分组成&…

Qt 自定义长条进度条(类似播放器进度条)

1.运行界面 2.步骤 其实很简单。 2.1绘制底图圆角矩形 2.2绘制播放进度圆角矩形 参考&#xff1a;painter绘图 3.源码 #pragma once#include <QWidget> #include <QLabel> #include <QHBoxLayout> #include <QMouseEvent> #include <QDebug&g…

探索Redis 6.0的新特性

Redis&#xff08;Remote Dictionary Server&#xff09;是一个开源的内存中数据结构存储系统&#xff0c;通常被用作缓存、消息队列和实时数据处理等场景。它的简单性、高性能以及丰富的数据结构支持使其成为了众多开发者和企业的首选。在Redis 6.0版本中&#xff0c;引入了一…

NVMe开发——PCIe复位

简介 PCIe中有4种复位机制&#xff0c;早期的3种被称为传统复位(Conventional Reset)。传统复位中的前2种又称为基本复位(Fundamental Resets)&#xff0c;分别为冷复位(Cold Reset)&#xff0c;暖复位(Warm Reset)。第3种复位为热复位(Hot Reset)。第4种复位被称为功能级复位…

SQL面试题(2)

第一题 创建trade_orders表: create table `trade_orders`( `trade_id` varchar(255) NULL DEFAULT NULL, `uers_id` varchar(255), `trade_fee` int(20), `product_id` varchar(255), `time` varchar(255) )ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_…

【机器学习基础】层次聚类-BIRCH聚类

&#x1f680;个人主页&#xff1a;为梦而生~ 关注我一起学习吧&#xff01; &#x1f4a1;专栏&#xff1a;机器学习 欢迎订阅&#xff01;相对完整的机器学习基础教学&#xff01; ⭐特别提醒&#xff1a;针对机器学习&#xff0c;特别开始专栏&#xff1a;机器学习python实战…

如何将视频的声音转换成音频?视频提取音频的小妙招

在数字化时代&#xff0c;视频和音频是我们生活中不可或缺的元素。有时候&#xff0c;我们可能只需要视频中的音频部分&#xff0c;这时就需要将视频的声音转换成音频文件。那么&#xff0c;如何实现这一操作呢&#xff1f;本文将为您介绍几种简单而实用的小妙招。 方法一&…

朱元璋如何处理十万女俘让蒙古人险些灭绝

明太祖朱元璋的铁血手腕&#xff1a;十万女俘与蒙古人的灭顶之灾 在中国历史上&#xff0c;明太祖朱元璋以其卓越的领导才能和深谋远虑的政治智慧&#xff0c;开创了明朝的辉煌篇章。然而&#xff0c;在他登基称帝的背后&#xff0c;却隐藏着一段鲜为人知的残酷往事。今天&…

抽象类、模板方法模式

抽象类概述 在Java中abstract是抽象的意思&#xff0c;如果一个类中的某个方法的具体实现不能确定&#xff0c;就可以申明成abstract修饰的抽象方法&#xff08;不能写方法体了&#xff09;&#xff0c;这个类必须用abstract修饰&#xff0c;被称为抽象类。 抽象方法定义&…

202435读书笔记|《半小时漫画中国史》——读点经济学与历史,生活更美好,趣味烧脑土地制度、商鞅变法、华丽丽的丝绸之路这里都有

202435读书笔记|《半小时漫画中国史》——读点经济学与历史&#xff0c;生活更美好&#xff0c;趣味烧脑土地制度、商鞅变法、华丽丽的丝绸之路这里都有 1. 土地政策、度量衡及税收2. 商鞅变法3. 西汉经济4. 西汉盐铁大辩论5. 西汉丝绸之路 《半小时漫画中国史&#xff1a;经济…

Day09:基础入门-算法逆向散列对称非对称JS源码逆向AESDESRSASHA

目录 算法加密-概念&分类&类型 加密解密-识别特征&解密条件 解密实例-密文存储&数据传输 思维导图 章节知识点&#xff1a; 应用架构&#xff1a;Web/APP/云应用/三方服务/负载均衡等 安全产品&#xff1a;CDN/WAF/IDS/IPS/蜜罐/防火墙/杀毒等 渗透命令&am…

2024年2月最新微信域名检测拦截接口源码

这段PHP代码用于检测指定域名列表中的域名是否被封。代码首先定义了一个包含待检测域名的数组 $domainList&#xff0c;然后遍历该数组&#xff0c;对每个域名发送HTTP请求并检查响应内容以判断域名是否被封。 具体步骤如下&#xff1a; 1. 定义待检测的域名列表。 2. 遍历域名…