在2016杭州云栖大会的“开源数据库之Redis专场”上,微博研发中心数据库技术负责人肖鹏带来了《微博的Redis定制之路》的精彩分享。分享中,他重点介绍了微博六年来使用Redis遇到的问题和积累的实践经验。
以下内容根据演讲PPT及现场分享整理。
微博属于国内较早一批Redis用户,微博目前由近千台Redis物理机,近万个Redis实例,日访问量达到万亿以上,从微博开始使用Redis到现在可以分为三个阶段:探索期、发育期、稳定期。最初上线试水Redis,是因为使用MySQL遇到问题,无法解决业务瓶颈。六年的时间内,微博在使用Redis过程中积累了大量的经验和技巧。
探索期:野蛮生长
微博最初使用Redis时,是采用Reids替换掉架构中的MC,形成Web+Redis+MySQL的架构。经过一段时间的使用,发现Redis的数据结构十分丰富,并且自带持久化功能。因此微博采取了一步大胆的尝试,将三层架构中的MySQL剥出,将Redis最为落地存储的数据库,将三层结构变为两层结构。
在初期使用Redis时遇到很多问题:首先性能上由于Redis原生是单线程的,在进行重操作,尤其是bgsave时会出现明显的卡顿,业务波动;其次故障恢复问题,宕机后恢复主从速度慢,经常出现带宽洪峰问题;此外,运维上也存在复杂度问题,版本升级频繁,主库切换常态化。
为了解决Bgsave带来的问题:一是可以从主线程中独立出来Bio thread,专门执行Bgsave等操作,避免干扰;二是在Redis中内置Cronsave功能,控制备份时间;三是放弃Bgaofrewrite。
在Redis替代MySQL存储落地的过程中,微博对Redis也进行很多定制化改造:
- 修改了AOF机制,增加原本不存在的POS位;
- 修改了Replication机制,实现基于AOF+POS位置的数据同步
- 修改落地机制,改为RDB+AOF滚动机制,保障数据持久化存储。
在使用Redis过程,同样可以对其热升级,简化运维复杂度:主程序中为全局的数据结构,其他功能封装到Libredis.so中,对Libredis.so实现动态热加载,每次版本升级实现热升级,不必再进行主库切换,极大地降低运维复杂度和业务风险。
总结一下,在探索期:以业务和运维驱动为主对Redis进行定制化改造,参考MySQL的Binlog机制进行数据同步,使得主库和从库一致性得到保障。在此之后微博Redis的改造全部基于该版本。
发展期:争奇斗艳
从下图可以看出,随着时间的发展,微博在逐渐向Redis上转移业务,从13年到14年经历了巨大的服务和产品线的提升,进而带了一些特殊的需求。
微博中最为重要的是关系类存储,其量达到千亿级别,将其存储在Redis中,势必会导致日益增长的关系类存储容量问题同成本之间的矛盾(常用的Redis的型号是128G);第二点微博中琳琅满目的技术类场景需要专门的解决方案;第三是判断类的需求,利用Redis进行判断时不仅具有较大的时延还需要占据大量的内存。
针对上述需求,微博对Redis进行了定制化的改造。
针对千亿级别的关系类存储,为了减少成本,定位从Storage变为Cache;放弃了原生的Hash结构(比较占内存),定制Longset数据结构(固定长度开放寻址的Hash数组);当 Cache miss之后,Client批量为O(1),同时内存占用降为原来的1/10。
针对计数类需求,根据业务场景将KV改为定长KV;采用Double hash解决冲突。由于其用于计数场景,因此将Redis中其他数据结构从Redis cont版本中剥离,变成一个超级精简的Redis,通过预分配内存,降低指针开销。通过上述操作不仅降低了成本,同时内存占用率降为原来的1/4。
微博目前拥有十亿多用户,每个用户都有粉丝数、关注数、博文数,十亿用户至少需要三十亿的计数,这是一个相当大的开销,这就需要对Redis进一步改造:通过Schema支持多列存计数,降低了内存占用,减少了网络开销;第二点,由于计数较小较快,完全使用内存会导致资源浪费,因此可以进行冷热数据分离,热数据放在内存里,冷数据放到SSD磁盘中;当为了便于读取冷数据,增加了LRU cache模块,保证冷数据的性能;此外,再改版之后Redis中还添加了异步IO线程访问磁盘,避免性能问题。
针对判断类需求,通过Bloomfilter,采用0110的方式很简单地进行判断,极大地降低了存储空间,同时也减少时延消耗,解决了微博上是否赞过等场景的高并发问题和成本问题。
稳定期:拾遗补缺
经过四年对Redis根据业务需求不断进行改造,目前微博对Redis的使用逐步到达稳定期,主要的工作是进行查缺补漏:
- 服务化方面,随着业务的变大,集群规模也随之增大,拼写Hash规则已经变成负担,并且每次变更扩容都变得及其复杂;
- 数据流动方面,多个存储的更新存在数据不一致的情况,并且业务双写也增加了程序的复杂性;
- 数据迁移方面,原有的Redis的拆分和改造都需要DBA和RD共同参与以便保证数据的一致性以及尽量降低对业务的冲击。
针对上述需求,微博自研了中间件实现路由功能;在架构右侧引入Configer Service模块,统一配置管理;同时引入Slot设计,实现动态的Slot迁移,对业务无影响,此外还架构中基于Slot设计无缝扩容功能,实现线性扩容,由于架构中具有Slot Manger、Remote Proxy、Configer Service以及iTransfor中间件,架构可以融入HA等运维机制。
在微博实际关注场景中,例如我关注了你,之后需要加入你的粉丝列表;需要更新我的关注列表;还要增加两人的计数器,一个操作带来很多的入库操作。最开始采用的是程序多写的方式,但随着工程量的增加,这种操作会带来很多隐患。目前微博通过自研的DataBus,基于MySQL的row格式binlog日志实现消息的流转,保障了数据的一致性;下游兼容Redis、HBase等存储;此外,用户可自定义DataBus中的transforemer模块,支持对MySQL消息的自定义处理规则处理。
最近微博对Redis 进行了iTranstor操作。当Redis达到物理机极限时,需要对Redis进行拆分,微博自研了iTranstor,实现对单端口的Redis实现拆分;其中用户可自定义拆分规则(作为独立组件或称为Redis的一个功能),可以实现Rang或者Hash的拆分规则,实现上下游不同的Redis分支。
总结和未来
微博在使用Redis的过程中,秉承着业务驱动的模式,所有技术上的改造都是为了满足不同的业务需求;反过来技术的变革也推进了业务形态的变化,两者相互作用,呈螺旋上升的趋势。
目前,各大公司对Redis的使用已经处于相对平稳的状态。未来,Redis结合SSD等硬盘会成为下一次的爆发点吗?让我们拭目以待。