在刚刚过去的5.1长假中,Redis的作者antirez在个人博客宣布Redis 6.0.0 stable版本release(http://antirez.com/news/132)。在新版本中,增加了诸多的特性,其中最令人激动的特性就是支持了IO多线程,antirez称之为最大的一次更新(it is the largest release of Redis ever as far as I can tell
)下面,我们来看一下6.0版本的新特性。
antirez对其中的几个新特性进行了介绍,我们来具体看一下。
Redis 6.0开始支持多线程IO,Redis 终于实现多线程了!?先打住,多线程是不可能多线程的,别多想~
Redis 在处理客户端的请求时,包括获取(Socket 读)、解析、执行、内容返回(Socket 写)等都由一个顺序串行的主线程处理,这就是所谓的“单线程”。
官方曾做过类似问题的回复:使用 Redis 时,几乎不存在 CPU 成为瓶颈的情况, Redis 主要受限于内存和网络。
例如在一个普通的 Linux 系统上,Redis 通过使用 Pipelining 每秒可以处理 100 万个请求,所以如果应用程序主要使用 O(N) 或 O(log(N)) 的命令,它几乎不会占用太多 CPU。
使用了单线程后,可维护性高。
如果使用多线程执行命令,引入的问题例如复杂性、锁的效率会大大增加。多线程模型虽然在某些方面表现优异,但是它却引入了程序执行顺序的不确定性,带来了并发读写的一系列问题,增加了系统复杂度、同时可能存在线程切换、甚至加锁解锁、死锁造成的性能损耗。
Redis 通过 AE 事件模型以及 IO 多路复用等技术,处理性能非常高,因此没有必要使用多线程。
单线程机制使得 Redis 内部实现的复杂度大大降低,Hash 的惰性 Rehash、Lpush 等等 “线程不安全” 的命令都可以无锁进行。
从 Redis 自身角度来说,因为读写网络的 Read/Write 系统调用占用了 Redis 执行期间大部分 CPU 时间,瓶颈主要在于网络的 IO 消耗。
优化主要有两个方向:
协议栈优化的这种方式跟 Redis 关系不大,支持多线程是一种最有效最便捷的操作方式。
所以总结起来,Redis 支持多线程主要就是两个原因:
Redis 6.0 的多线程默认是禁用的,只使用主线程。如需开启需要修改 redis.conf 配置文件:
io-threads-do-reads yes
开启多线程后,还需要设置线程数,否则是不生效的。同样修改 redis.conf 配置文件:
io-threads 4
关于线程数的设置,官方有一个建议:4 核的机器建议设置为 2 或 3 个线程,8 核的建议设置为 6 个线程,线程数一定要小于机器核数。
还需要注意的是,线程数并不是越大越好,官方认为超过了 8 个基本就没什么意义了。
作者 antirez 在 RedisConf 2019 分享时曾提到:Redis 6 引入的多线程 IO 特性对性能提升至少是一倍以上。
美团技术团队的官方博客中,表示压测的结果,开启多线程IO后性能提升显著:
详细参见:https://zhuanlan.zhihu.com/p/76788470
RESP 全称 REdis Serialization Protocol,是 Redis 服务端与客户端之间通信的协议。RESP2 协议已经使用了很多年,antirez希望使用一个新的协议来支持新的特性,一开始antirez是打算完全放弃 RESP2 的,全面切换为 RESP3 协议,但是很快又改变了主意,所以 RESP3 协议在Redis 6里面是让用户决定的,Redis连接用 RESP2 建立,如果用户用HELLO
命令握手,就可以进入RESP3协议的模式。
为什么需要新协议?因为旧协议不满足语义上的需求了。尽管有一些 RESP3 专属的新功能,但是最主要的原因是我们需要允许Redis返回复杂的数据类型,而且不用客户端“必须”得知道如扁平数组要转成什么类型、数字怎么正确和布尔值对应等才能处理。
在 RESP2 中,所有的返回内容,都是一个字符串数组的形式,不管是 list
还是 sorted set
。因此客户端需要自行去根据类型进行解读,增加了客户端实现的复杂性。
下面以具体的命令展示 RESP3 中的具体变化。
127.0.0.1:6379> HSET myhash a 1 b 2 c 3
(integer) 3
127.0.0.1:6379> HGETALL myhash
1) "a"
2) "1"
3) "b"
4) "2"
5) "c"
6) "3"
127.0.0.1:6379> HELLO 3 #转换成RESP3的命令
1# "server" => "redis"
2# "version" => "999.999.999"
3# "proto" => (integer) 3
4# "id" => (integer) 5
5# "mode" => "standalone"
6# "role" => "master"
7# "modules" => (empty array)
127.0.0.1:6379> HGETALL myhash
1# "a" => "1"
2# "b" => "2"
3# "c" => "3"
以前返回两个field的hash,就是直接无差别地返回4个值,而新的RESP3就会告诉客户端返回两个key-value,通过%
表示键值对(也成为map类型)的个数。
更加详细的设计,请参见 RESP3 的详细设计:https://github.com/antirez/RESP3/blob/master/spec.md
目前的 Redis(5及以下版本),没有用户权限管理这个概念,只有一个AUTH
密码验证功能,基本上能够接入的用户就是root
用户。
Redis需要ACLs(权限控制),因为在更大的环境中人们需要权限控制模块去更好地控制不同客户端能做哪些操作。另一个点就是,加入权限控制也是为了在从应用bug中更好地保护数据。
如果账户(worker)只能执行BRPOP
、 LPUSH
,那其他的开发者在不小心加了一行用于测试、清理测试数据的FLUSHALL
而导致数据丢失、然后经历5小时恢复的噩梦的概率会更低。
Redis 6 中加入ACL的功能,能够对接入的用户进行三个层面的权限控制:
具体详细操作可以参见官方文档说明:https://redis.io/topics/acl
关于这一点,作者antirez只是简单的提了一下,并没有详细的解释,这里就不妄加臆断。
客户端缓存的功能是该版本的全新特性,服务端能够支持让客户端缓存values,Redis作为一个本身作为一个缓存数据库,自身的性能是非常出色的,但是如果可以在Redis客户端再增加一层缓存结果,那么性能会更加的出色。Redis实现的是一个服务端协助的客户端缓存,叫做tracking
。客户端缓存的命令是:
CLIENT TRACKING ON|OFF [REDIRECT client-id] [PREFIX prefix] [BCAST] [OPTIN] [OPTOUT] [NOLOOP]
当tracking
开启时, Redis会"记住"每个客户端请求的key,当key的值发现变化时会发送失效信息给客户端(invalidation message
)。失效信息可以通过 RESP3 协议发送给请求的客户端,或者转发给一个不同的连接(支持RESP2+ Pub/Sub)。
更多的详细操作,这里就不进行过多的介绍了,具体可以参见:http://antirez.com/news/130
这是一个令人激动的新特性,但是作者antirez对这个特性表示是6.0中最不成熟的特性(however I think that right now this is the most immature feature of Redis 6
)。并表示在后续的版本中会继续进行优化,我们拭目以待。
针对 Cluster 的代理,这么多年了,仍然有不少人在Cluster的接入方式上挣扎,因为缺少合适的驱动而无法使用Cluster。所以开发了这个Proxy功能。作者也强调,虽然这个Proxy 让 Cluster 拥有了像单实例一样的接入方式,但是本质上还是 Cluster,不支持的命令还是不会支持,比如跨 slot 的多Key操作。
详情参见:https://github.com/RedisLabs/redis-cluster-proxy
本篇参考:
正式支持多线程!Redis 6.0与老版性能对比评测
Redis 6 RC1 is out today
Redis 6 新功能提前看