Redis 6.0虽然还没有正式发布,但RC版本已经出来了,社区里讨论得很热烈,多线程、ACL、RESP3、客户端缓存这些新特性,听起来都很诱人。

我也忍不住,提前体验了一下Redis 6.0的RC版,结果,从最开始的兴奋,到中间的踩坑,再到差点放弃,经历了一个跌宕起伏的过程。今天这篇文章,就来聊聊我体验Redis 6.0新特性的经历,有哪些新特性,踩了哪些坑,最后是放弃了还是坚持下来了,希望能给想提前体验Redis 6.0的朋友一些参考。

先说明一下,Redis 6.0目前还在开发中,正式版还没有发布,本文基于的是当前的RC版本,正式版可能会有一些变化,具体以正式版为准。生产环境不建议现在就用,等正式版发布,稳定了再用也不迟。

一、为什么要折腾Redis 6.0

先说说,为什么我要折腾还没正式发布的Redis 6.0。

最开始,我用的是Redis 5.0,用得好好的,没什么问题,也没想着要升级。后来,在技术社区里,看到大家都在讨论Redis 6.0的新特性,尤其是多线程,说Redis 6.0支持多线程了,性能能提升好几倍,我就心动了。

我们的业务,Redis的QPS很高,高峰期能到十几万,虽然Redis 5.0也能扛,但CPU经常打满,性能到了瓶颈,一直在想办法优化。听说Redis 6.0支持多线程了,能利用多核CPU,性能提升很大,我就忍不住,想提前体验一下,看看能不能解决我们的性能瓶颈。

而且,Redis 6.0还有ACL权限控制、RESP3协议、客户端缓存这些新特性,听起来都很实用,尤其是ACL,我们现在用Redis,都是一个密码,所有人都能用,权限没法控制,很不安全,一直想要一个细粒度的权限控制,Redis 6.0的ACL,正好能解决这个问题。

就这样,我怀着激动的心情,开始了Redis 6.0的体验之旅,没想到,这一折腾,就是好几天,踩了无数的坑,差点就放弃了。

二、Redis 6.0有哪些新特性

在说踩坑经历之前,先简单介绍一下Redis 6.0的主要新特性,让大家有个大概的了解。

1. 多线程IO

这是Redis 6.0最受关注的新特性。以前的Redis,是单线程的,所有的命令,都在一个线程里执行,虽然性能已经很高了,但只能利用一个CPU核心,多核CPU的优势发挥不出来。

Redis 6.0引入了多线程IO,不过,这里的多线程,不是说命令的执行变成多线程了,命令的执行,还是单线程的,多线程,主要是用来处理网络IO的,也就是读取客户端的请求,和返回响应给客户端,这两个环节,用多线程来处理,而命令的执行,还是在主线程里,这样,既提高了网络IO的性能,又保持了Redis单线程的简单性,不会有并发问题。

这个设计,很巧妙,因为Redis的瓶颈,很多时候不是命令执行慢,而是网络IO慢,尤其是在高并发、大value的场景下,网络IO占了很大的比例,用多线程处理网络IO,能大大提高吞吐量。

2. ACL权限控制

以前的Redis,权限控制很简单,只有一个密码,要么全权限,要么没权限,没法做细粒度的权限控制,比如某个用户,只能读,不能写,或者只能操作某个key,都做不到。

Redis 6.0引入了ACL(Access Control List),支持细粒度的权限控制,可以创建多个用户,每个用户,可以设置不同的密码,不同的命令权限,不同的key权限,比如,某个用户,只能执行get命令,只能操作以user:开头的key,这样,权限就更精细了,也更安全了。

这个功能,对于多团队共用一个Redis集群的场景,非常实用,不同的团队,用不同的用户,只能操作自己的key,不会互相影响,也更安全。

3. RESP3协议

RESP是Redis的通信协议,以前用的是RESP2,Redis 6.0引入了新的RESP3协议,比RESP2更强大,支持更多的数据类型,比如null、布尔值、浮点数、大数、有序集合的map等,客户端和服务端的通信,更高效,也更灵活。

不过,RESP3是向后兼容的,客户端可以选择用RESP2还是RESP3,不需要担心升级的问题。

4. 客户端缓存

客户端缓存,是Redis 6.0的另一个重要新特性,也叫服务端辅助的客户端缓存。简单来说,就是Redis服务端,帮助客户端,管理本地缓存,当某个key的值变更了,服务端会通知客户端,这个key失效了,客户端就可以更新本地缓存,或者删除本地缓存。

这个功能,能大大提高缓存的性能,因为客户端可以把热点数据,缓存在本地内存里,不需要每次都请求Redis,只有当数据变更了,才会收到通知,更新缓存。这样,既提高了性能,又保证了数据的一致性。

5. 其他新特性

除了上面这些,Redis 6.0还有一些其他的新特性,比如:

  • 新的模块API,支持更多的模块开发功能。
  • 改进的复制功能,性能更好,更稳定。
  • 新的RDB格式,支持更多的信息,加载更快。
  • 改进的内存管理,内存使用更高效。
  • 很多命令的优化和bug修复。

总的来说,Redis 6.0的新特性,还是很吸引人的,尤其是多线程IO和ACL,解决了很多实际的痛点,这也是我为什么要提前体验的原因。

三、入门:安装和初体验

说干就干,我开始了Redis 6.0的安装和初体验。

下载和编译:

因为Redis 6.0还没正式发布,没有现成的安装包,我就从GitHub上,下载了RC版本的源码,然后编译安装。

编译的过程,还算顺利,和以前的版本差不多,make一下就好了,没有遇到什么大问题。编译完成后,启动Redis,用redis-cli连接上去,看看版本,确实是6.0的版本,心里有点小激动。

初体验多线程:

启动之后,我第一件事,就是体验多线程。Redis 6.0的多线程,默认是关闭的,需要在配置文件里,设置io-threads的数量,比如设置成4,就是用4个线程处理网络IO。

我设置了io-threads 4,然后重启Redis,用redis-cli连接,执行info threads命令,看看线程情况,确实有4个IO线程在运行,说明多线程开启成功了。

然后,我用redis-benchmark,做了个简单的性能测试,对比一下单线程和多线程的性能。测试结果,让我有点失望,多线程的性能,提升不是很大,只有10%左右,没有想象中的好几倍那么夸张。

后来我查了一下,才知道,多线程的性能提升,和场景有很大的关系,在小value、高QPS的场景下,提升不大,因为瓶颈不在网络IO,而在命令执行;在大value、高并发的场景下,提升才比较明显,能到50%甚至更多。我们的场景,大部分是小value,所以提升不大,这让我有点失落。

不过,好歹是有提升的,而且大value的场景,我们也有,所以还是继续体验下去。

体验ACL:

接下来,我体验了ACL权限控制。Redis 6.0的ACL,用起来还是比较简单的,可以通过配置文件,或者ACL命令,来创建和管理用户。

我创建了一个测试用户,设置了密码,只允许执行get命令,只允许操作以test:开头的key,然后用这个用户连接Redis,测试了一下,执行get命令,没问题,执行set命令,就被拒绝了,操作不是test:开头的key,也被拒绝了,权限控制,确实生效了。

这个功能,我还是很满意的,终于有细粒度的权限控制了,以后多团队共用Redis,就安全多了。

体验RESP3和客户端缓存:

RESP3和客户端缓存,因为需要客户端的支持,我用的是redis-cli,支持RESP3,切换到RESP3模式后,返回的数据格式,确实和以前不一样了,支持了更多的数据类型,不过,日常用起来,差别不是很大。

客户端缓存,我也简单试了一下,开启客户端缓存后,修改key的值,客户端确实能收到失效通知,功能是正常的,不过,这个功能,需要客户端的深度支持,目前支持的客户端还不多,实际用起来,还有点麻烦。

初体验下来,感觉Redis 6.0的新特性,都还不错,虽然多线程的性能提升,没有想象中那么大,但其他功能,还是很实用的。那时候,我还觉得,Redis 6.0挺好的,没什么大问题,没想到,后面的坑,一个接一个地来。

四、踩坑一:客户端兼容性问题

第一个大坑,就是客户端兼容性问题。

我们的项目,用的是Java,Redis客户端,用的是Jedis,版本比较老,是2.9的版本。我把Redis升级到6.0之后,最开始,连接是正常的,简单的get、set命令,也能执行,我以为兼容性没问题,就没太在意。

结果,在测试的时候,发现了一些奇怪的问题,比如,有时候执行命令,会报错,说协议错误;有时候,连接会突然断开,重连之后又好了;还有的时候,返回的数据,格式不对,解析失败。

这些问题,时有时无,很不稳定,我排查了很久,才发现,是Jedis的版本太老了,对Redis 6.0的支持不好,尤其是在某些命令和协议上,有兼容性问题。

后来,我把Jedis升级到了最新的3.x版本,这些问题,才基本解决了。但是,升级Jedis,又带来了新的问题,因为Jedis 3.x的API,和2.x有一些变化,我们的代码里,有一些地方,用了老的API,升级之后,编译报错,不得不改代码,这又花了不少时间。

而且,我们的项目里,还有其他的中间件,也用到了Redis,比如Spring Cache、Redisson、Shiro等,这些组件,对Redis 6.0的支持,也参差不齐,有的没问题,有的有小问题,有的甚至不支持,需要升级版本,或者做适配,这一通折腾下来,花了好几天时间,才把所有的兼容性问题,基本解决。

这时候,我才意识到,升级Redis版本,不是只升级服务端就行了,客户端、各种中间件,都要考虑兼容性,这是一个系统工程,不是那么简单的。

五、踩坑二:多线程的坑

第二个大坑,就是多线程的坑。

前面说过,Redis 6.0的多线程,默认是关闭的,需要手动开启。我开启了多线程之后,最开始,测试是正常的,性能也有一定的提升,我以为没问题了。

结果,在压测的时候,发现了一个奇怪的问题,就是CPU的使用率,很不均衡,有的核心打满了,有的核心很闲,没有充分利用多核CPU。

我排查了一下,发现是因为,Redis 6.0的多线程,只是网络IO多线程,命令执行还是单线程的,所以,命令执行的那个主线程,CPU会打满,而IO线程,只有在网络IO繁忙的时候,才会忙,大部分时候,比较闲,所以,CPU使用率不均衡。

而且,在某些场景下,开启多线程之后,性能反而下降了,因为多线程之间,有上下文切换的开销,还有锁的竞争,如果网络IO不是瓶颈,开启多线程,反而会增加开销,降低性能。

后来,我查了官方的文档,才知道,多线程的开启,是有条件的,不是所有场景都适合开启,官方建议,只有在QPS很高,或者value很大,网络IO成为瓶颈的时候,才开启多线程,而且,IO线程的数量,也不是越多越好,一般设置成CPU核心数的一半,或者和CPU核心数一样,太多了,反而会因为上下文切换,降低性能。

我按照官方的建议,调整了IO线程的数量,并且只在网络IO繁忙的场景下开启,性能才稳定下来,没有出现下降的情况。

除了性能的问题,多线程还有一个坑,就是和某些命令的兼容性问题。比如,有些阻塞命令,比如BLPOP、BRPOP,在多线程模式下,有一些小问题,有时候会出现阻塞不正常的情况,虽然不是大问题,但也影响使用。

还有,多线程模式下,慢查询日志,也有一些变化,以前的慢查询,都是在主线程里的,现在,IO线程里的操作,也会记录慢查询,排查问题的时候,要注意区分。

这些坑,虽然都不是致命的,但也花了我不少时间去排查和调整,让我对多线程的热情,消减了不少。

六、踩坑三:ACL的坑

第三个大坑,就是ACL的坑。

ACL权限控制,是我很期待的一个功能,最开始体验的时候,感觉还不错,权限控制也生效了。但深入使用之后,发现了不少坑。

第一个坑,就是ACL的配置,比较复杂,规则很多,容易写错。ACL的规则,包括命令权限、key权限、分类权限等,组合起来,非常灵活,但也很复杂,我在配置的时候,好几次都写错了规则,导致权限不对,要么该拒绝的没拒绝,要么该允许的没允许,排查了很久,才搞清楚规则的写法。

第二个坑,就是和现有系统的兼容性问题。我们的系统里,有很多地方,用到了Redis,不同的服务,用的key前缀不一样,操作的命令也不一样。要给每个服务,都配置单独的ACL用户,并且设置正确的权限,工作量很大,而且很容易出错。

而且,有些服务,用到的命令比较多,或者key的命名不规范,没有统一的前缀,配置ACL的时候,很麻烦,要么权限给大了,不安全,要么权限给小了,服务用不了。

第三个坑,就是ACL的性能开销。开启ACL之后,每个命令执行前,都要做权限检查,这会有一定的性能开销,虽然不大,但在高QPS的场景下,还是有一定的影响。而且,ACL用户越多,规则越复杂,性能开销就越大。

第四个坑,就是ACL的持久化和同步问题。ACL的配置,可以存在配置文件里,也可以存在ACL文件里,还可以用命令动态修改。动态修改的ACL,需要保存到文件里,才能持久化,不然重启之后就没了。而且,在集群模式下,ACL的配置,需要在每个节点上都配置,或者同步过去,不然,不同节点的权限不一样,会有问题。

这些坑,让我意识到,ACL虽然功能强大,但要在生产环境用好,还是需要花不少功夫的,不是简单配置一下就行了。

七、踩坑四:集群和持久化的问题

第四个大坑,就是集群和持久化的问题。

我们的Redis,是集群部署的,用的是Redis Cluster。我把Redis升级到6.0之后,最开始,集群是正常的,节点之间能通信,数据也能正常分片。

但在测试的时候,发现了一些问题。比如,在做故障转移的时候,有时候会出现数据不一致的情况,某个节点的数据,和其他节点不一样,排查了很久,才发现,是Redis 6.0的复制功能,有一些变化,和老版本的节点,混合部署的时候,有兼容性问题,我把所有节点,都升级到6.0之后,这个问题才解决。

还有,持久化也有一些小问题。Redis 6.0用了新的RDB格式,比老版本的RDB,信息更丰富,加载更快,但新的RDB格式,老版本的Redis是读不了的,也就是说,一旦用Redis 6.0生成了RDB文件,就不能回退到老版本了,这对于升级来说,是一个风险,万一升级出问题,想回退,都回退不了。

而且,AOF持久化,也有一些变化,Redis 6.0的AOF,性能更好了,但也有一些新的配置项,需要调整,我最开始,用老的配置,发现AOF的性能,不如预期,调整了配置之后,才好起来。

还有,在混合持久化模式下,也遇到了一些小问题,比如,重启加载的时候,偶尔会报错,虽然重新加载就好了,但也说明,RC版本,还是有一些不稳定的地方。

这些问题,虽然都不是致命的,但也让我意识到,RC版本,毕竟不是正式版,还是有一些bug和不稳定的地方,生产环境用,风险还是比较大的。

八、差点放弃

经历了这么多坑,我真的差点就放弃了。

那时候,我折腾了好几天,兼容性问题、多线程的坑、ACL的坑、集群和持久化的问题,一个接一个,解决了一个,又来一个,搞得我身心俱疲。

而且,最关键的是,我最期待的多线程性能提升,在我们的场景下,提升并不大,只有10%左右,付出这么多时间和精力,去升级,感觉有点不值得。

还有,RC版本的稳定性,也让我担心,虽然测试下来,没什么大问题,但毕竟不是正式版,万一在生产环境出问题,那就麻烦了,到时候,回退都回退不了,因为RDB格式不兼容。

那几天,我一直在犹豫,要不要放弃,继续用Redis 5.0,等正式版发布了,稳定了,再升级。

九、坚持下来的理由

犹豫了很久,最后,我还是决定,先在测试环境,继续体验和优化,不着急上生产,等正式版发布了,再考虑生产环境的升级。

坚持下来,主要有这几个理由:

1. 新特性确实有价值

虽然多线程的性能提升,在我们的场景下不大,但ACL权限控制、客户端缓存这些新特性,确实很有价值,能解决我们实际的痛点,尤其是ACL,我们一直想要细粒度的权限控制,Redis 6.0终于有了,这个功能,值得我们等待和升级。

2. 提前熟悉,为正式版做准备

现在提前体验,提前踩坑,等正式版发布了,我们就能更快地上手,更少地踩坑,升级也会更顺利。现在在测试环境踩的坑,都是在为生产环境的升级积累经验。

3. 性能还是有提升的

虽然多线程的整体提升不大,但在大value的场景下,提升还是很明显的,我们也有一些大value的场景,升级之后,这些场景的性能,会有改善,这也是有价值的。

4. 社区活跃,问题会逐步解决

Redis的社区很活跃,RC版本的问题,会逐步修复,等正式版发布的时候,这些问题,应该都解决了,稳定性也会有保障。现在提前反馈问题,也能帮助Redis变得更好。

就这样,我没有放弃,继续在测试环境,体验和优化Redis 6.0,把遇到的问题,都记录下来,反馈给社区,也为以后的生产环境升级,做准备。

十、给想提前体验Redis 6.0的朋友的建议

最后,给想提前体验Redis 6.0的朋友,一些建议,希望大家能少踩坑。

1. 不要在生产环境用RC版本

RC版本,毕竟不是正式版,还是有一些bug和不稳定的地方,生产环境,一定要等正式版发布,稳定了再用,不要为了尝鲜,在生产环境用RC版本,出了问题,得不偿失。

2. 注意客户端和中间件的兼容性

升级Redis,不只是升级服务端,客户端、各种中间件,都要考虑兼容性,提前测试,提前升级,避免到时候,出现兼容性问题,影响业务。

3. 多线程不要盲目开启

多线程不是所有场景都适合,不要盲目开启,要根据自己的业务场景,测试之后,再决定要不要开启,以及开启几个IO线程。一般来说,只有在网络IO成为瓶颈的时候,开启多线程才有意义。

4. ACL要提前规划

ACL的配置,比较复杂,要提前规划,统一key的命名规范,明确每个服务的权限,不要等到用的时候,才临时配置,那样很容易出错。

5. 注意数据格式的兼容性

Redis 6.0的RDB格式,和老版本不兼容,一旦升级,就很难回退,升级之前,一定要做好备份,并且想好回退方案,万一出问题,能及时回退。

6. 充分测试,不要着急上线

升级之前,一定要在测试环境,充分测试,包括功能测试、性能测试、兼容性测试、故障转移测试等,把所有的问题,都在测试环境暴露出来,解决了,再考虑上线,不要着急上线,避免在生产环境出问题。

十一、写在最后

Redis 6.0的体验之旅,从最开始的兴奋,到中间的踩坑,再到差点放弃,最后决定坚持下来,确实经历了很多。

总的来说,Redis 6.0的新特性,还是很有价值的,多线程IO、ACL、客户端缓存这些功能,都能解决实际的痛点,虽然现在的RC版本,还有一些问题和坑,但相信正式版发布的时候,这些问题都会解决,Redis 6.0会成为一个优秀的版本。

不过,升级是一个系统工程,不是简单地换个版本就行了,需要考虑兼容性、性能、稳定性、回退方案等各种因素,一定要谨慎,不要着急,等正式版发布,稳定了,再考虑生产环境的升级。

希望这篇文章,能给想提前体验Redis 6.0的朋友一些参考,也欢迎大家在评论区,分享自己体验Redis 6.0的经历和问题,一起交流讨论。

最后,期待Redis 6.0正式版的发布,相信它会给我们带来更多的惊喜。