微架构设计:微博计数器的设计

做者:cyduhtml

 

背景:  
每一条微博的转发和评论背后都是一串串说不完的故事,可是今天主要讲的是  计数服务,计数服务详尽地记录着每条微博  被评论的次数 和  被转发的次数,固然也还有更多的喜怒哀乐都记录于此。 
 
数据量:  
微博总数量:  千亿级 并且每秒都在飞速增加中。每条微博都有一个64位的惟一id。
访问量:  每秒百万级 还在稳步增加中。  根据微博的id来访问。
 
主要接口: 
增长评论数 (默认为0)
增长转发数 (默认为0)
获取评论数 
获取转发数
获取评论数 + 获取转发数  (这个接口访问量最大)
 
评论数和转发数,你均可以认为是 32位的整形数值。不会是负数,默认是0。 
 
要求: 
因为用户对于数字很是的敏感(想一想你好不容易拉到一位粉丝,可是粉丝数没涨的痛苦吧。),因此咱们要求数据很是准确,延迟极低(1s之内),服务稳定性极高(千万别由于某大妈扫个地拨了插座就把数字弄没了...) 
 
作为架构师,固然也须要全方位地考虑架构成本问题,而后去作各类的折衷。 这里主要考虑的成本是: 机器成本,开发成本,维护成本。 
 

有兴趣的架构师和准架构师们能够一块儿思考,怎样才能用最少的机器,最短期内开发出最易维护的计数器系统。。。固然,得知足咱们数据量,性能和高可用的要求。 mysql

对这一块很是的感兴趣,并且有靠谱的想法和建议,烦请私信简历给 @cydu 或者 @微博平台架构,咱们这里还有大量相似的问题期待着你来解决!   固然,也能够直接评论一块儿讨论。  
 
PS: 后面我会给出咱们的理解和解决的思路,期待你们一块儿来优化。
 
Update: 更新了数据持久化和一致性保证相关的内容,多谢 @lihan_harry @郑环Zheng @51刘达 等同窗的提醒。 
 
Update2: 更新了 对于weibo_id key的优化,使用前缀压缩,能够节省近一半的空间。 感谢 @吴廷彬 @drdrxp 的建议!
 
Update3: 更新了 对于value 使用二维数组,多列进行压缩编码的优化思路, 再次感谢 @吴廷彬 的建议,
 
Update4: 更新Redis方案下内存使用的估算, 感谢 @刘浩bupt 的提醒。 
 
上周挖了一个坑
([微架构设计]微博计数器的设计(上) http://qing.weibo.com/1639780001/61bd0ea133002460.html ) ,
虽然挖这个坑的动机是很不纯的(很明显的招聘软文, 很是欣慰的是确实收到了很多靠谱的简历, 但愿简从来得更猛烈一些! ), 可是和你们讨论的过程当中,仍是收获很大的, 也认识了很多新朋友。 
 
对于一个简单的计数服务来讲,确实很是的简单,咱们能够有不少的解决方案: 
 
方案一:  直接上mysql 
 
这个不用多说了吧,足够的简单暴力。 可是在产品发展的初期快速迭代的阶段,他可以解决不少的问题,也不失为一个不错的解决方案。 
 
数据量过大怎么办? 
 
对于一亿甚至几亿如下的数据规模来讲,拆表可以解决不少问题,对于微博计数器来讲至少有两种经典的拆法: 
 
一. 按id取模,把数据拆分到N个表当中去。 这个方案的悲剧是: 扩展性很差,很差加表,数据一旦满了,加起来很郁闷。虽然能够预先多分一些表,可是对于weibo这种快速增加的业务来讲,严重影响了业务的快速增加需求。 
 
二. 按id的时间来分段拆表,满了就建新表。 这个方案的悲剧是: 冷热不均,最近的weibo确定是被访问最频繁的,而老的库又基本没有访问。 能够经过冷热库混合部署的方案来缓解,可是部署和维护的成本很是大。 
 
数据量从亿上升到千亿后,这个问题的本质就发生了变化,维护上千张表,热点还各不相同须要常常切换调整,这是一件很是悲剧的事情。。。
 
访问量太大怎么办? 
 
应对访问量,也有不少的经典的方法: 
 
一. 上Cache(Eg: Memcache), 访问时先访问Cache,不命中时再访问mysql. 这样作有两个郁闷点: 空数据也得Cache(有一半以上的微博是没有转发也没有评论的,可是依然有大量的访问会查询他); Cache频繁失效(因为计数更新很是快,因此常常须要失效Cache再重种,还会致使数据不一致);作为最基础的服务,使用复杂,客户端须要关注的东西更多
 
二. 更好的硬件解决。 上FusionIO + HandleSocket + 大内存 优化.  经过硬件的方式也可以解决问题,可是这是最典型的Scale up的方案。虽然彻底不用开发,可是硬件成本不低,且对于更复杂的需求,以及流量快速的增加,也很难应对。 
 
优势: 
一. 不用开发, 码农们能够用写代码的时间出去泡泡妞。
二. 方案成熟, 数据复制,管理,修复方案都很成熟。 
 
 
缺点: 
一. 对大数据量和高并发访问支持很差,很是的力不从心。
二. 维护成本和硬件成本都很高。 
 
总的来讲: Mysql分表 + Cache/硬件 加速的方案 对于数据规模和访问量不是特别巨大的状况下,很是不错的解决方案,可是量大了以后很是不合事宜.  
 
既然 Mysql不行,那用NoSQL 呢? 
 
 
方案二: Redis 
 
作为一个简单的内存数据结构来讲,Redis提供很是简单易用的访问接口,并且有至关不错的单机性能。 经过incr实现的 Counter Pattern,用来作计数器服务,更是简单轻松。 经过上层的分表,增长slave等方式,堆一些机器,也可以解决大数据量和高并发访问的问题。 
 
可是Redis是纯内存的(vm机制不成熟并且即将被废弃,咱们线上确定是不敢直接使用的!),因此成本也不算低,咱们简单的来估算一下数据存储量(下面都是按照Redis 2.4.16的实现,在64位系统,指针为8字节来估算的) : 
 
假设 key 为8字节,value为 4字节,经过incr存储的话:  
一个 value 经过 createStringObjectFromLongLong 建立一个robj,因为value在LONG_MIN 和LONG_MAX 之间,因此能够将value用 ptr指针来存储,须要占用 sizeof(robj) = 16 字节;
一个key(即微博id) 最长64位数字(Eg: 5612814510546515491),但经过 sdsdup 以字符串的形式存储,至少须要 8(struct sdshdr)+19+1 = 28字节;
为了存到Redis 的dict里面,须要一个dictEntry对象,继续 3*8 = 24字节; 
放到db->dict->ht[0]->table中存储dictEntry的指针,再要 8个字节;
 
 
存储一个64位key,32位value的计数,Redis也至少须要耗费: 16 + 28 + 24 + 8 = 76 字节。 1000亿个key全内存的话,就至少须要 100G * 76 = 7.6TB的内存了(折算76G内存机器也须要100台!)。  咱们的有效数据实际上是 1000亿*32位 = 400GB,可是却须要7.6TB来存储,内存的有效利用率约为:  400GB/7600GB  = 5.3%. 
 
即便这样,对于不少热点的数据,只有一个副本,单机性能不够,系统的稳定性也没法保证(单机Down掉咋办?), 还须要复制多份。 再算上为了不内存碎片引入的jemalloc的内存开销; 再算了dictExpand等须要的临时内存空间; 再算上系统要用的内存开销。。。那要的机器就更多了,保守估计须要300-400台以上的机器。 
 
总的来讲: Redis作为优秀的内存数据结构,接口方便,使用简单,对于小型数据量的中高访问量的计数类服务来讲,是一个很不错的选择,可是对于微博计数器这种极端的应用场景,成本仍是没法接受! 
 
还有一些同窗提出了用 Cassandra,MongoDB 等其余NoSQL的方案,不管是从可维护性的角度,仍是从机器利用率的角度,都很难以接受(有兴趣的同窗能够仔细分析一下)。 
 
普通的NoSQL也不行,那怎么办? 尝试定制咱们本身的Counter!
 
Update4:
//@刘浩bupt: @cydu 刚刚仔细阅读了文中redis容量预估的部分,有两点小瑕疵:1.对于value的存储,文中估算了16个字节,其实这部分开销是能够节省的。createStringObjectFromLongLong函数,对于小于REDIS_SHARED_INTEGERS的value值,不会额外分配空间。REDIS_SHARED_INTEGERS默认是10000,调大一些能够知足大部分需求
 
//@刘浩bupt: @cydu 2.是能够评估下使用zipmap达到的内存利用率。redis不是只有string->string的kv存储,仍是有一些能够挖掘的东西的。instagram在其工程博客中介绍过(http://t.cn/S7EUKe),改用zipmap后,其存储1M的数据,内存占用由70M优化到了16M。鉴于新浪微博大量的使用redis,定制redis实现服务也是个思路。
 
感谢 @刘浩bupt 同窗帮我指出对于Redis容量预估的不许确,经过Redis自带的 REDIS_SHARED_INTEGERS 机制确实可能大量节省value所占的内存,可是因为这个方案须要依赖存储shared_int的指针,不太好迁移到方案三里面去。 
Zipmap这个优化的思路是至关不错的,对于通用的Redis的使用,咱们会持续关注。 
 
方案三: Counter
 
计数器是一个普通的基础服务,可是由于数据量太大了,从而量变引起了质变。 因此咱们作Counter时的一个思路就是: 牺牲部分的通用性,针对微博转发和评论的大数据量和高并发访问的特色来进行定点优化。 
 
1. 大量微博(一半以上)是没有转发,或者没有评论,甚至是没有转发也没有评论。 
 
针对这种状况的优化: 抛弃 存储+Cache的思路,  由于这些为0的数据,也必须进到Cache中(不管是旁路仍是穿透),由于查询量并不小,这对于咱们Cache的利用率影响很是很是的大(有一半的数据是空的。)  而咱们采用相似 存储即Cache(存储自己就在内存中) 时,这一类的数据是能够不存储的,当查不到的时候,就返回0。   
 
经过这种状况,1000亿个数字,咱们能够减小3/5,即最多只须要存 400亿个数字。这算是最经典的稀疏数组的优化存储方式了。  
 
2. 微博的评论数和转发数 的关联度很是的高。
 
他们都有相同的主Key, 有大量转发的微博通常也有评论,有大量评论的通常转发量也不小。 并且访问量最大的Feed页基本上取评论数的时候,也会取转发数。。。
 
针对这种状况的优化: 咱们将评论数和转发数 能够考虑存储在一块儿,这样的话,能够节省大量key的存储空间。 由 微博ID+评论数; 微博ID+转发数 变为: 微博ID+评论数+转发数的结构。 
 
PS: 这个优化和上一个优化是有一些小冲突的,部分有转发没有评论的微博,须要多存一个0; 可是通过数据评估,咱们发现这个优化仍是至关必要的: a. key存储的空间比评论数还要长,一个8字节,一个4字节; b. 对于应用层来讲,批量请求能够减小一次访问,可以降请求的压力,同时提高响应的时间; 
 
(具体的数字不方便透露,可是这个结论你们能够随机抽取一批公开的微博来验证)
 
3. 数据结构的优化
 
经过方案二中Redis对内存使用的分析,咱们发现是很是"奢侈"的, 大量的重复存储着指针和预留的字段,并且形成大量的碎片内存的使用, 固然Redis主要是出于通用性的考虑。 针对这种状况: 
@果爸果爸 同窗设计了一个更轻量更简单的数据结构,能更好的利用内存,核心思路: 
 
a. 经过下面的item结构来存储 转发和评论数: 
struct item{
   int64_t weibo_id;
   int repost_num;
   int comment_num; 
};
存储数字,而不是字符串,没有多余的指针存储, 这样的话,两个数字只占 16个字节; 
 
b. 程序启动的时候,开辟一大片的内存 (table_size * sizeof(item)) 并清0他。 
 
c. 插入时: 
h1 = hash1(weibo_id);
h2 = hash2(weibo_id);
 
若是 h1%table_size 是空的,则把item存储到这个位置上; 
不然 s=1 并找 ( h1 + h2*s ) % table_size 的位置,若是还不空的话,s++继续找空位。。。
 
d. 查询时: 
和插入的过程相似,找到一个数据后,比较weibo_id 和 item.weibo_id 是否一致,一致
则表示查到,不然查到空的则表示为值为0; 
 
e. 删除时: 
查找到所在位置,设置特殊的标志; 下次插入时,能够填充这个标志位,以复用内存。。。
 
通过咱们实测,当2亿数据这种长度的数组中,容量不超过95%的时候,冲突率是能够接受的
(最悲剧的时候可能须要作几百次的内存操做才能找到相应的空位, 性能上彻底可以接受; )
 
通过这个优化以后,咱们的总数据量变成了: 
400亿 * 16B = 640GB; 基本是方案二的 十分之一还少! 
 
4. 转发和评论数 Value的优化
 
继续观察,咱们发现大量的微博,虽然有转发和评论,可是值通常都比较小,几百或者几千的,
超过几万的weibo不多(数据调研显示在十万分之一如下)。
 
因此咱们把 item 升级为: 
struct item{
    int64_t weibo_id;
    unsigned short repost_num; 
    unsigned short comment_num;
};
对于转发数和评论数大于 65535 的weibo,咱们在这里记录一个特殊的标志位FFFF,而后去
另外的dict中去查找(那边不作这个优化)。事实上,还能够把 unsigned short优化为 int:12 之类的极端状况,可是更复杂,且收益通常,因此咱们仍是选用unsigned short。 
 
通过这个优化后,咱们的总数据量变成了: 
400亿 * 12B = 480GB, 这个数据量已经差很少是单机可以存储的容量了。 
每秒的查询量由100W变成了50W, 更新量每秒只有数万没有变化,和查询量比能够先忽略。
 
4.1 补充 Value的优化
 
@吴廷彬: 另外,64bit value能够用utf-8的相似思想再压缩。最后由于cpu/mem不是瓶颈,能够将weibo_id和后面的value分开放在两个数组里面,对应的index同样便可。而后会发现value数组里面的64bit不少位全是0,或许能够考虑以K为单位的数据作简单数据压缩放入内存里面,这个压缩比应该是惊人的。
 
@吴廷彬: 回复 @cydu:value能够用二维数组怎么样。 若是1K为单位压缩则每一行表示1K个数据。而后对数据进行压缩写入。 通常可能每行只用100个字节?
 
@cydu: 这样确实能够,变长编码会有意义,反正cpu应该不是瓶颈,有更新的时候整块从新编码,取也是全取出再解压。还一个好处是我加列更方便了,如今我加列的代价实际上是很高的。 
 
最先的时候,我也想过用变长压缩,可是思路一直局限在一个value里面作压缩,因为只有两列,咱们又是用定长的存储,一方面变长有开销(标志位标志用了多少位来表示),另外一方面定长开给的32位省出来也没有合适的用处(能够和key的优化结合起来,用更少的字段)。  @吴廷彬 一说二维数据,立马变长压缩的好处就显现出来了。 
 
我能够把key单独存储,把value,按 1024个value甚至更多个value 压缩到一个mini block中存储,在定长的状况下,这个mini block的size是 1024*32 = 4K. 可是事实上,这4K中包含了大量的 0, 我不用本身整复杂的变长编码,直接拿这4K的数据作LZF压缩,只存储压缩后的数据就好了, 取的时候先解压缩。 具体的压缩效率得看数据才能定,可是根据通常文本的压缩到 50% 应该是很是轻松的,也就是说,至少能够节省 400亿 * 2 = 80GB的内存。 
 
这个方案最大的一个好处还不在于这80GB的内存的节省,而是: 
1. 我前面优化提到的 大于 65535 的转发和评论,我能够考虑简单作了,反正变长嘛,不影响,整个方案是简化了的。(固然须要具体的数据测试一下,验证哪一个更好)
 
2. [ 至关重要!!] 对于微博的计数,其实咱们是有加列的需求的,好比其余的相似评论数的数字,我原来的方案中,加列的代价是至关高的,须要重开一个大数组,还要事先设好hint(对于新业务来讲,hint值的很差选取,可是他对性能和内存的使用率影响又是致命的!),而这个方案,不管你加多少列都其实没啥关系, 用内存的长度只和你真实的数据量相关! 
 
通过这个优化后,我保守的估计,咱们可以在以前的基础上,再节省 80GB的内存! 
 
5. key的优化
 

@吴廷彬 很好的文章。weibo_id是8byte的,压缩可以压到接近4byte.假如一堆数据是AB,AC,AD,AE,AF, XE,XY,XZ.把他在内存里面A开头放在一坨内存,X开头放在另一坨,里面只用存B,C,D,E,F和Y,Z. 基本上能减小4个字节。能省掉40G*4=160G?redis

 

@drdrxp: 存储分红2^24个区,weibo_id%(2^24)指到区的号上,记录中再用40bit 存储weibo_id/(2^24),记录中另外12bit 存转发,12bit存评论, 1条记录总共8字节,480G能够优化到320G. 若是能实际考察下评论转发数的分布应该能够更加优化1些.sql

 
感谢 @吴廷彬 @drdrxp 提的这个建议,这一块的优化空间确实很是的大。后面其实有提到,咱们会根据时间段或者根据weibo id把大的table 划分红多个小的table(主要是为了可以序列化到磁盘腾空间给更热的数据)。 因此在一个小table里面的数据都是weibo_id比较接近的,Eg: 5612814510546515491, 5612814510546515987, 咱们能够把这64位key中相同的高32位归并起来。作为小table的属性(prefix),就没必要每一条都存储了。 8字节的key,至少可以节省 4字节。 
 
struct item{ 
    int weibo_id_low;
    unsigned short repost_num;
    unsigned short comment_num;
};
 
通过这个优化后,咱们的总数据量变成了: 
400亿 * 8B = 320GB, ^_^ 
 
也感谢 @drdrxp 的建议,以前也考虑过12bit来存评论数和转发数,确实可以优化很多,可是因为多出来的bit不知道干吗,就没搞了,呵呵。你的建议和 @吴廷彬 提的建议都主要是在key上作文章,很赞! 
 
6. 批量查询
 
对于Feed页来讲,一次取到N条微博,而后查询他的计数,这里能够很好的批量化查询来优化响应时间。
以一次批量访问10个微博的计数来讲,对于Counter碰到的压力就是 5W requests/second, 100W keys/second;
 
对于全内存的简单服务来讲,单机已经基本可以扛 5W+ 的请求了。 
 
 
7. 冷热数据 
 
继续看这400亿个数字,咱们发现,访问热点很是的集中,大量去年,甚至前年的weibo无人访问。
本能的可能想到经典Cache的作法,热的数据在内存,冷的数据放磁盘。 可是若是引入lru的话,意味
着咱们的struct item得膨胀,会占用更多内存。并且对于0数据也得Cache。。。
 
对于这种状况,咱们设计了一个很是简单的内存和磁盘淘汰策略,根据weiboid的区间(实际上是时间)
来进行淘汰,按区间划分,超过半年的dump到磁盘上去,半年内的继续留存在内存,当少许用户挖坟的时候
(访问很老的微博并转发/评论),咱们去查询磁盘,并将查询的结果放到 Cold Cache当中.
 
为了方便把旧的数据dump到磁盘,咱们把那个大的table_size拆成多个小的table,每一个table都是不一样的
时间区间内的weibo计数,dump的时候,以小的table为单位。
 
为了提升磁盘的查询效率,dump以前先排序,并在内存中建好索引,索引建在Block上,而非key上。
一个Block能够是4KB甚至更长,根据索引最多一次随机IO把这个Block取出来,内存中再查询完成; 
 
通过这个优化,咱们能够把将近200G的数据放到磁盘当中, 剩余120GB的数据仍然留在内存当中。  并且即便随着weibo数愈来愈多,咱们也依然只要保存120GB的数据在内存中就好了,磁盘的数据量会增长,热点的数据也会变化,可是总的热点数据的量是变化不多的! 
 
[微架构设计]微博计数器的设计(下)
 
 
8. 数据的持久化
 
对于Sorted 部分的数据,一旦刷到磁盘后,就只会读,不会修改,除非在作和Cold Block作merge的时候,才会重写 (目前这一块merge的逻辑没有实现,由于必要性不高)。
 
对于内存中的数据,咱们会按期把 Block 完整的dump到 磁盘中,造成 unsorted block。 而后每一次内存操做都会有相应的Append log, 一旦机器故障了,能够从 磁盘上的Block上加载,再追加Append log中的操做日志来恢复数据。 
 
固然,从整个架构上,一旦Counter崩溃等严重错误,致使数据错误,咱们还能够经过 具体数据的存储服务上把数据从新计算出来,恢复到Counter当中。 固然这种计数的代价是很是高的,你想一想姚晨那么多粉丝,counter一遍很恐怖的, 咱们也另外作了一些二级索引之类的简单优化。 
 
9. 一致性保证
 
@l ihan_harry上边文章提到计数对正确性要求高,因为计数不知足幂等性。那么这个问题是怎么解决的 
 
@ cydu回复  @lihan_harry :是这样的,前面有一个消息队列,经过相似于transid的方案来作除重,避免多加和少加; 固然这里主要是指用主从的结构,incr累加,即便是最终一致也不至于太离谱; 另外,咱们还有作实际的存储数据到Counter的按期数据校验,之后面的数据存储为准 
 
@ 郑环Zheng貌似还会有写请求单点问题,老数据的删除递减走硬盘,多机房冗余,机器假死宕机数据会不会丢失,删微博的时候还要清空相关计算不呢 
 
@ cydu回复  @郑环Zheng :是的,为了incr 的准确性,仍是使用Master-Slave的结构,因此Master的单点问题依然存在,须要靠主从切换,以及过后的数据修复来提升数据的准确性。 
 
10. 分布式化
 
出于稳定性的数据冗余的考虑,并且考虑到weibo如今数据增加的速度,在可预见的将来,数字会
变成1500亿,2000亿甚至更高。 
 
咱们在上层仍是作了一些简单的拆分的,按照weiboid取模,划分到4套上(主要是考虑到后续数据的增加),
每套Master存储后面又挂2个Slave, 一方面是均摊读的压力,另外一方面主要是容灾(当主挂掉的时候,
还有副在,不影响读,也可以切换)
 
so, 我仍是没能单机扛住这1000亿个数字,和每秒100W次的查询。。。只好厚着脸皮问老大申请了十几台机器。
 
优势: 单机性能真的很好,内存利用率很高,对后续扩展的支持也至关不错。 
缺点: 咱们码农泡妞的时间少了,得抽空写写代码。。。可是,若是不用写码的话,那码农还能干吗呢?
 
总之: 对于这种极端的状况,因此咱们采用了一样极端的方式来优化,牺牲了部分的通用性。
 
 
方案四: Counter Service
 
方案三出来后, 微博计数的问题是解决了,可是咱们还有用户关注粉丝计数呢,好友计数,会员计数... 
数字社会嘛,天然是不少数字,每个数字背后都是一串串的故事。 

[微架构设计]微博计数器的设计(下)


针对这种状况,咱们在Counter的基础上,再把这个模块服务化,对外提供一整套的 Counter Service, 
并支持动态的Schema修改(主要是增长),这个服务的核心接口相似于下面这个样子: 
 
 
//增长计数, 计数的名字是: "weibo" 
add counter weibo                           
 
// 向"weibo"这个计数器中增长一列,列名是 weibo_id, 最长为64位,通常也是64位,默认值为0, 并且这一列是key
add column weibo weibo_id hint=64 max=64 default=0 primarykey 
 
// 向"weibo"这个计数器中增长一列,列名是 comment_num, 最长为32位,通常是16位,默认值为0 
add column weibo comment_num hint=16 max=32 default=0  suffix=cntcm     
 
// 向"weibo"这个计数器中增长一列,列名是 repost_num, 最长为32位,通常是16位,默认值为0
add column weibo repost_num hint=16 max=32 default=0  suffix=cntrn    
 
// 向"weibo"这个计数器中增长一列,列名是 attitude_num, 最长为32位,通常是8位,默认值为0
add column weibo attitude_num hint=8 max=32 default=0  suffix=cntan    
 
.... 
 
// 设置weibo计数中 weibo_id=1234 的相关计数,包括 comment_num, repost_num, attitude_num
set weibo 1234 111 222 333
 
// 获取weibo计数中 weibo_id=1234 的相关计数,包括 comment_num, repost_num, attitude_num
get weibo 1234 
 
// 获取weibo计数中 weibo_id=1234 的相关 comment_num
get weibo 1234.cntcm
 
// 增长weibo计数中 weibo_id=1234 的相关 comment_num
incr weibo 1234.cntcm
 
.... 
 
当 add column的时候,咱们会根据hint值再增长一个大的table (table_size * sizeof(hint)), 
可是这里不存储key,只有value,用原来item那个大table的相同key。 对于超过部分依然是走
另外的存储。 
 
经过计数器服务化以后,最大的好处就是,后面咱们再要加计数,有可能量没有那么大,能够很快的
建立出来。。。 
 
缺点: 
    对于非数值类的key名,可能会退化到字符串的存储,咱们能够经过简化的base64等机制来缩短空间;
    对于频繁修改老的数据,致使cold buffer膨胀的问题,能够经过按期的merge来缓解(相似于Leveldb的机制);
 
 
方案五: 你的方案
 
 
对于工程类的问题,其实永远不会有标准的答案,一千个架构师能给出一万个设计方案来,并且没有
一个是标准的答案,只有最适合你的那一个! 这里只简单分享一下个人一个思考过程和不一样阶段最核心
关注的点,欢迎你们一块儿讨论。  
 
期待你的思路和方案! 期待大家的简历, 请私信 @cydu 或者 @微博平台架构。 
固然,微博平台除了计数器这一个典型的小Case外,还有更多更大的挑战须要你的方案!
相关文章
相关标签/搜索