一、Redis 4.0版本特性简介
1.1 Redis 模块系统
Redis 4.0 发生的最大变化就是加入了 模块系统,这个系统模块可以让用户通过自己编写的代码来扩展和实现 Redis 本身并不具备的功能,因为模块系统是通过高层次 API 实现的,它与 Redis 内核本身完全分离、互不干扰,所以用户可以在有需要的情况下才启用这个功能。
目前已经有用户使用这个功能开发了各种各样的模块,比如 Redis Labs 开发的一些模块就可以在 http://redismodules.com 看到。
模块功能使得用户可以将 Redis 用作基础设施,并在上面构建更多功能,这给 Redis 带来了无数新的可能性。
1.2 PSYNC 2.0 (主从同步优化)
Redis 4.0 新版本的 PSYNC
命令解决了旧版本的 Redis 在复制时的一些不够优化的地方:
- 在旧版本 Redis 中,如果一个从服务器在 FAILOVER 之后成为了新的主节点,那么其它从节点在复制这个新主的时候就必须进行全量复制。 在 Redis 4.0 中,新主和从服务器在处理这种情况时,将在条件允许的情况下使用部分复制。
- 在旧版本 Redis 中,一个从服务器如果重启了,那么它就必须与主服务器重新进行全量复制,在 Redis 4.0 中,只要条件允许,主从在处理这种情况时将使用部分复制。
- 在旧版本中,当复制为链式复制的时候,如 A—>B—>C ,主节点为A。当A出现问题,C节点不能正常复制B节点的数据。当提升B为主节点,C需要全量同步B的数据。在PSYNC2:PSYNC2解决了链式复制之间的关联性。A出现问题不影响C节点,B提升为主C不需要全量同步。
- 在使用星形复制时,如一主两从。A—>B , A—>C ,主节点为A。当A出现问题,B提升为主节点,C 重新指向主节点B。使用同步机制PSYNC2,C节点只做增量同步即可。在使用sentinel故障转移可以较少数据重新同步的延迟时间,避免大redis同步出现的网络带宽占满。
1.3 缓存驱逐策略优化
新添加了Last Frequently Used(LFU)缓存驱逐策略; LFU:最不经常使用。在一段时间内,使用次数最少的数据,优先被淘汰;
另外 Redis 4.0 还对已有的缓存驱逐策略进行了优化,使得它们能够更健壮、高效、快速和精确。
1.4 Lazy Free(UNLINK
命令)
在 Redis 4.0 之前,用户在使用 DEL
命令删除体积较大的键,又或者在使用 FLUSHDB
和 FLUSHALL
删除包含大量键的数据库时,都可能会造成服务器阻塞。
为了解决以上问题,Redis 4.0 新添加了 UNLINK
命令,这个命令是 DEL
命令的异步版本,它可以将删除指定键的操作放在后台线程里面执行,从而尽可能地避免服务器阻塞:
|
|
因为一些历史原因,执行同步删除操作的 DEL
命令将会继续保留。此外,Redis 4.0 中的 FLUSHDB
和 FLUSHALL
这两个命令都新添加了 ASYNC
选项,带有这个选项的数据库删除操作将在后台线程进行:
|
|
还有,执行 rename oldkey newkey
时,如果newkey已经存在,Redis会先删除已经存在的newkey,这也会引发上面提到的删除大key问题。如果想让Redis在这种场景下也使用lazyfree的方式来删除,可以按如下配置:
|
|
1.5 交换数据库
Redis 4.0 对数据库命令的另外一个修改是新增了 SWAPDB
命令,这个命令可以对指定的两个数据库进行互换: 比如说,通过执行命令 SWAPDB 0 1
,我们可以将原来的数据库 0 变成数据库 1 ,而原来的数据库 1 则变成数据库 0。
1.6 混合持久化
Redis 4.0 新增了 RDB-AOF 混合持久化格式,这是一个可选的功能,在开启了这个功能之后,AOF 重写产生的文件将同时包含 RDB 格式的内容和 AOF 格式的内容,其中 RDB 格式的内容用于记录已有的数据,而 AOF 格式的内存则用于记录最近发生了变化的数据,这样 Redis 就可以同时兼有 RDB 持久化和 AOF 持久化的优点 —— 既能够快速地生成重写文件,也能够在出现问题时,快速地载入数据。
这个功能可以通过配置项:aof-use-rdb-preamble
进行开启。
1.7 内存命令
新添加了一个MEMORY命令,这个命令可以用于视察内存使用情况,并进行相应的内存管理操作:
|
|
其中,使用MEMORY USAGE子命令可以估算储存给定键所需的内存:
|
|
使用MEMORY STATS子命令可以查看 Redis 当前的内存使用情况:
|
|
使用MEMORY PURGE子命令可以要求分配器释放内存:
|
|
使用MEMORY MALLOC-STATS子命令可以展示分配器内部状态:
|
|
1.8 兼容 NAT 和 Docker
在Redis Cluster集群模式下,集群的节点需要告诉用户或者是其它节点连接自己的IP和端口。
默认情况下,Redis会自动检测自己的IP和从配置中获取绑定的PORT,告诉客户端或者是其它节点。而在Docker环境中,如果使用的不是host网络模式,在容器内部的IP和PORT都是隔离的,那么客户端和其它节点无法通过节点公布的IP和PORT建立连接。
4.0中增加了三个配置:
- cluster-announce-ip:要宣布的IP地址
- cluster-announce-port:要宣布的数据端口
- cluster-announce-bus-port:节点通信端口,默认在端口前+1
如果配置了以后,Redis节点会将配置中的这些IP和PORT告知客户端或其它节点。而这些IP和PORT是通过Docker转发到容器内的临时IP和PORT的。
Redis 4.0 之后支持自动内存碎片整理,通过以下选项进行配置:
|
|
实现原理可参考:https://zhuanlan.zhihu.com/p/67381368
1.9 其它方面
- Redis Cluster的故障检测方式改变,node之间的通讯减少;
- 慢日志记录客户端来源IP地址,这个小功能对于故障排查很有用处;
- 新增zlexcount命令,用于sorted set中,和zrangebylex类似,不同的是zrangebylex返回member,而zlexcount是返回符合条件的member个数;
二、Redis 5.0版本特性简介
2.1 Stream类型
Stream与Redis现有数据结构比较:
Stream | List、Pub/Sub、Zset |
---|---|
获取元素高效,复杂度为O(logN) | List获取元素的复杂度为O(N) |
支持offset,每个消息元素有唯一id。不会因为新元素加入或者其它元素淘汰而改变id。 | List没有offset概念,如果有元素被逐出,无法确定最新的元素 |
支持消息元素持久化,可以保存到AOF和RDB中 | Pub/Sub不支持持久化消息 |
支持消费分组 | Pub/Sub不支持消费分组 |
支持ACK(消费确认) | Pub/Sub不支持 |
Stream性能与消费者数量无明显关系 | Pub/Sub性能与客户端数量负相关 |
允许按时间线逐出历史数据,支持block,给予radix tree和listpack,内存开销少 | Zset不能重复添加相同元素,不支持逐出和block,内存开销大 |
不能从中间删除消息元素 | Zet支持删除任意元素 |
2.2 新的Redis模块API
新的Redis模块API:定时器(Timers)、集群(Cluster)和字典API(Dictionary APIs)。
2.3 集群管理器更改
redis3.x和redis4.x的集群管理主要依赖基于Ruby的redis-trib.rb脚本,redis5.0彻底抛弃了它,将集群管理功能全部集成到完全用C写的redis-cli中。可以通过命令redis-cli –cluster help查看帮助信息。
2.4 Lua改进
将Lua脚本更好地传播到 replicas/AOF;
Lua脚本现在可以超时并在副本中进入BUSY状态。
2.5 RDB格式变化
Redis5.0开始,RDB快照文件中增加存储key逐出策略LRU和LFU:
- LRU(Least Recently Used):最近最少使用。长期未使用的数据,优先被淘汰。
- LFU(Least Frequently Used):最不经常使用。在一段时间内,使用次数最少的数据,优先被淘汰。
Redis5.0的RDB文件格式有变化,向下兼容。因此如果使用快照的方式迁移,可以从Redis低版本迁移到Redis5.0,但不能从Redis5.0迁移到低版本。
2.6 动态HZ
以前redis版本的配置项hz都是固定的,redis5.0将hz动态化是为了平衡空闲CPU的使用率和响应能力。当然这个是可配置的,只不过在5.0中默认是动态的,其对应的配置为:dynamic-hz yes
2.7 ZPOPMIN&ZPOPMAX命令
- ZPOPMIN key [count] 在有序集合ZSET所有key中,删除并返回指定count个数得分最低的成员,如果返回多个成员,也会按照得分高低(value值比较),从低到高排列。
- ZPOPMAX key [count] 在有序集合ZSET所有key中,删除并返回指定count个数得分最高的成员,如果返回多个成员,也会按照得分高低(value值比较),从高到低排列。
- BZPOPMIN key [key …] timeout ZPOPMIN的阻塞版本。
- BZPOPMAX key [key …] timeout ZPOPMAX的阻塞版本。
2.8 CLIENT新增命令
- CLIENT UNBLOCK
格式:
CLIENT UNBLOCK client-id [TIMEOUT|ERROR]
用法: 当客户端因为执行具有阻塞功能的命令如BRPOP、XREAD被阻塞时,该命令可以通过其它连接解除客户端的阻塞 - CLIENT ID 该命令仅返回当前连接的ID。每个连接ID都有某些保证: 它永远不会重复,可以判断当前链接是否断开过; ID是单调递增的。可以判断两个链接的接入顺序。
2.9 其它事项
- 主动碎片整理V2:增强版主动碎片整理,配合Jemalloc版本更新,更快更智能,延时更低;
- HyperLogLog改进:在Redis5.0中,HyperLogLog算法得到改进,优化了计数统计时的内存使用效率;
- 更好的内存统计报告;
- 客户经常连接和断开连接时性能更好;
- 错误修复和改进;
- Jemalloc内存分配器升级到5.1版本;
- 许多拥有子命令的命令,新增了HELP子命令,如:XINFO help、PUBSUB help、XGROUP help…
- LOLWUT命令:没什么实际用处,根据不同的版本,显示不同的图案,类似安卓;
- 如果不为了API向后兼容,将不再使用 “slave” 一词;
- Redis核心在许多方面进行了重构和改进。
三、Redis 6.0版本特性简介
3.1 多线程IO
Redis的多线程部分只是用来处理网络数据的读写和协议解析,执行命令仍然是单线程顺序执行。所以不需要去考虑控制 key、lua、事务,LPUSH/LPOP 等等的并发及线程安全问题。
Redis6.0的多线程默认是禁用的,只使用主线程。如需开启需要修改redis.conf配置文件:io-threads-do-reads yes
。开启多线程后,还需要设置线程数,否则是不生效的。修改redis.conf配置文件:io-threads
,关于线程数的设置,官方有一个建议:4核的机器建议设置为2或3个线程,8核的建议设置为6个线程,线程数一定要小于机器核数。还需要注意的是,线程数并不是越大越好,官方认为超过了8个基本就没什么意义了。
更多关于redis 6.0多线程的讲解,请查看:https://www.cnblogs.com/madashu/p/12832766.html
3.2 SSL支持
连接支持SSL协议,更加安全。
3.3 ACL支持
在之前的版本中,Redis都会有这样的问题:用户执行FLUSHAL,现在整个数据库就空了在以前解决这个问题的办法可能是在Redis配置中将危险命令进行rename,这样将命令更名为随机字符串或者直接屏蔽掉,以满足需要。当有了ACL之后,就可以控制比如:这个连接只允许使用RPOP,LPUSH这些命令,其它命令都无法调用。
Redis ACL是Access Control List(访问控制列表)的缩写,该功能允许根据可以执行的命令和可以访问的键来限制某些连接。它的工作方式是:在客户端连接之后,需要客户端进行身份验证,以提供用户名和有效密码:如果身份验证阶段成功,则将连接与指定用户关联,并且该用户具有指定的限制。可以对Redis进行配置,使新连接通过“默认”用户进行身份验证(这是默认配置),但是只能提供特定的功能子集。
在默认配置中,Redis 6(第一个具有ACL的版本)的工作方式与Redis的旧版本完全相同,也就是说,每个新连接都能够调用每个可能的命令并访问每个键,因此ACL功能对于客户端和应用程序与旧版本向后兼容。同样,使用 requirepass
配置指令配置密码的旧方法仍然可以按预期工作,但是现在它的作用只是为默认用户设置密码。
Redis AUTH命令在Redis 6中进行了扩展,因此现在可以在两个参数的形式中使用它:
|
|
也可按照旧格式使用时,即:
|
|
Redis提供了一个新的命令ACL来维护Redis的访问控制信息,详情见:https://redis.io/topics/acl
3.4 RESP(Redis Serialization Protocol)
RESP(Redis Serialization Protocol)是 Redis 服务端与客户端之间通信的协议。Redis 6之前使用的是 RESP2,而Redis 6开始在兼容RESP2的基础上,开始支持RESP3。在Redis 6中我们可以使用HELLO命令在RESP2和RESP3协议之间进行切换:
|
|
推出RESP3的目的:一是因为希望能为客户端提供更多的语义化响应(semantical replies),降低客户端的复杂性,以开发使用旧协议难以实现的功能;另一个原因是为了实现 Client side caching(客户端缓存)功能。详细见:https://github.com/antirez/RESP3/blob/master/spec.md
|
|
3.5 客户端缓存
基于 RESP3 协议实现的客户端缓存功能。为了进一步提升缓存的性能,将客户端经常访问的数据cache到客户端。减少TCP网络交互。不过该特性目前合并到了unstable 分支,作者说等6.0 GA版本之前,还要修改很多。
客户端缓存的功能是该版本的全新特性,服务端能够支持让客户端缓存values,Redis作为一个本身作为一个缓存数据库,自身的性能是非常出色的,但是如果可以在Redis客户端再增加一层缓存结果,那么性能会更加的出色。Redis实现的是一个服务端协助的客户端缓存,叫做tracking。客户端缓存的命令是:
|
|
当tracking开启时,Redis会"记住"每个客户端请求的key,当key的值发现变化时会发送失效信息给客户端。失效信息可以通过 RESP3 协议发送给请求的客户端,或者转发给一个不同的连接(支持RESP2+ Pub/Sub)。
更多信息见:http://antirez.com/news/130
3.6 集群代理
因为 Redis Cluster 内部使用的是P2P中的Gossip协议,每个节点既可以从其它节点得到服务,也可以向其它节点提供服务,没有中心的概念,通过一个节点可以获取到整个集群的所有信息。所以如果应用连接Redis Cluster可以配置一个节点地址,也可以配置多个节点地址。但需要注意如果集群进行了上下节点的的操作,其应用也需要进行修改,这样会导致需要重启应用,非常的不友好。从Redis 6.0开始支持了Prxoy,可以直接用Proxy来管理各个集群节点。本文来介绍下如何使用官方自带的proxy:redis-cluster-proxy 通过使用 redis-cluster-proxy 可以与组成Redis集群的一组实例进行通讯,就像是单个实例一样。Redis群集代理是多线程的,使用多路复用通信模型,因此每个线程都有自己的与群集的连接,该连接由属于该线程本身的所有客户端共享。
在某些特殊情况下(例如MULTI事务或阻塞命令),多路复用将被禁用;并且客户端将拥有自己的集群连接。这样客户端仅发送诸如GET和SET之类的简单命令就不需要Redis集群的专有连接。
redis-cluster-proxy的主要功能特点:
- 路由:每个查询都会自动路由到集群的正确节点
- 多线程
- 支持多路复用和专用连接模型
- 在多路复用上下文中,可以确保查询执行和答复顺序
- 发生ASK | MOVED错误后自动更新集群的配置:当答复中发生此类错误时,代理通过获取集群的更新配置并重新映射所有插槽来自动更新集群。 更新完成后所有查询将重新执行,因此,从客户端的角度来看,一切正常进行(客户端将不会收到ASK | MOVED错误:它们将在收到请求后直接收到预期的回复) 群集配置已更新)。
- 跨槽/跨节点查询:支持许多命令,这些命令涉及属于不同插槽(甚至不同集群节点)的多个键。这些命令会将查询分为多个查询,这些查询将被路由到不同的插槽/节点。 这些命令的回复处理是特定于命令的。 某些命令(例如MGET)将合并所有答复,就好像它们是单个答复一样。 其它命令(例如MSET或DEL)将汇总所有答复的结果。 由于这些查询实际上破坏了命令的原子性,因此它们的用法是可选的(默认情况下禁用)。
- 一些没有特定节点/插槽的命令(例如DBSIZE)将传递到所有节点,并且将对映射的回复进行映射缩减,以便得出所有回复中包含的所有值的总和。
- 可用于执行某些特定于代理的操作的附加PROXY命令
3.7 Disque module
这个本来是作者几年前开发的一个基于 Redis 的消息队列工具,但多年来作者发现 Redis 在持续开发时,它也要持续把新的功能合并到这个Disque 项目里面,这里有大量无用的工作。因此这次它在 Redis 的基础上通过 Modules 功能实现 Disque。
如果业务并不需要保持严格消息的顺序,这个 Disque 能提供足够简单和快速的消息队列功能。
3.8 其它事项
- 新的Expire算法:用于定期删除过期key的函数activeExpireCycle被重写,以便更快地收回已经过期的key;
- 提供了许多新的Module API;
- 从服务器也支持无盘复制:在用户可以配置的特定条件下,从服务器现在可以在第一次同步时直接从套接字加载RDB到内存;
- SRANDMEMBER命令和类似的命令优化,使其结果具有更好的分布;
- 重写了Systemd支持;
- 官方redis-benchmark工具支持cluster模式;
- 提升了RDB日志加载速度;