用最少的机器支撑万亿级访问,微博6年Redis优化历程
《用最少的机器支撑万亿级访问,微博6年Redis优化历程》要点: 微博是从 2010 年开始引入 ?Redis,现在 Redis 已经广泛应用于微博的多个业务场景,如关系、计数、通知提醒等,目前 Redis 集群存储超过百亿记录,每天上万亿的读取访问.随着业务的快速发展,我们在使用过程中碰到的问题及解决方法给大家做一个分享.主要包括以下方面: 实现机制高可用、业务极致定制以及服务化. Redis 2.0 时代(2010 – 2011)实现机制高可用优化微博最早使用的是 Redis 2.0 版本,在初期业务规模不大的时候,Redis 服务运行比较稳定.但是随着业务数据量和访问量的增加,一些问题逐渐暴露出来: 持久化问题在我们大多数业务场景中 Redis 是当做存储来使用,会开启持久化机制.线上采用单机多实例的部署结构,服务器的内存使用率也会比较高.由于官方版本触发 bgsave 和 bgrewriteaof 操作的时间点是不可控的,依赖于相关的配置项和业务的写入模型,因此可能会出现单机部署的多个 Redis 实例同时触发 bgsave 或 bgrewriteaof 操作,这两个操作都是通过 fork 出一个子进程来完成的,由于 copy-on-write 机制,可能会导致服务器内存很快耗尽,Redis 服务崩溃. 此外在磁盘压力较大时(生成 rdb、aof 重写),对 aof 的写入及 fsync 操作可能会出现阻塞,虽然从 2.4 版本开始 fsync 操作调整到 bio 线程来做,主线程 aof 的写入阻塞仍会导致服务阻塞. 主从同步问题 为了提高服务可用性,避免单点问题,我们线上业务 Redis 大多采用主从结构部署.官方版本的主从同步机制,在网络出现问题时(如瞬断),会导致主从重新进行一次全量复制.对单个端口来说,如果数据量小,那么这个影响不大,而如果数据量比较大的话,就会导致网络流量暴增,同时 slave 在加载 rdb 时无法响应任何请求.当然官方 2.8 版本支持了 psync 增量复制的机制,一定程度上解决了主从连接断开会引发全量复制的问题,但是这种机制受限于复制积压缓冲区大小,同时在主库故障需要执行切主操作场景下,主从仍然需要进行全量复制. 版本升级及管理问题 ?早期 Redis 版本运行不够稳定,经常需要修复 bug、支持新的运维需求及版本优化,导致版本迭代很频繁.官方版本在执行升级操作时,需要服务重启,我们大多数线上业务都开启了持久化机制,重启操作耗时较长,加上使用 Redis 业务线比较多,版本升级操作的复杂度很高.由于统一版本带来的运维工作量实在太高,线上 Redis 版本曾经一度增加到十几个,给版本管理也带来很大的困难. 为了解决以上问题我们对 Redis 原生实现机制做了以下优化: 1. 对于持久化机制,采用 rdb + aof 的持久化方式.aof 文件按固定大小滚动,生成 rdb 文件时记录当前 aof 的 position,全量的数据包含在 rdb 和所记录位置点之后的 aof 文件,废弃 aof 重写机制,生成 rdb 后删除无效的 aof 文件;增加了定时持久化操作的配置项 cronsave,将单机部署的多个 Redis 实例的持久化操作分散在不同的时间点进行,并且错开业务高峰;将对 aof 的写入操作也放到 bio 线程来做,解决磁盘压力较大时 Redis 阻塞的问题. 2. 对于主从同步机制,借鉴 MySQL 的复制机制并做了简化.使用 rdb + aof 的方式,支持基于 aofpositon 的增量复制.从库只需与主库进行一次全量同步同步,后续主从连接断开或切主操作,从库都是与主库进行增量复制. 对于版本升和管理级的问题,Redis 的核心处理逻辑封装到动态库,内存中的数据保存在全局变量里,通过外部程序来调用动态库里的相应函数来读写数据.版本升级时只需要替换成新的动态库文件即可,无须重新载入数据.通过这样的方式,版本升级只需执行一条指令,即可在毫秒级别完成代码的升级,同时对客户端请求无任何影响. ??除了以上几点,也做了很多其它的优化,如主从延迟时间检测,危险命令认证等.通过逐步的优化,内部的 Redis 版本也开始进入稳定期,应用规模也在持续的增加. 业务极致定制化时代(2012 – 2013)RedisCounter / LongSet在某些特定的业务场景下,随着业务规模的持续增加,Redis 的使用又暴露出来一些问题,尤其是服务成本问题(小编:是省服务器的意思?).为此结合特定的业务场景我们对 Redis 做了一些定制的优化.这里主要介绍一下在关系和计数两个业务场景下做的定制优化. 关系微博关系业务包含添加、取消关注,判断关注关系等相关的业务逻辑,引入 Redis 后使用的是 hash 数据结构,并且当作存储使用.但是随着用户规模的快速增长,关系服务 Redis 容量达到十几 TB,并且还在快速的增长,如何应对成本压力? 为了解决服务成本问题,我们把 Redis 的角色由 storage 调整为 cache. 这是因为随着用户数量的增长,业务模型由初期的热点数据不集中已经转变为有明显的冷热之分.对于关注关系变更、判断关注关系,hash 数据结构是最佳的数据结构,但是存在以下问题:
于是,我们定制了 longset 数据结构,它是一个“固定长度开放寻址的 hash 数组”,通过选择合适的 hash 算法及数组填充率,可实现关注关系变更及判断的性能与原生 Redis hash 相当,同时 cache miss 后通过 client 重建 longset 结构,实现 O(1) 复杂度回写. 通过定制 longset 数据结构,将关系 Redis 内存占用降低了一个数量级(小编:这该节约了多少服务器……发奖金了吗?),同时保证了服务性能. (编辑:ASP站长网) |