用最少的机器支撑万亿级访问,微博6年Redis优化历程
mhr18 2024-11-02 11:57 30 浏览 0 评论
专注于Java领域优质技术,欢迎关注
作者:刘东辉
编者按:本文由刘东辉向高可用架构供稿,基于在 5 月 15 日 Redis 用户交流会上的演讲内容。
刘东辉,新浪微博基础架构组研发工程师。2013 年加入微博,先后参与微博 Redis、CounterService、SSDCache、CacheService 等基础组件的设计与开发工作,目前专注于分布式缓存、存储方向。
微博是从 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 数据结构是最佳的数据结构,但是存在以下问题:
- cache miss 后回写关注列表性能差,对于关注数较多的微博会员,回写操作耗时可达到 10ms,这对于单线程的 Redis 来说是致命的;
- Redis hash 结构的内存使用率不高,要保证 cahce 的命中率所需的 cache 容量仍然是很大的。
于是,我们定制了 longset 数据结构,它是一个“固定长度开放寻址的 hash 数组”,通过选择合适的 hash 算法及数组填充率,可实现关注关系变更及判断的性能与原生 Redis hash 相当,同时 cache miss 后通过 client 重建 longset 结构,实现 O(1) 复杂度回写。
通过定制 longset 数据结构,将关系 Redis 内存占用降低了一个数量级(小编:这该节约了多少服务器……发奖金了吗?),同时保证了服务性能。
计数
微博有很多计数场景,如用户纬度的关注数、粉丝数,微博纬度的转发数、评论数等。计数作为微博中一项很重要的数据,在微博业务中承担了很重要的角色。为更好的满足计数业务需求,我们基于 Redis 定制了内部的计数服务。
原生 Redis 为了支持多数据类型,需要维护很多指针信息,存储一份业务计数要占到约 80 个字节,内存利用率很低。为此我们定制了第一版计数器 Redis counter,通过预先分配内存数组存储计数,并且采用 doublehash 解决冲突,减少了原生 Redis 大量的指针开销。通过以上优化将内存成本降低到原来的 1/4 以下。(小编:又节约了 3 / 4 服务器……)
随着微博的发展,微博纬度的计数不断增加,在原来的转发数、评论数基础上,又增加了表态数,2013 年还上线了阅读数。 Redis counter 已不能很好的解决这类扩展问题:
- 存储单条微博相关的计数,需要重复存储微博 mid 信息,并且数据全部存储在内存,服务成本较高;
- 获取单条微博全部的计数,需要调用多次计数接口,对服务端压力很大。
为此我们又设计了改进版的计数器 CounterService,增加如下特性:
- Schema 支持多列:支持动态加列,内存使用精简到 bit
- 冷热数据分离:频繁访问的热数据存储在 memory,访问较少的冷数据存储在磁盘,降低服务成本
- LRU 缓存冷数据:增加 LRU 模块,缓存访问到的冷数据,保证冷数据的访问性能。
- 异步 IO 线程访问冷数据:避免冷数据的访问影响服务的整体性能
??通过以上的定制优化,我们从根本上解决了计数业务的成本及性能问题。
除了以上关系、计数业务场景的定制优化,为了满足判断类业务场景需求,定制了 BloomFilter 服务;为了满足 feed 聚合业务场景需求,定制了 VerctorService 服务;为了降低服务成本,定制了 SSDCache 服务等。(小编:老板感动得流泪了)
服务化时代(2014 -)
Cache Service、SSD Cache
随着微博业务的快速增长,Redis 集群规模也在持续增加,目前微博 Redis 集群内存占用数十 TB,服务于数百个业务线,Redis 集群的管理依然面临很多的问题。
数据迁移问题
随着时间推移,越来越多的业务由于数据量的增加,单端口到内存占用已经达到上限,微博内部建议单端口内存不超过 20GB,因此需要重新拆分端口,这就涉及到数据迁移,目前迁移操作是通过内部开发的一个迁移工具来完成的,迁移操作的成本相对较高。
数据路由问题
?目前的使用方式,需要在业务代码中实现数据路由规则,路由规则的变更需要重新上线代码,业务变更复杂度较高。同时节点配置采用 DNS 的方式,存在实时性和负载不均的问题,虽然使用过程中有对应的解决策略,但是需要一定的运维干预,运维复杂度较高。
HA 系统不成熟
当前的 HA 系统更多的是采用自动发现问题,手动确认处理的策略,没有实现真正意义的自动化,运维成本依然很高。
为了解决以上问题,我们在 Redis 基础上实现服务化框架 CacheService。
CacheService 最早是为了解决内部使用 memcached 遇到的问题而开发的服务化框架,主要包含以下几个模块:
- 配置中心 ConfigServer
微博内部的配置服务中心,主要是管理静态配置和动态命名服务的一个远程服务,并能够在配置发生变更的时候实时通知监听的 ConfigClient。
- 资源层
实际的数据存储引擎,初期支持 memcached,后续又扩展了 Redis、SSDCache 组件,其中 SSDCache 是为了降低服务成本,内部开发的基于 SSD 的存储组件,用于缓存介于 memory 和 DB 之间的 warm 数据。
- 代理层
代理业务端的请求,并基于设定的路由规则转发到后端的 cache 资源,它本身是无状态的。proxy 启动后会去从 ConfigServer 加载后端 cache 资源的配置列表进行初始化,并接收 ConfigServer 的配置变更的实时通知。
- 客户端
提供给业务方使用的 SDK 包,通过它不需要在业务代码中实现数据路由规则,业务方也无需关心后端 cache 的资源。只需要简单配置所使用的服务池名 group 和业务标识 namespace 即可使用 cache 资源,client 从 ConfigServer 获取 proxy 的节点列表,选择合适的 proxy 节点发送请求,支持多种负载均衡策略,同时会自动探测 proxy 节点变更。
- 集群管理系统 ClusterManager
管理集群中各个组件的运行状态以保证业务的 SLA 指标,当出现异常时会自动执行运维处理。同时配置变更、数据迁移等集群操作也都是由它来负责。
??
为支持 Redis 服务化,在服务化框架扩展支持了 Redis proxy,同时为了实
现在线数据迁移,参照 Redis cluster 的设计思想,对内部 Redis 存储做了改造,支持 slot 数据分片,数据迁移操作由 ClusterManager 组件执行,完成 slot 的重新规划及数据迁移。此外还支持 Redis 的 failover 机制,在master 或 slave 节点故障时会自动执行容错处理。我们 Redis 服务化项目 tribe 是从 2015 年底开始上线,处于逐步完善过程中。
总结
从对 Redis 的优化历程可以看出,技术的进步是由业务的需求推动的,我们需要拥抱需求。同时对于一个服务我们需要持续优化并保证服务的运维友好性才能保证服务的生命力。后续的一些计划,完善服务化体系中冷热数据分级存储机制以降低服务成本;引入新的组件以更好的满足业务需求、进一步完善集群管理组件降低运维复杂度。
相关推荐
- 【推荐】一个开源免费、AI 驱动的智能数据管理系统,支持多数据库
-
如果您对源码&技术感兴趣,请点赞+收藏+转发+关注,大家的支持是我分享最大的动力!!!.前言在当今数据驱动的时代,高效、智能地管理数据已成为企业和个人不可或缺的能力。为了满足这一需求,我们推出了这款开...
- Pure Storage推出统一数据管理云平台及新闪存阵列
-
PureStorage公司今日推出企业数据云(EnterpriseDataCloud),称其为组织在混合环境中存储、管理和使用数据方式的全面架构升级。该公司表示,EDC使组织能够在本地、云端和混...
- 对Java学习的10条建议(对java课程的建议)
-
不少Java的初学者一开始都是信心满满准备迎接挑战,但是经过一段时间的学习之后,多少都会碰到各种挫败,以下北风网就总结一些对于初学者非常有用的建议,希望能够给他们解决现实中的问题。Java编程的准备:...
- SQLShift 重大更新:Oracle→PostgreSQL 存储过程转换功能上线!
-
官网:https://sqlshift.cn/6月,SQLShift迎来重大版本更新!作为国内首个支持Oracle->OceanBase存储过程智能转换的工具,SQLShift在过去一...
- JDK21有没有什么稳定、简单又强势的特性?
-
佳未阿里云开发者2025年03月05日08:30浙江阿里妹导读这篇文章主要介绍了Java虚拟线程的发展及其在AJDK中的实现和优化。阅前声明:本文介绍的内容基于AJDK21.0.5[1]以及以上...
- 「松勤软件测试」网站总出现404 bug?总结8个原因,不信解决不了
-
在进行网站测试的时候,有没有碰到过网站崩溃,打不开,出现404错误等各种现象,如果你碰到了,那么恭喜你,你的网站出问题了,是什么原因导致网站出问题呢,根据松勤软件测试的总结如下:01数据库中的表空间不...
- Java面试题及答案最全总结(2025版)
-
大家好,我是Java面试陪考员最近很多小伙伴在忙着找工作,给大家整理了一份非常全面的Java面试题及答案。涉及的内容非常全面,包含:Spring、MySQL、JVM、Redis、Linux、Sprin...
- 数据库日常运维工作内容(数据库日常运维 工作内容)
-
#数据库日常运维工作包括哪些内容?#数据库日常运维工作是一个涵盖多个层面的综合性任务,以下是详细的分类和内容说明:一、数据库运维核心工作监控与告警性能监控:实时监控CPU、内存、I/O、连接数、锁等待...
- 分布式之系统底层原理(上)(底层分布式技术)
-
作者:allanpan,腾讯IEG高级后台工程师导言分布式事务是分布式系统必不可少的组成部分,基本上只要实现一个分布式系统就逃不开对分布式事务的支持。本文从分布式事务这个概念切入,尝试对分布式事务...
- oracle 死锁了怎么办?kill 进程 直接上干货
-
1、查看死锁是否存在selectusername,lockwait,status,machine,programfromv$sessionwheresidin(selectsession...
- SpringBoot 各种分页查询方式详解(全网最全)
-
一、分页查询基础概念与原理1.1什么是分页查询分页查询是指将大量数据分割成多个小块(页)进行展示的技术,它是现代Web应用中必不可少的功能。想象一下你去图书馆找书,如果所有书都堆在一张桌子上,你很难...
- 《战场兄弟》全事件攻略 一般事件合同事件红装及隐藏职业攻略
-
《战场兄弟》全事件攻略,一般事件合同事件红装及隐藏职业攻略。《战场兄弟》事件奖励,事件条件。《战场兄弟》是OverhypeStudios制作发行的一款由xcom和桌游为灵感来源,以中世纪、低魔奇幻为...
- LoadRunner(loadrunner录制不到脚本)
-
一、核心组件与工作流程LoadRunner性能测试工具-并发测试-正版软件下载-使用教程-价格-官方代理商的架构围绕三大核心组件构建,形成完整测试闭环:VirtualUserGenerator(...
- Redis数据类型介绍(redis 数据类型)
-
介绍Redis支持五种数据类型:String(字符串),Hash(哈希),List(列表),Set(集合)及Zset(sortedset:有序集合)。1、字符串类型概述1.1、数据类型Redis支持...
- RMAN备份监控及优化总结(rman备份原理)
-
今天主要介绍一下如何对RMAN备份监控及优化,这里就不讲rman备份的一些原理了,仅供参考。一、监控RMAN备份1、确定备份源与备份设备的最大速度从磁盘读的速度和磁带写的带度、备份的速度不可能超出这两...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- oracle位图索引 (63)
- oracle批量插入数据 (62)
- oracle事务隔离级别 (53)
- oracle 空为0 (50)
- oracle主从同步 (55)
- oracle 乐观锁 (51)
- redis 命令 (78)
- php redis (88)
- redis 存储 (66)
- redis 锁 (69)
- 启动 redis (66)
- redis 时间 (56)
- redis 删除 (67)
- redis内存 (57)
- redis并发 (52)
- redis 主从 (69)
- redis 订阅 (51)
- redis 登录 (54)
- redis 面试 (58)
- 阿里 redis (59)
- redis 搭建 (53)
- redis的缓存 (55)
- lua redis (58)
- redis 连接池 (61)
- redis 限流 (51)