从宫廷剧看如何清洗数据

作者: | 更新日期:

继综艺被封杀后,现在命运之剑终于射中了宫廷剧。

本文首发于公众号:天空的代码世界,微信号:tiankonguse

一、宫廷剧的消失

2019年1月30号,我看到了一个新闻。
大概内容是广电总局要开始封杀宫廷剧了,卫视已经全面禁播了。
既然这样,那网络上相关视频网站也不远了。

当时我在朋友圈发布了一个状态:好消息 & 坏消息。

中午才发完这个朋友圈,晚上就收到消息,所有页面都要下掉“宫廷”两个字。
从此,世上再无“宫廷剧”了。

我所说的坏消息有很多面。
由于我处于视频行业,其中的一面就是这段时间有得忙了。
对于很多人来说,甚至晚上都睡不好觉了。

二、写扩散与读计算

对于一个视频,往往需要给视频打上一些标签,比如偶像、宫廷、玄幻、都市、喜剧、武打、悬疑等等。

视频的数量少则千万,多则上亿,如果每个视频直接储存对应的文本标签,则存在一个写扩散问题。
以宫廷为例,写扩散的具体含义就是所有的宫廷剧,都会储存“宫廷”这两个文字。
突然有一天,我们要改名字了,那就需要将标签是宫廷的视频都找出来,然后改成新的名字(需要修改成千上万的视频)。
这个扩散是很严重的,稍微有点经验的程序员都不会这样设计系统。

面对这个问题,大家常见的解决方案是 ID 化。
具体来说就是预先将所有的标签储存在一个地方(字典表),并分配一个唯一的数字。
如果一个视频要和这个标签关联起来,这个视频储存对应的数字即可。

这样做后读数据就麻烦一点,需要来计算转换ID对应的含义。
具体是第一次只能通过视频ID查出标签ID,然后还需要通过标签ID来查询标签对应的含义(字典表翻译)。

当然,通过这样一个小代价,我们就解决了上面的写扩散问题。
再来修改标签的含义时,我们只需要修改标签表里标签ID对应的含义。
这样只修改一个标签字典表的一行数据,所有页面的数据都可以瞬间生效了。

这个就对应上了一句名言:任何问题都可以通过加一层来解决。

三、读计算与缓存

上一小节中,我们看到可以把一些逻辑放在读的时候再来计算,可以解决很多写扩散问题。
于是越来越多的逻辑都提升到了读服务上。

这个计算服务其实就是两年前我写的那篇文章《每秒千万每天万亿级别服务之诞生》。
当然,经过两年,这个服务的量级早已翻了很多倍。
目前已经到达每秒2亿每天亿亿了。

这个服务当时主要解决两个问题。
第一就是聚合众多数据源,第二就是读时统一计算各种写扩散的数据。

随着访问量越来越大,两个问题引出了另外一个问题:撑不住了。
假设来了10万每秒的量,数据源那么多,不是都可以撑得住这个量的;计算逻辑那么多而且那么重,也不是所有计算逻辑都计算的过来。

这就需要在计算服务之上加一个缓存来抗量。

加了缓存是解决了访问量的问题,但也引入一个更大的问题:数据一致性。
作为一个资料系统,最终一致性是可以接受的,但是如果最终一致的时间太长那就不了接受了。

而储存容量不变的话,数据的缓存时间与命中率在一定范围内是正相关的。
而命中率又和逻辑层以及数据源息息相关,毕竟底层不能抗量。

面对日益增长的访问量,数据一致性、储存、命中率、透传量这些关系变得更为复杂。
最后,通过一系列优化,初步缓解了缓存问题,并写下了一篇文章《每秒千万级别的量是重生还是炼狱?

通过这些优化,命中率得到了提高、核心数据的数据一致性得到了保障、透传量也大大降低了。
解决数据一致性的关键就是通知,多做的工作只是通知带来的问题,如数据一致性、通知量等问题。

就这样,在读计算之上加了一层缓存,解决了面临的新问题。

四、清洗全局数据与局部数据

上面可以看到,读计算解决了写扩散的问题,缓存解决了计算量大的问题。
但是那些都是在正常情况下的一些流程,如果数据出现异常是怎么办呢?

之前曾遇到过底层数据全部异常的情况,还写了一篇文章《数据脏了怎么办》。
总结来说,面对全局数据异常,缓存系统需要有刷新全局数据的能力。
而最优的答案就在上篇文章里面,这个功能我当时就实现投入到生产系统中了。

这次底层字典表变更,属于读计算层的变更,在上层还有一层缓存层。
读计算逻辑变更,缓存是无感知的,这就导致数据迟迟不能更新。
另外,上面的文章也提到,加了通知后,为了提高命中率,缓存时间也变得很长很长。

字典表的变更+缓存时间很长+无通知,这就导致这个变更对外迟迟不能生效。
而且这个只是一个Id的变更,并不是全局变更,这里导致全局清理数据功能不起作用了。

为啥呢? 资料系统是一个三级批量系统(数据类型+数据id+字段id),数据缓存是按字段维度储存的。
数据类型接近上百个,id的量是上亿级别,字段是上千级别的,这样相乘就是10亿亿的几笔,这个数据量太多了。

对于全局数据异常时,配置了洗数据功能以及频率,慢慢洗就行了,最终所有数据都会刷新。
而对于局部数据异常时,这个功能就是牛刀杀鸡了,另外也有点浪费。

当然,这个系统设计的时候,就支持每个字段id单独配置缓存时间,这就使得可以做到对局部数据来控制回源时间。
但是问题是回源时间并不能代替洗数据功能,因为我们不知道什么时候可以把回源时间调整回来。

当然,这个时间也不是完全未知的。
假设我们的缓存时间是一天时间,那至少要在一天后才能设置回来,不然就可能依旧读到旧数据了。

读到这里,有心的朋友应该就猜到了,字段维度也可以增加洗数据的能力。
这样局部洗数据就相当高效精确了,恩就是这样,只是目前还没有这个功能。

不过目前时间还算充足,走过期时间慢慢淘汰就行了。
如果真是紧急,调小缓存时间就行了。

-EOF-

本文首发于公众号:天空的代码世界,微信号:tiankonguse
如果你想留言,可以在微信里面关注公众号进行留言。

关注公众号,接收最新消息

tiankonguse +
穿越