真彩色图像数据量 计算_纯干货,记录一次年数据量上亿的业务实现过程

8f1df3f784c3387ba1dc7e7d9cd8d93a.png

一. 背景简述

我们是一个电商平台,为了提高活跃用户量,提出了一个签到活动的案子,当然,该需求的落地交到了我手上。

同一个业务的实现有多种方式,具体选择使用哪种需要从实际来考量,另外,我始终贯彻的一个原则是:而且,真正的高级用户喜欢选择能够找到的最简单的工具,直到他们认为简单的工具不适用为止。

现在平台的注册用户有300+万,日活大概在30万~40万之间,所以,数据量是不可绕过的一个点。

服务框架使用的是BladeX,对今天聊的内容来说无关紧要;存储工具有MySQL,Redis;而消息队列有RocketMQ,很容易想到,这个可能在某些环节提供优化。

二. 需求分析

只列举几个关键的需求,这里所说的关键有些是从逻辑处理的角度来出发的。

1.运营可以创建签到活动,活动有起止时间,活动开始前可编辑,可删除,同一时刻只允许存在一个有效的活动;

2.运营可以给活动配置奖励,奖励规则有两种,每日赠送和连续签到赠送,连续签到可设置不同的签到天数,且每一种规则在同一个活动中只允许存在一个;奖励可配置替换品,当库存不足时可以使用替换品来继续赠送;已配置的奖励属性可以随时修改;

3.运营可以看到领奖记录,可以根据活动名称、领奖人等条件来筛选记录;

4.客户可以看到当天的签到状态,可以看到每个月的签到详情(默认是当月),可以看到所有的领奖记录(由近到远,有分页);

三. 表结构设计

引用之前的文章中的一段话:物理设计是良好性能的基础,存储方式对于数据库性能的提高至关重要。

首先就是签到活动表的设计,这个不难,根据需要的属性添加字段即可;另外,像这种活动配置一类的数据,就算是按照7天1个计算,一年下来也就52条,根本不需要索引;而额外的缓存可选可不选,因为它虽然允许修改,但条件是在活动未生效前,使用MySQL提供的内置缓存足以。另外多说一句,所有有价值的数据删除都是逻辑删除,是否有价值的标准根据业务来衡量,没有一个放之四海而皆准的原则。

其次就是奖励配置表的设计,该表的数据同签到活动表的数据相关联。根据上面的需求分析可以知道,奖励配置的数据量 = 签到活动的数据量 * 2~5,总之一年也就几百条数据,所以表设计规则同签到活动表的设计规则相同。再多说一句,我通常将配置类的数据修改操作拆成删除 + 创建,目的是为了保留之前的数据状态,方便回溯,如果仅仅依赖操作日志的话有很多局限。

接着就是签到明细表的设计,这是该业务实现过程中第一个需要慎重思考之处。

首先排除的就是签到一天记录一条数据的实现方式,这是最愚蠢的设计,没有之一。不论是出于相同字段重复数据导致的资源(磁盘、内存、IO)浪费,还是在捞数据时需要的聚合操作导致的内存与CPU的浪费,都是最糟糕的设计。

很多人想到bitmap存储,不错,但是我们需要做一些额外的考量:第一个,在MySQL中实现bitmap通常选择int来做,而bigint长度8字节64位,也就是说最多可以存储64天的签到记录,显然这个限制我们是不能接受的;第二个,某一天的签到状态可能有多种,假设存在补签,则无法通过01来区分。

最终采取的方式与bitmap类似,使用一个字符串来存储,自左向右依次排开,一个字符表示签到一天,该字符串可以提供如下信息:

a.字符串末尾必定是1,字符串的长度就是自活动之日起,第n天的签到标志;

b.根据字符串的长度可直接判断当天是否已经签到(也可以通过updateTime来判断);

c.若字符串的长度小于昨天与活动开始日的差,可直接将连续签到天数置0(也可以通过updateTime来判断);

d.若字符串的长度等于昨天与活动开始日的差,可通过字符串下标左移计算连续签到天数。直至遇到0字符为止;

当然,我是有做已签到次数与连续签到天数等汇总字段来减少逻辑计算的,且两个字段的一致性维护成本非常低。

好了,现在需要考虑数据量的事情了,一个人在一个签到活动中有一条记录,我们给日活乘以2就是60万~80万,一年下来撑死5000+万数据量,听到这个数字先别怕,我们可以建立索引。

我们在sign_config_id上与user_id上单独建立索引,为何不建立联合索引呢?因为不需要!使用签到记录的场景有两个:当查询某个人的签到记录时,通过user_id已经过滤掉99.99%的数据了,剩下的数据量<=签到活动数据量;而sign_config_id可以统计某个签到活动的参与人数等内容给运营使用,甚至可以把该聚合结果丢到签到活动表里。

索引的代价不足为虑,两个索引需要的内存大概50,000,000 * 16B * 2 = 1.5G,而且最频繁修改的签到内容相关的字段不会导致索引重建,另外如果要解决页分裂问题,可以在插入记录时字节将签到内容字段补全,当然这种操作的代价就是需要更多的磁盘空间。

缓存需要吗?我觉得不需要!缓存解决的是高峰期的数据快速访问的问题,而按照当前平台下的日活计算,根据28比例拆到早晚06:00~10:00,QPS也就在10个左右,慌个鸡儿。

最后就是领奖记录表的设计,由于存在每日赠送,所以一年下来的数据量保守估计在300,000 * 365 = 1亿左右,这也是该业务实现过程中第二个需要慎重思考之处。

其实都不用想,没有搜索引擎,必然要拆表。关键是如何拆?根据搜索条件来拆!客户端默认请求当前签到活动下的领奖记录,如果不存在则按照领奖顺序由近及远捞数据,领奖记录的顺序同签到活动的顺序一致,所以咱们根据签到活动来动态建表完全没有问题,即便运营还需要有其他条件来筛选,但管理端的访问量可以忽略不计。

拆表一时爽,现在需要填坑了。拆表不能无限拆,当小表非常多的时候,访问整个数据集需要大量的表整合操作(注意我说的是表整合不是联表,联表就炸了),我们拆表的目的是提高数据库的访问性能,但如果在逻辑处理上浪费了大量的时间,就得不偿失了,所以必须要有表合并策略。

合并的表有两个特点:第一个是没有修改操作或者很少有修改操作;第二个是读取操作也不是很频繁,当然频繁也无所谓。(我尽说废话)

在这里我设置的表合并时间为表创建后的第16天凌晨00:05,原因是我们的领奖记录在第15天后便不再变更,具体内容与本文核心无关,不做过多赘述。

而表迁移的方式有两种:

第一种是将小表的数据直接插入到总表里,有个问题是数据迁移过程中当需要整个数据集的时候存在冗余的脏数据,在表迁移完成后数据恢复完整性;

第二种是先将总表完整地拷贝出来,小表向拷贝表中迁移数据,数据迁移完成将流量切到拷贝表,删除旧表,这样在表迁移过程中也可以保证数据的完整性,但需要两倍的总表存储空间。

同时由于小表中的主键独立,所以需要一些额外的操作来保证总表中的主键唯一。我采用的方式是将小表关联的签到活动主键左移47位再同领奖记录主键做或运算,设计初衷非常简单:主键最高位置0,64 - 1 - 47 = 16,16位能存储最大的数字是65535,足够签到活动使用了;47位能存储的最大数字大概是100多万亿,也足够领奖记录使用了;最关键的是该组合基本能保证主键自增,这样在总表中插入数据的效率非常高,只有很少很少的页分裂出现。

四. 逻辑处理

其实逻辑处理主要就是业务的具体实现,说一下数据整合吧。

在上述的表结构设计下有几种策略:

1>若根据单个签到活动捞数据,则先判断该活动的领奖记录是否处于15天内,如果在的话直接走小表,不在则走总表;

2>若根据多个签到活动捞数据,则先将15天内的小表拿出来按照时间顺序组成一条链,走一个小表丢弃一个签到活动,若最终还有未拿到数据的签到活动,再走总表;

3>若没有签到活动等必要条件,则按照策略2将小表组成一条链,并将总表添加至链尾,依次访问链中的每张表做筛选来整合数据。

当然,我在签到活动表里有做数据迁移状态、迁移后的总表数据量等字段来协助处理整个数据集的分页问题,总之数据整合的方式有很多,所谓小鸡不尿尿,各有各的道。

让SQL飞:InnoDB下的锁策略​mp.weixin.qq.com
96bf642a748780b5b0504419dc1fcba3.png
让SQL飞:RR级别下的GAP锁范围​mp.weixin.qq.com
11f3d5e606f068244c8ffc4e034ac72d.png
在网络中狂奔:KCP协议​mp.weixin.qq.com
4267318ca21ca4fbba0869fcce6952e3.png
花5min就能搞清楚redis和zookeeper分布式锁的区别,太有必要读一下了​mp.weixin.qq.com
23436183bdc562e005f4ef320aebd91a.png
多线程·锁池与等待池(jdk1.8);在乐观锁下,真的有必要关心CAS中的ABA问题吗???​mp.weixin.qq.com
4267318ca21ca4fbba0869fcce6952e3.png
拜托,别再问我数据库性能优化了!​mp.weixin.qq.com
5f80263d33fc220045d30850c742f600.png

原创不易,点个赞再走呗!

Thanks♪(・ω・)ノ

--------------------------------

公众号:以镒称铢

出自<孙子兵法 · 军形>,故胜兵若以镒称铢,败兵若以铢称镒。胜者之战民也,若决积水于千仞之溪者,形也。

长按下图二维码关注,你将发现一个不一样的世界,君子引而不发,跃如也~

f7a68f762c517997d25a10ea174bafd6.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值