这是Redis开发运维实战的第一篇文章。

我先做个自我介绍。我在快手工作,负责公司亿万级别Cache相关工作。在这之前,我在阿里云Redis团队主要参与云上Redis的保障性工作,以及Redis诊断系统的开发,在那里我与各种使用Redis的公司打交道,了解了他们各自不同的Redis使用场景,遇到并解决了各种千奇百怪的问题。更早之前,我在搜狐视频开发了公司的Redis私有云平台CacheCloud,现在已经在Github开源,已经在很多公司使用。另外,在工作之余我和我之前的leader张益军写过一本书《Redis开发与运维》,这本书在社区也受到很好的评价。

对于我来说,从开始接触Redis到今天,已经有6年时间了。 虽然Redis使用起来很简单,但我相信,不论你是在项目中使用Redis,还是在面试时被问到Redis相关的问题,都还是会有这样或那样的疑问。比如说怎么用Redis实现分布式锁,Redis怎么处理过期键,缓存雪崩、穿透、热点问题怎么处理,持久化、集群方案怎么选择,如何优雅地给Redis做键值分析等等问题。这些问题在6年前的时候,我就觉得高深莫测,不知道怎么入手解决。

任何事情都需要一个循序渐进的过程,所以我骨子里根本不相信所谓的“速成”。从一开始的Redis小白到现在同事眼中的半个Redis专家,我前前后后摸索了很多年,虽然比别人笨点,但是都还一直慢慢前进。细说起来,我的经历可能与很多基础架构或者DBA的同学不同,在工作开始的前几年里,我是做业务开发的,换句话说,我就是一名Java程序员。

那我后来又怎么与Redis结缘的呢?一切都要从下面的一次“事故”说起。

我在搜狐的时候,主要负责视频推荐系统的开发,由于涉及按照用户维度和视频维度操作推荐结果,Leader选择Redis作为缓存,底层使用HBase作为存储架构。当时,我们团队基本都在使用Memcached,我个人也对Memcached比较熟悉,但之所以选择Redis,我想是因为它支持的数据结构比较丰富,同时还支持持久化;另外最重要的是Redis已经开始支持原生集群,而Memcached主要使用客户端分片来实现集群功能,实际上不是真正的分布式。

对当时的我来说,Redis就是一个黑盒,我只知道它提供了5种数据结构以及一些常用的命令,其他更深入的就不甚了解了。后来我接到一个这样的需求:用户在浏览网站时,需要实现保存和展示用户喜欢视频的功能。于是,我使用了Redis的有序集合数据结构:

127.0.0.1:6379> zadd ANDROID_3a18f1956477c22e 1548931180025 10000001  1548931240365 10000002 1548931258497 10000003 1548931268847 10000004(integer) 4127.0.0.1:6379> zrevrange ANDROID_3a18f1956477c22e 0 -1 withscores1) "10000004"2) "1548931268847"3) "10000003"4) "1548931258497"5) "10000002"6) "1548931240365"7) "10000001"8) "1548931180025"

一切都如此简单。但是,突然有一天晚上,我们的推荐服务发出令人揪心的可用性报警,网站部分区域出现了天窗(全空白,恰好没做降级),一切都乱套了。不过,唯一幸运的是负责运维的同事可以判断在出现这个问题之前,刚好上线了我做的这个功能,于是经过简单的项目回滚之后暂时解决了这个问题。

出了问题肯定要复盘,经过分析我们发现是接口遭到恶意刷新。这就引出了另外两个问题:一个是本身接口安全性的问题和故障降级问题,这里先不展开讨论了;另一个就是有序集合本身也没有做容量限制,Redis内存暴增,Java里出现了大量大对象,造成频繁的GC,导致可用性下降。

后来,随着推荐系统规模的扩大,我们用到的Redis集群越来越多,同时碰到的问题也越来越多,随之系统稳定性的挑战也越来越大。经过商议,我们决定开发一个Redis的PaaS平台,也就是前面说的CacheCloud。我们的初衷是做出一个通用的Redis平台,一方面,能标准化地运维手上的Redis,解放我们的运维压力;另一方面,希望它能够帮助业务快速定位问题,同时通过这个过程让大家逐步熟悉Redis的相关知识。

经过几个月的努力,项目成功上线。一波宣传和推广后,各个业务组都开始接入该平台,由于我对Redis比较感兴趣,Leader将这些工作全部交给我来负责。但是始料未及的是,各个业务组接入后,由于使用场景以及业务的多态性,几乎每天都有很多人找我处理各种问题,譬如:用你们的Redis为什么超时了?为什么内存用得这么快?集群的数据分布以及高可用怎么实现的?客户端效率怎么上不去?诸多问题,把我搞得焦头烂额。没办法,既然自己负责了这块业务,就必须解决这些问题。于是我进一步阅读之前买来的书籍以及Redis官方文档,甚至尝试直接从源码中找答案。经过一段时间的努力,我不仅把上面的问题基本解决了,而且对于Redis的知识体系有了更进一步的认识。我把每次遇到的问题都书写到个人博客中,一两个月后不仅处理了大部分问题,还积累了数十篇文章,成长带来的成就感也爆棚。

到了阿里云和快手之后,因为公司的体量越来越大,我负责的Redis实例个数以及规模也越来越大,问题同样也更是千奇百怪,很多问题需要对Redis源码、业务架构有更深入的理解和认识才能解决。

比如说在阿里云的时候,有个客户反馈Redis内存突然暴增造成数据丢失,希望我们给出具体原因,我按照常规的内存分析方法均没有发现异常,最后突然想到有可能是Redis的rehash造成的。经过和同事看源码做试验,最终找到原因,果然是Redis的rehash所致

我经历的这几家公司所用的Redis分布式架构均不相同,有官方的版本、有自研的版本,也有其他开源版本。在使用和运维这些不同的架构中,我理解了每种架构的优劣势和使用场景,对于分布式存储架构也有了更深的认识,“独乐乐不如众乐乐”,我也希望能将这些理解传递给你,启发到你,进而帮助到你,我们一起成长。

说了这么多,其实是想表达一个核心的观点:学习一门技术的最快方法就是带着问题来学。千万不要相信网上有些观点说“Redis挺简单,直接看源码就好了”。我想,除非你是技术大牛,否则这是不可能实现的,对于普通人来讲90%的可能就是打退堂鼓。所以,我也希望这个公众号是带着这个思路来撰写的,实际上Redis的核心点大概就如下几个:数据结构的理解、客户端的优化、复制和持久化的选择、分布式高可用的实现、缓存设计与优化,等等。而我的公众号也围绕着这几点。

  • 如何正确选择数据结构

  • 基础编码不能忽视-sds

  • Redis到底能存储多少个键值对-hashtable解析

  • 节省内存利器-ziplist(压缩列表)

  • 大列表怎么存-quicklist(快速列表)

  • 链表的二分查找-skiplist(跳跃表)

  • 你真的了解Redis里都是什么key吗?

  • 事件模型分析:Redis为什么这么快?

  • 缓存的设计与优化(一):更新策略、粒度、穿透问题

  • 缓存的设计与优化(二):无底洞问题

  • 缓存的设计与优化(三):热点key问题

  • Redis的典型使用场景:分布式锁

  • Redis的典型使用场景:消息队列

  • Redis的典型使用场景:红包秒杀

  • BIO还是NIO客户端

  • 揪出Redis的性能瓶颈(一):客户端管理

  • 持久化RDB和AOF的进化史

  • 揪出Redis的性能瓶颈(二):持久化抉择

  • 复制计划史:全量复制、部分复制、psync2

  • 揪出Redis的性能瓶颈(三):复制的隐忧

  • Redis内存开销在哪儿?

  • 开源节流(一):干掉内存毒瘤死键问题

  • 开源节流(二):键值对象优化

  • 开源节流(三):内存碎片的解决之道

  • 开源节流(四):小细节大用处

  • 开源节流(五):硬件成本拯救者Pika

  • 高可用探针:Redis Sentinel

  • 分布式缓存数据分布与实现方式

  • 根红苗正集群方案:Redis Cluster深入实践(上)

  • 根红苗正集群方案:Redis Cluster深入实践(下)

  • 基于代理集群方案:Twemproxy

  • 基于代理集群方案:Codis

  • 基于range的集群方案:redis range

  • Memcached真的过时了吗?

  • 除了Redis还有啥好用的?

  • 小工具大用处:我的Redis工具箱

  • 是否该升级你的Redis:“版本帝”Redis

  • Redis同城容灾。

  • Redis异地多活

  • Redis安全七法:你的Redis是否安全?

  • Linux与Redis:Linux操作系统的配置优化

  • CacheCloud的开发计划

  • 《Redis开发与运维》第二版计划等等