You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

131 lines
13 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 加餐(七) | 从微博的Redis实践中我们可以学到哪些经验
你好,我是蒋德钧。
我们知道微博内部的业务场景中广泛使用了Redis积累了大量的应用和优化经验。微博有位专家曾有过一个[分享](https://mp.weixin.qq.com/s?__biz=MzI4NTA1MDEwNg==&mid=2650782429&idx=1&sn=7f2df520a7295a002c4a59f6aea9e7f3&chksm=f3f90f48c48e865e478d936d76c5303663c98da506f221ede85f0f9250e5f897f24896147cfb&scene=27#wechat_redirect)介绍了Redis在微博的优化之路其中有很多的优秀经验。
俗话说“他山之石可以攻玉”学习掌握这些经验可以帮助我们在自己的业务场景中更好地应用Redis。今天这节课我就结合微博技术专家的分享以及我和他们内部专家的交流和你聊聊微博对Redis的优化以及我总结的经验。
首先我们来看下微博业务场景对Redis的需求。这些业务需求也就是微博优化和改进Redis的出发点。
微博的业务有很多例如让红包飞活动粉丝数、用户数、阅读数统计信息流聚合音乐榜单等同时这些业务面临的用户体量非常大业务使用Redis存取的数据量经常会达到TB级别。
作为直接面向终端用户的应用微博用户的业务体验至关重要这些都需要技术的支持。我们来总结下微博对Redis的技术需求
* 能够提供高性能、高并发的读写访问,保证读写延迟低;
* 能够支持大容量存储;
* 可以灵活扩展,对于不同业务能进行快速扩容。
为了满足这些需求微博对Redis做了大量的改进优化概括来说既有对Redis本身数据结构、工作机制的改进也基于Redis自行研发了新功能组件包括支持大容量存储的RedRock和实现服务化的RedisService。
接下来我们就来具体了解下微博对Redis自身做的一些改进。
## 微博对Redis的基本改进
根据微博技术专家的分享我们可以发现微博对Redis的基本改进可以分成两类避免阻塞和节省内存。
首先针对持久化需求他们使用了全量RDB加增量AOF复制结合的机制这就避免了数据可靠性或性能降低的问题。当然Redis在官方4.0版本之后也增加了混合使用RDB和AOF的机制。
其次在AOF日志写入刷盘时用额外的BIO线程负责实际的刷盘工作这可以避免AOF日志慢速刷盘阻塞主线程的问题。
再次增加了aofnumber配置项。这个配置项可以用来设置AOF文件的数量控制AOF写盘时的总文件量避免了写入过多的AOF日志文件导致的磁盘写满问题。
最后,在主从库复制机制上,使用独立的复制线程进行主从库同步,避免对主线程的阻塞影响。
在节省内存方面,微博有一个典型的优化,就是定制化数据结构。
在使用Redis缓存用户的关注列表时针对关注列表的存储他们定制化设计了LongSet数据类型。这个数据类型是一个存储Long类型元素的集合它的底层数据结构是一个Hash数组。在设计LongSet类型之前微博是用Hash集合类型来保存用户关注列表但是Hash集合类型在保存大量数据时内存空间消耗较大。
而且当缓存的关注列表被从Redis中淘汰时缓存实例需要从后台数据库中读取用户关注列表再用HMSET写入Hash集合在并发请求压力大的场景下这个过程会降低缓存性能。跟Hash集合相比LongSet类型底层使用Hash数组保存数据既避免了Hash表较多的指针开销节省内存空间也可以实现快速存取。
从刚才介绍的改进工作你可以看到微博对Redis进行优化的出发点和我们在前面课程中反复强调的Redis优化目标是一致的。我自己也总结了两个经验。
第一个经验是高性能和省内存始终都是应用Redis要关注的重点这和Redis在整个业务系统中的位置是密切相关的。
Redis通常是作为缓存在数据库层前端部署就需要能够快速地返回结果。另外Redis使用内存保存数据一方面带来了访问速度快的优势另一方面也让我们在运维时需要特别关注内存优化。我在前面的课程里介绍了很多和性能优化、节省内存相关的内容比如说第1820讲你可以重点回顾下并且真正地在实践中应用起来。
第二个经验是在实际应用中需要基于Redis做定制化工作或二次开发来满足一些特殊场景的需求就像微博定制化数据结构。不过如果要进行定制化或二次开发就需要了解和掌握Redis源码。所以我建议你在掌握了Redis的基本原理和关键技术后把阅读Redis源码作为下一个目标。这样一来你既可以结合原理来加强对源码的理解还可以在掌握源码后开展新增功能或数据类型的开发工作。对于如何在Redis中新增数据类型我在[第13讲](https://time.geekbang.org/column/article/281745)中向你介绍过,你可以再复习下。
除了这些改进工作为了满足大容量存储需求微博专家还在技术分享中提到他们把RocksDB和硬盘结合使用以扩大单实例的容量我们来了解下。
## 微博如何应对大容量数据存储需求?
微博业务层要保存的数据经常会达到TB级别这就需要扩大Redis实例的存储容量了。
针对这个需求微博对数据区分冷热度把热数据保留在Redis中而把冷数据通过RocksDB写入底层的硬盘。
在微博的业务场景中冷热数据是比较常见的。比如说有些微博话题刚发生时热度非常高会有海量的用户访问这些话题使用Redis服务用户请求就非常有必要。
但是等到话题热度过了之后访问人数就会急剧下降这些数据就变为冷数据了。这个时候冷数据就可以从Redis迁移到RocksDB保存在硬盘中。这样一来Redis实例的内存就可以节省下来保存热数据同时单个实例能保存的数据量就由整个硬盘的大小来决定了。
根据微博的技术分享我画了一张他们使用RocksDB辅助Redis实现扩容的架构图
![](https://static001.geekbang.org/resource/image/c0/b0/c0fdb8248a3362afd29d3efe8b6b21b0.jpg)
从图中可以看到Redis是用异步线程在RocksDB中读写数据。
读写RocksDB的延迟毕竟比不上Redis的内存访问延迟这样做也是为了避免读写冷数据时阻塞Redis主线程。至于冷数据在SSD上的布局和管理都交给RocksDB负责。RocksDB目前已经比较成熟和稳定了可以胜任Redis冷数据管理这个工作。
关于微博使用RocksDB和SSD进行扩容的优化工作我也总结了两条经验想和你分享一下。
首先,**实现大容量的单实例在某些业务场景下还是有需求的。**虽然我们可以使用切片集群的多实例分散保存数据,但是这种方式也会带来集群运维的开销,涉及到分布式系统的管理和维护。而且,切片集群的规模会受限,如果能增加单个实例的存储容量,那么,即使在使用较小规模的集群时,集群也能保存更多的数据。
第二个经验是如果想实现大容量的Redis实例**借助于SSD和RocksDB来实现是一个不错的方案**。我们在[第28讲](https://time.geekbang.org/column/article/298205)中学习的360开源的Pika还有微博的做法都是非常好的参考。
RocksDB可以实现快速写入数据同时使用内存缓存部分数据也可以提供万级别的数据读取性能。而且当前SSD的性能提升很快单块SSD的盘级IOPS可以达到几十万级别。这些技术结合起来Redis就能够在提供大容量数据存储的同时保持一定的读写性能。当你有相同的需求时也可以把基于SSD的RocksDB应用起来保存大容量数据。
## 面向多业务线微博如何将Redis服务化
微博的不同业务对Redis容量的需求不一样而且可能会随着业务的变化出现扩容和缩容的需求。
为了能够灵活地支持这些业务需求微博对Redis进行了服务化改造RedisService。所谓服务化就是指使用Redis集群来服务不同的业务场景需求每一个业务拥有独立的资源相互不干扰。
同时所有的Redis实例形成一个资源池资源池本身也能轻松地扩容。如果有新业务上线或是旧业务下线就可以从资源池中申请资源或者是把不用的资源归还到资源池中。
形成了Redis服务之后不同业务线在使用Redis时就非常方便了。不用业务部门再去独立部署和运维只要让业务应用客户端访问Redis服务集群就可以。即使业务应用的数据量增加了也不用担心实例容量问题服务集群本身可以自动在线扩容来支撑业务的发展。
在Redis服务化的过程中微博采用了类似Codis的方案通过集群代理层来连接客户端和服务器端。从微博的公开技术资料中可以看到他们在代理层中实现了丰富的服务化功能支持。
* 客户端连接监听和端口自动增删。
* Redis协议解析确定需要路由的请求如果是非法和不支持的请求直接返回错误。
* 请求路由:根据数据和后端实例间的映射规则,将请求路由到对应的后端实例进行处理,并将结果返回给客户端。
* 指标采集监控:采集集群运行的状态,并发送到专门的可视化组件,由这些组件进行监控处理。
此外,在服务化集群中,还有一个配置中心,它用来管理整个集群的元数据。同时,实例会按照主从模式运行,保证数据的可靠性。不同业务的数据部署到不同的实例上,相互之间保持隔离。
按照我的理解画了一张示意图显示了微博Redis服务化集群的架构你可以看下。
![](https://static001.geekbang.org/resource/image/58/ff/58dc7b26b8b0a1df4fd1faeee24618ff.jpg)
从Redis服务化的实践中我们可以知道当多个业务线有共同的Redis使用需求时提供平台级服务是一种通用做法也就是服务化。
当把一个通用功能做成平台服务时,我们需要重点考虑的问题,包括**平台平滑扩容、多租户支持和业务数据隔离、灵活的路由规则、丰富的监控功能**等。
如果要进行平台扩容我们可以借助Codis或是Redis Cluster的方法来实现。多租户支持和业务隔离的需求是一致我们需要通过资源隔离来实现这两个需求也就是把不同租户或不同业务的数据分开部署避免混用资源。对于路由规则和监控功能来说微博目前的方案是不错的也就是在代理层proxy中来完成这两个功能。
只有很好地实现了这些功能,一个平台服务才能高效地支撑不同业务线的需求。
## 小结
今天这节课我们学习了微博的Redis实践从中总结了许多经验。总结来说微博对Redis的技术需求可以概括为3点分别是高性能、大容量和易扩展。
为了满足这些需求除了对Redis进行优化微博也在自研扩展系统包括基于RocksDB的容量扩展机制以及服务化的RedisService集群。
最后,我还想再跟你分享一下我自己的两个感受。
第一个是关于微博做的RedisService集群这个优化方向是大厂平台部门同学的主要工作方向。
业务纵切、平台横切是当前构建大规模系统的基本思路。所谓业务纵切是指把不同的业务数据单独部署这样可以避免相互之间的干扰。而平台横切是指当不同业务线对运行平台具有相同需求时可以统一起来通过构建平台级集群服务来进行支撑。Redis就是典型的多个业务线都需要的基础性服务所以将其以集群方式服务化有助于提升业务的整体效率。
第二个是代码实践在我们成长为Redis高手过程中的重要作用。
我发现对Redis的二次改造或开发是大厂的一个必经之路这和大厂业务多、需求广有密切关系。
微博做的定制化数据结构、RedRock和RedisService都是非常典型的例子。所以如果我们想要成为Redis高手成为大厂中的一员那么先原理后代码边学习边实践就是一个不错的方法。原理用来指导代码阅读的聚焦点而动手实践至关重要需要我们同时开展部署操作实践和阅读代码实践。纸上得来终觉浅绝知此事要躬行希望你不仅重视学习原理还要真正地用原理来指导实践提升自己的实战能力。
## 每课一问
按照惯例我给你提个小问题你在实际应用Redis时有没有一些经典的优化改进或二次开发经验
欢迎你在留言区聊一聊你的经验,我们一起交流讨论。如果你觉得今天的内容对你有所帮助,也欢迎你把今天的内容分享给你的朋友或同事。