百亿级日访问量的应用如何作缓存架构设计?

  引言

“   微博日活跃用户 1.6 亿+,每日访问量达百亿级,面对庞大用户群的海量访问,良好的架构且不断改进的缓存体系具备很是重要的支撑做用。算法

本文由新浪微博技术专家陈波老师,分为以下四个部分跟你们详细讲解那些庞大的数据都是如何呈现的:后端

  • 微博在运行过程当中的数据挑战数组

  • Feed 平台系统架构缓存

  • Cache 架构及演进服务器

  • 总结与展望网络

  微博在运行过程当中的数据挑战

  Feed 平台系统架构

Feed 平台系统架构总共分为五层:数据结构

  • 最上面是端层,好比 Web 端、客户端、你们用的 iOS 或安卓的一些客户端,还有一些开放平台、第三方接入的一些接口。架构

  • 下一层是平台接入层,不一样的池子,主要是为了把好的资源集中调配给重要的核心接口,这样遇到突发流量的时候,就有更好的弹性来服务,提升服务稳定性。运维

  • 再下面是平台服务层,主要是 Feed 算法、关系等等。性能

  • 接下来是中间层,经过各类中间介质提供一些服务。

  • 最下面一层就是存储层。

       Feed Timeline

你们平常刷微博的时候,好比在主站或客户端点一下刷新,最新得到了十到十五条微博,这是怎么构建出来的呢?

刷新以后,首先会得到用户的关注关系。好比他有一千个关注,会把这一千个 ID 拿到,再根据这一千个 UID,拿到每一个用户发表的一些微博。

同时会获取这个用户的 Inbox,就是他收到的特殊的一些消息,好比分组的一些微博、群的微博、下面的关注关系、关注人的微博列表。

拿到这一系列微博列表以后进行集合、排序,拿到所须要的那些 ID,再对这些 ID 去取每一条微博 ID 对应的微博内容。

若是这些微博是转发过来的,它还有一个原微博,会进一步取原微博内容。经过原微博取用户信息,进一步根据用户的过滤词对这些微博进行过滤,过滤掉用户不想看到的微博。

根据以上步骤留下的微博,会再进一步来看,用户对这些微博有没有收藏、点赞,作一些 Flag 设置,还会对这些微博各类计数,转发、评论、赞数进行组装,最后才把这十几条微博返回给用户的各类端。

这样看来,用户一次请求获得的十几条记录,后端服务器大概要对几百甚至几千条数据进行实时组装,再返回给用户。

整个过程对 Cache 体系强度依赖,因此 Cache 架构设计优劣会直接影响到微博体系表现的好坏。

       Feed Cache 架构

接下来咱们看一下 Cache 架构,它主要分为六层:

  • 第一层是 Inbox,主要是分组的一些微博,而后直接对群主的一些微博。Inbox 比较少,主要是推的方式。

  • 第二层是 Outbox,每一个用户都会发常规的微博,都会到它的 Outbox 里面去。根据存的 ID 数量,实际上分红多个 Cache,普通的大概是 200 多条,若是长的大概是 2000 条。

  • 第三层是一些关系,它的关注、粉丝、用户。

  • 第四层是内容,每一条微博一些内容存在这里。

  • 第五层就是一些存在性判断,好比某条微博我有没有赞过。以前有一些明星就说我没有点赞这条微博怎么显示我点赞了,引起了一些新闻。而这种就是记录,实际上她有在某个时候点赞过但可能忘记了。

  • 最下面还有比较大的一层——计数,每条微博的评论、转发等计数,还有用户的关注数、粉丝数这些数据。

    Cache 架构及演进

       简单 KV 数据类型

接下来咱们着重讲一下微博的 Cache 架构演进过程。最开始微博上线时,咱们是把它做为一个简单的 KV 数据类型来存储。

咱们主要采起哈希分片存储在 MC 池子里,上线几个月以后发现一些问题:有一些节点机器宕机或是其余缘由,大量的请求会穿透 Cache 层达到 DB 上去,致使整个请求变慢,甚至 DB 僵死。

因而咱们很快进行了改造,增长了一个 HA 层,这样即使 Main 层出现某些节点宕机状况或者挂掉以后,这些请求会进一步穿透到 HA 层,不会穿透到 DB 层。

这样能够保证在任何状况下,整个系统命中率不会下降,系统服务稳定性有了比较大的提高。

对于这种作法,如今业界用得比较多,而后不少人说我直接用哈希,但这里面也有一些坑。

好比我有一个节点,节点 3 宕机了,Main 把它给摘掉,节点 3 的一些 QA 分给其余几个节点,这个业务量还不是很大,穿透 DB,DB 还能够抗住。

但若是这个节点 3 恢复了,它又加进来以后,节点 3 的访问就会回来,稍后节点 3 由于网络缘由或者机器自己的缘由,它又宕机了,一些节点 3 的请求又会分给其余节点。

这个时候就会出现问题,以前分散给其余节点写回来的数据已经没有人更新了,若是它没有被剔除掉就会出现混插数据。

实际上微博是一个广场型的业务,好比突发事件,某明星找个女友,瞬间流量就 30% 了。

突发事件后,大量的请求会出如今某一些节点,会致使这些节点很是热,即使是 MC 也没办法知足这么大的请求量。这时 MC 就会变成瓶颈,致使整个系统变慢。

基于这个缘由,咱们引入了 L1 层,仍是一个 Main 关系池,每个 L1 大概是 Main 层的 N 分之一,六分之1、八分之1、十分之一这样一个内存量,根据请求量我会增长 4 到 8 个 L1,这样全部请求来了以后首先会访问 L1。

L1 命中的话就会直接访问,若是没有命中再来访问 Main-HA 层,这样在一些突发流量的时候,能够由 L1 来抗住大部分热的请求。

对微博自己来讲,新的数据就会越热,只要增长不多一部份内存就会抗住更大的量。

简单总结一下:经过简单 KV 数据类型的存储,咱们其实是以 MC 为主的,层内 Hash 节点不漂移,Miss 穿透到下一层去读取。

经过多组 L1 读取性能提高,可以抗住峰值、突发流量,并且成本会大大下降。

对读写策略,采起多写,读的话采用逐层穿透,若是 Miss 的话就进行回写。对存在里面的数据,咱们最初采用 Json/xml,2012 年以后就直接采用 Protocol Buffer 格式,对一些比较大的用 QuickL 进行压缩。

       集合类数据

刚才讲到简单的 QA 数据,那对于复杂的集合类数据怎么来处理?

好比我关注了 2000 人,新增 1 我的,就涉及到部分修改。有一种方式是把 2000 个 ID 所有拿下来进行修改,但这种对带宽、机器压力会很大。

还有一些分页获取,我存了 2000 个,只须要取其中的第几页,好比第二页,也就是第十到第二十个,能不能不要全量把全部数据取回去。

还有一些资源的联动计算,会计算到我关注的某些人里面 ABC 也关注了用户 D。这种涉及到部分数据的修改、获取,包括计算,对 MC 来讲其实是不太擅长的。

各类关注关系都存在 Redis 里面取,经过 Hash 分布、储存,一主多从的方式来进行读写分离。如今 Redis 的内存大概有 30 个 T,天天都有 2-3 万亿的请求。

在使用 Redis 的过程当中,实际上仍是遇到其余一些问题。好比从关注关系,我关注了 2000 个 UID,有一种方式是全量存储。

但微博有大量的用户,有些用户登陆得比较少,有些用户特别活跃,这样所有放在内存里成本开销是比较大的。

因此咱们就把 Redis 使用改为 Cache,好比只存活跃的用户,若是你最近一段时间没有活跃,会把你从 Redis 里踢掉,再次有访问的时候再把你加进来。

这时存在一个问题,由于 Redis 工做机制是单线程模式,若是它加某一个 UV,关注 2000 个用户,可能扩展到两万个 UID,两万个 UID 塞回去基本上 Redis 就卡住了,没办法提供其余服务。

因此咱们扩展一种新的数据结构,两万个 UID 直接开了端,写的时候直接依次把它写到 Redis 里面去,读写的整个效率就会很是高。

它的实现是一个 long 型的开放数组,经过 Double Hash 进行寻址。

咱们对 Redis 进行了一些其余的扩展,你们可能也在网上看到过咱们以前的一些分享,把数据放到公共变量里面。

整个升级过程,咱们测试 1G 的话加载要 10 分钟,10G 大概要 10 分钟以上,如今是毫秒级升级。

对于 AOF,咱们采用滚动的 AOF,每一个 AOF 是带一个 ID 的,达到必定的量再滚动到下一个 AOF 里去。

对 RDB 落地的时候,咱们会记录构建这个 RDB 时,AOF 文件以及它所在的位置,经过新的 RDB、AOF 扩展模式,实现全增量复制。

       其余数据类型:计数

接下来还有一些其余的数据类型,好比一个计数,实际上计数在每一个互联网公司均可能会遇到,对一些中小型的业务来讲,实际上 MC 和 Redis 足够用的。

但在微博里计数出现了一些特色:单条 Key 有多条计数,好比一条微博,有转发数、评论数,还有点赞;一个用户有粉丝数、关注数等各类各样的数字。

由于是计数,它的 Value size 是比较小的,根据它的各类业务场景,大概就是 2-8 个字节,通常 4 个字节为多。

而后每日新增的微博大概十亿条记录,总记录就更可观了,而后一次请求,可能几百条计数要返回去。

       计数器 Counter Service

最初是能够采起 Memcached,但它有个问题,若是计数超过它内容容量时,会致使一些计数的剔除,宕机或重启后计数就没有了。

另外可能有不少计数它为零,那这个时候怎么存,要不要存,存的话就占不少内存。

微博天天上十亿的计数,光存 0 都要占大量的内存,若是不存又会致使穿透到 DB 里去,对服务的可溶性会存在影响。

2010 年以后咱们又采用 Redis 访问,随着数据量愈来愈大以后,发现 Redis 内存有效负荷仍是比较低的,它一条 KV 大概须要至少 65 个字节。

但实际上咱们一个计数须要 8 个字节,而后 Value 大概 4 个字节,因此有效只有 12 个字节,还有四十多个字节都是被浪费掉的。

这还只是单个 KV,若是在一条 Key 有多个计数的状况下,它就浪费得更多了。

好比说四个计数,一个 Key 8 个字节,四个计数每一个计数是 4 个字节,16 个字节大概须要 26 个字节就好了,可是用 Redis 存大概须要 200 多个字节。

后来咱们经过本身研发的 Counter Service,内存降至 Redis 的五分之一到十五分之一如下,并且进行冷热分离,热数据存在内存里,冷数据若是从新变热,就把它放到 LRU 里去。

落地 RDB、AOF,实现全增量复制,经过这种方式,热数据单机能够存百亿级,冷数据能够存千亿级。

整个存储架构大概是上图这样,上面是内存,下面是 SSD,在内存里是预先把它分红 N 个 Table,每一个 Table 根据 ID 的指针序列,划出必定范围。

任何一个 ID 过来先找到它所在的 Table,若是有直接对它增增减减,有新的计数过来,发现内存不够的时候,就会把一个小的 Table Dump((内存信息)转储,转存 ) 到 SSD 里去,留着新的位置放在最上面供新的 ID 来使用。

有些人疑问说,若是在某个范围内,个人 ID 原本设的计数是 4 个字节,可是微博特别热,超过了 4 个字节,变成很大的一个计数怎么处理?

对于超过限制的,咱们把它放在 Aux dict 进行存放,对于落在 SSD 里的 Table,咱们有专门的 IndAux 进行访问,经过 RDB 方式进行复制。

       其余数据类型:存在性判断

除了计数,微博还有一些业务,一些存在性判断。好比一条微博展示的,有没有点赞、阅读、推荐,若是这个用户已经读过这个微博了,就不要再显示给他。

这种有一个很大的特色,它检查是否存在,每条记录很是小,好比 Value 1 个 bit 就能够了,但总数据量巨大。

好比微博天天新发表微博 1 亿左右,读的可能有上百亿、上千亿这种总的数据须要判断。

怎么来存储是个很大的问题,并且这里面不少存在性就是 0。仍是前面说的,0 要不要存?

若是存了,天天就存上千亿的记录;若是不存,那大量的请求最终会穿透 Cache 层到 DB 层,任何 DB 都没办法抗住那么大的流量。

咱们也进行了一些选型:首先直接考虑能不能用 Redis。单条 KV 65 个字节,一个 KV 能够 8 个字节的话,Value 只有 1 个 bit,这样算下来每日新增内存有效率是很是低的。

第二种咱们新开发的 Counter Service,单条 KV Value 1 个 bit,我就存 1 个 byt,总共 9 个 byt 就能够了。

这样每日新增内存 900G,存的话可能就只能存最新若干天的,存个三天差很少快 3 个 T 了,压力也挺大,但比 Redis 已经好不少。

咱们最终方案是本身开发 Phantom,先采用把共享内存分段分配,最终使用的内存只用 120G 就能够。

算法很简单,对每一个 Key 能够进行 N 次哈希,若是哈希的某一个位它是 1,那么进行 3 次哈希,三个数字把它设为 1。

把 X2 也进行三次哈希,后面来判断 X1 是否存在的时候,从进行三次哈希来看,若是都为 1 就认为它是存在的;若是某一个哈希 X3,它的位算出来是 0,那就百分百确定是不存在的。

它的实现架构比较简单,把共享内存预先拆分到不一样 Table 里,在里面进行开方式计算,而后读写,落地的话采用 AOF+RDB 的方式进行处理。

整个过程由于放在共享内存里面,进程要升级重启数据也不会丢失。对外访问的时候,建 Redis 协议,它直接扩展新的协议就能够访问咱们这个服务了。

小结一下:到目前为止,咱们关注了 Cache 集群内的高可用、扩展性、组件高性能,还有一个特别重要就是存储成本,还有一些咱们没有关注到的,好比运维性如何,微博如今已经有几千差很少上万台服务器等。

       进一步优化

       服务化

采起的方案首先就是对整个 Cache 进行服务化管理,对配置进行服务化管理,避免频繁重启,另外若是配置发生变动,直接用一个脚本修改一下。

服务化还引入 Cluster Manager,实现对外部的管理,经过一个界面来进行管理,能够进行服务校验。

服务治理方面,能够作到扩容、缩容,SLA 也能够获得很好的保障。另外,对于开发来讲,如今就能够屏蔽 Cache 资源。

  总结与展望

最后简单总结一下,对于微博 Cache 架构来讲,咱们从它的数据架构、性能、储存成本、服务化等不一样方面进行了优化加强。

相关文章
相关标签/搜索