UNION重生之架构篇

作者: | 更新日期:

上篇文章介绍了UNION加了缓存后遇到的问题与优化, 这里是整理一下其中的架构.

零、进化无止境

面对一个问题时, 不管宣称找到如何”完美”的解决方案, 都会引入一系列更”复杂”的问题!
我很喜欢这一句话,因为这一句话是我自己说的.

野蛮时代进入工具时代, 进化的速度加快了, 更多的问题也暴露出来了.

我想UNION的终极问题是: UNION存在的意义是什么?

一、我们把它隔离出来吧!

不存在通过加一层解决不了的架构, 如果存在, 那就加两层.

先来看看之前Union的架构.

在上篇文章已经提到这个架构面临的核心问题是 众多数据源扩容难服务使用多线程模式维护成本高.

“我们把它隔离出来吧!”, 有一天, 老板和几位高工拉上我开会时说了这么一句话. 他们经过分析, 众多数据源中, 最最重要的数据其实不多, 一些不重要的播放量和UGC视频数据比较占容量, 可以保持不变.
于是就计划把核心数据独立储存起来, 上层再加个缓存降低底层聚合层的压力, 最后聚合层使用通用网络框架重写.

切换架构如下:

缓存层的本地缓存储存是一维的. key是 table_key_field_version_platform_ext, value就是对应的值的序列化以及缓存相关信息.
在缓存REDIS里, 由于可以使用HASH, 所以key是 table_key, sub_key是 field_version_platform_ext, value和本地结构一致.

二、中转

对于之前经常听说但没去了解原理的东西, 总是感觉很神圣, 比如中转, tiankonguse说.

对于一个缓存层,肯定会面临着数据一致性的问题.
面对这个问题, 我们的结论是本地缓存延迟一分钟生效, REDIS依靠接收通知更新, 三个小时兜底回源.

为什么不在本地缓存上直接接入中转呢?
问了ZMQ中转的负责人, 机器太多, 中转服务器会撑不住的.
即使ZMQ中转废弃了, 未来的微博中转也接受不了这么多机器.

又考虑到要废弃就中转,引入新中转, 于是我这个接中转的服务需要灵活点, 不然后续改造风险太大.
于是最终架构就是下面的样子了:
插件可以是任何适配器,中转,服务,接口等, 后来统一写上线时发出了微博中转, 这里也快速接入了微博中转.

之前听说有人做了法拉第中转, 后来又做了个法拉利中转, 最后还听说要做一个新中转, 果然有必要做成适配器.

三、热key策略

墨菲定律: 只要有可能发生的事情, 一定会发生.
Anything that can go wrong will go wrong.

由于一些特殊原因, 导致共享内存性能特别低, 缓存层REDIS的流量特别大.
虽然第一期目标隔离缓存层完成了, 但是缓存层需要砸更多的机器来承担那么大的访问量, 缓存层REDIS的流量也越来越大.

中间发生了一次故障, 于是我加了一个优化: 冷数据不走缓存了.
对访问的key进行计数, 超过指定访问量后才走缓存.

热key策略上线后效果明显, 缓存层的CPU下降不少, REDIS的流量下降一些, 不过聚合层的访问量也上升不少, 但是这个没有解决根本问题.

四、版本号系统

真正的缓存需要实时更新, 无名者说

缓存的REDIS压力依然比较大,于是就设计一套版本号系统上线了.
版本号上线后, 就可以做到数据毫秒更新了,也可以加大本地缓存时间了.

版本号系统上线后整体命中率其实没变化多少, 因为之前缓存REDIS就是过期时间三个小时, 现在转移到本地缓存了, 不过降低了缓存REDIS的压力.

五、一致性hash

对于multiget命令来说,分布式部署更多的节点,并不能提升multiget的承载量,甚至出现节点数越多,multiget的效率反而会降低,这就是multiget黑洞。

其实现在缓存层的命中率已经可以了, 但是上线版本号功能后加大了本地缓存的超时时间,内存可能会不够,所以需要把数据拆分一下,于是上线了一致性hash.

考虑到将所有节点挂载一个hash会使访问量扩大几十倍, 所以要分很多小set, 一个set一组一致性hash.

架构如下图:

multiget黑洞是一件很恐怖的事情.
假设四台机器一组小set, 每个机器访问量1w/s, 一组总共4W/s.
由于请求是批量的, 一个请求将被拆分为4个, 这样到达下层的请求量就是16W/s, 单机4w/s.
路由层和实际处理数据层是一台机器, 所以单机请求就有5W/s了.

本来一台机器1w/s的请求, 上了一致性HASH后请求量变成5w/s了, 网络框架的收发包部分逐渐也成为CPU的瓶颈了.
这个网络框架又不支持多端口, 于是只好一台机器部署多个相同的服务然后开不同的端口, 来缓解框架收发包的瓶颈.

其实上线hash作用不大, 在海量数据下, 常数倍的扩大内存,起不到很好的效果.

六、过载保护

高性能服务器需要柔性服务, 无名者说

之前缓存REDIS之所以没有过载保护, 是因为之前负载统计都是进程内统计的, 而我的服务是多进程, 由于短时间内各进程实际处理量偏差较大, 这样统计意义就不大了.
于是我又实现了一个基于共享内存的单机过载保护库.

七、通用数据加载

对于重复的事情, 就做成工具吧, 无名者说

一般业务的数据都是在MYSQL中,让每个接入的业务写同步程序调用统一写来同步数据还是存在成本的.
所以这里有了一个通用的dataload程序,当然这个程序当初诞生UNION的时候, 那位伟大的同事就一手把这些都实现了.

八、总结

UNION存在的意义是什么,我常自问

到目前为止, 总体架构就是下面的样子了.

下篇文章介绍UNION的运营部分吧.


长按图片关注公众号, 接受最新文章消息.

点击查看评论

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

关注小密圈,学习各种算法

tiankonguse +
穿越