最近研究研究新版本redis的新特性,网上查了查资料,这里记录一下。

0. Redis的版本迭代和里程碑

Redis从发布至今,已经有十余年的时光了,一直遵循着自己的命名规则:

  • 版本号第二位如果是奇数,则为非稳定版本 如2.7、2.9、3.1
  • 版本号第二位如果是偶数,则为稳定版本 如2.6、2.8、3.0、3.2

当前奇数版本就是下一个稳定版本的开发版本,如2.9版本是3.0版本的开发版本
我们可以通过redis.io官网来下载自己感兴趣的版本进行源码阅读:

历史发布版本的源码:https://download.redis.io/releases/

其中有几个里程碑式的版本,需要我们了解下:

当然,以前的1、2、3版本就不介绍了。

1.Redis 1

2.Redis 2

3.Redis 3

4.Redis 4

4.1 模块系统

       Redis 4.0 发生的最大变化就是加入了模块系统, 这个系统可以让用户通过自己编写的代码来扩展和实现 Redis 本身并不具备的功能,因为模块系统是通过高层次 API 实现的, 它与 Redis 内核本身完全分离、互不干扰, 所以用户可以在有需要的情况下才启用这个功能。目前已经有人使用这个功能开发了各种各样的模块, 比如 Redis Labs 开发的一些模块就可以在 http://redismodules.com 看到。模块功能使得用户可以将 Redis 用作基础设施, 并在上面构建更多功能, 这给 Redis 带来了无数新的可能性。

4.2 PSYNC 2.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同步出现的网络带宽占满。

4.3 缓存驱逐策略优化

新添加了Last Frequently Used(LFU)缓存驱逐策略;

LFU:最不经常使用。在一段时间内,使用次数最少的数据,优先被淘汰;

另外 Redis 4.0 还对已有的缓存驱逐策略进行了优化, 使得它们能够更健壮、高效、快速和精确。

4.4 Lazy Free

        在 Redis 4.0 之前, 用户在使用 DEL命令删除体积较大的键, 又或者在使用 FLUSHDBFLUSHALL删除包含大量键的数据库时, 都可能会造成服务器阻塞。

        ​为了解决以上问题, Redis 4.0 新添加了UNLINK命令, 这个命令是DEL命令的异步版本, 它可以将删除指定键的操作放在后台线程里面执行, 从而尽可能地避免服务器阻塞:

redis> UNLINK fruits
(integer) 1
        ​ 因为一些历史原因, 执行同步删除操作的DEL命令将会继续保留。此外, Redis 4.0 中的 FLUSHDBFLUSHALL这两个命令都新添加了ASYNC选项, 带有这个选项的数据库删除操作将在后台线程进行:
redis> FLUSHDB ASYNC
OK

redis> FLUSHALL ASYNC
OK
        ​ 还有,执行 rename oldkey newkey时,如果newkey已经存在,Redis会先删除已经存在的newkey,这也会引发上面提到的删除大key问题。如果想让Redis在这种场景下也使用lazyfree的方式来删除,可以按如下配置:
lazyfree-lazy-server-del yes

4.5 交换数据库

        ​Redis 4.0 对数据库命令的另外一个修改是新增了SWAPDB命令, 这个命令可以对指定的两个数据库进行互换: 比如说, 通过执行命令 SWAPDB 0 1 , 我们可以将原来的数据库 0 变成数据库 1 , 而原来的数据库 1 则变成数据库 0。

4.6 混合持久化

        ​Redis 4.0 新增了 RDB-AOF 混合持久化格式, 这是一个可选的功能, 在开启了这个功能之后, AOF 重写产生的文件将同时包含 RDB 格式的内容和 AOF 格式的内容, 其中 RDB 格式的内容用于记录已有的数据, 而 AOF 格式的内存则用于记录最近发生了变化的数据, 这样 Redis 就可以同时兼有 RDB 持久化和 AOF 持久化的优点 —— 既能够快速地生成重写文件, 也能够在出现问题时, 快速地载入数据。这个功能可以通过配置项:aof-use-rdb-preamble进行开启。

4.7 内存命令

         新添加了一个MEMORY命令, 这个命令可以用于视察内存使用情况, 并进行相应的内存管理操作:

redis> MEMORY HELP
1) "MEMORY USAGE <key> [SAMPLES <count>] - Estimate memory usage of key"
2) "MEMORY STATS                         - Show memory usage details"
3) "MEMORY PURGE                         - Ask the allocator to release memory"
4) "MEMORY MALLOC-STATS                  - Show allocator internal stats"
其中, 使用MEMORY USAGE子命令可以估算储存给定键所需的内存:
redis> SET msg "hello world"
OK

redis> SADD fruits apple banana cherry
(integer) 3

redis> MEMORY USAGE msg
(integer) 62

redis> MEMORY USAGE fruits
(integer) 375

使用MEMORY STATS子命令可以查看 Redis 当前的内存使用情况:

redis> MEMORY STATS
1) "peak.allocated"
2) (integer) 1014480
3) "total.allocated"
4) (integer) 1014512
5) "startup.allocated"
6) (integer) 963040
7) "replication.backlog"
8) (integer) 0
9) "clients.slaves"
10) (integer) 0
11) "clients.normal"
12) (integer) 49614
13) "aof.buffer"
14) (integer) 0
15) "db.0"
16) 1) "overhead.hashtable.main"
    2) (integer) 264
    3) "overhead.hashtable.expires"
    4) (integer) 32
17) "overhead.total"
18) (integer) 1012950
19) "keys.count"
20) (integer) 5
21) "keys.bytes-per-key"
22) (integer) 10294
23) "dataset.bytes"
24) (integer) 1562
25) "dataset.percentage"
26) "3.0346596240997314"
27) "peak.percentage"
28) "100.00315093994141"
29) "fragmentation"
30) "2.1193723678588867"
使用 MEMORY PURGE子命令可以要求分配器释放内存:
redis> MEMORY PURGE
OK
使用 MEMORY MALLOC-STATS子命令可以展示分配器内部状态:
127.0.0.1:6306> MEMORY MALLOC-STATS
___ Begin jemalloc statistics ___
Version: 4.0.3-0-ge9192eacf8935e29fc62fddc2701f7942b1cc02c
Assertions disabled
Run-time option settings:
  opt.abort: false
  opt.lg_chunk: 21
  opt.dss: "secondary"
  opt.narenas: 32
  opt.lg_dirty_mult: 3 (arenas.lg_dirty_mult: 3)
  opt.stats_print: false
  opt.junk: "false"
  opt.quarantine: 0
  opt.redzone: false
  opt.zero: false
  opt.tcache: true
  opt.lg_tcache_max: 15
CPUs: 8
Arenas: 32
Pointer size: 8
Quantum size: 8
Page size: 4096
Min active:dirty page ratio per arena: 8:1
Maximum thread-cached size class: 32768
Chunk size: 2097152 (2^21)
Allocated: 3101248, active: 3371008, metadata: 1590912, resident: 4657152, mapped: 8388608
Current active ceiling: 4194304
 
arenas[0]:
assigned threads: 1
dss allocation precedence: secondary
min active:dirty page ratio: 8:1
dirty pages: 823:10 active:dirty, 1 sweep, 3 madvises, 1032 purged
                            allocated      nmalloc      ndalloc    nrequests
small:                         586304        25408        13607    170099066
large:                        2514944      3967215      3967203      3967223
huge:                               0            1            1            1
total:                        3101248      3992624      3980811    174066290
active:                       3371008
mapped:                       6291456
metadata: mapped: 159744, allocated: 346240
...
...

4.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的。 Active Defrag

Redis 4.0 之后支持自动内存碎片整理(Active Defrag),通过以下选项进行配置:

# 开启自动内存碎片整理(总开关)
activedefrag yes
# 当碎片达到 100mb 时,开启内存碎片整理
active-defrag-ignore-bytes 100mb
# 当碎片超过 10% 时,开启内存碎片整理
active-defrag-threshold-lower 10
# 内存碎片超过 100%,则尽最大努力整理
active-defrag-threshold-upper 100
# 内存自动整理占用资源最小百分比
active-defrag-cycle-min 25
# 内存自动整理占用资源最大百分比
active-defrag-cycle-max 75

实现原理可参考:https://zhuanlan.zhihu.com/p/67381368

4.9 其他

  • Redis Cluster的故障检测方式改变,node之间的通讯减少;
  • 慢日志记录客户端来源IP地址,这个小功能对于故障排查很有用处;
  • 新增zlexcount命令,用于sorted set中,和zrangebylex类似,不同的是zrangebylex返回member,而zlexcount是返回符合条件的member个数;

5. Redis 5

5.1 Stream类型

Stream与Redis现有数据结构比较:

StreamList, 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支持删除任意元素

5.2 新的Redis模块API

新的Redis模块API:定时器(Timers)、集群(Cluster)和字典API(Dictionary APIs)。

5.3 集群管理器更改

       ​ redis3.x和redis4.x的集群管理主要依赖基于Ruby的redis-trib.rb脚本,redis5.0彻底抛弃了它,将集群管理功能全部集成到完全用C写的redis-cli中。可以通过命令redis-cli --cluster help查看帮助信息。

5.4 Lua改进

将Lua脚本更好地传播到 replicas/AOF;

Lua脚本现在可以超时并在副本中进入BUSY状态。

5.5 RDB格式变化

Redis5.0开始,RDB快照文件中增加存储key逐出策略LRU和LFU:

  • LRU(Least Recently Used):最近最少使用。长期未使用的数据,优先被淘汰。

  • LFU(Least Frequently Used):最不经常使用。在一段时间内,使用次数最少的数据,优先被淘汰。

Redis5.0的RDB文件格式有变化,向下兼容。因此如果使用快照的方式迁移,可以从Redis低版本迁移到Redis5.0,但不能从Redis5.0迁移到低版本。

5.6 动态HZ

​ 以前redis版本的配置项hz都是固定的,redis5.0将hz动态化是为了平衡空闲CPU的使用率和响应能力。当然这个是可配置的,只不过在5.0中默认是动态的,其对应的配置为:dynamic-hz yes

5.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的阻塞版本。

5.8 CLIENT新增命令

  • CLIENT UNBLOCK

    **格式:**CLIENT UNBLOCK client-id [TIMEOUT|ERROR]

    **用法:**当客户端因为执行具有阻塞功能的命令如BRPOP、XREAD被阻塞时,该命令可以通过其他连接解除客户端的阻塞

  • CLIENT ID

    该命令仅返回当前连接的ID。每个连接ID都有某些保证: 它永远不会重复,可以判断当前链接是否断开过; ID是单调递增的。可以判断两个链接的接入顺序。

5.9 其他

  • 主动碎片整理V2:增强版主动碎片整理,配合Jemalloc版本更新,更快更智能,延时更低;

  • HyperLogLog改进:在Redis5.0中,HyperLogLog算法得到改进,优化了计数统计时的内存使用效率;

  • 更好的内存统计报告;

  • 客户经常连接和断开连接时性能更好;

  • 错误修复和改进;

  • Jemalloc内存分配器升级到5.1版本;

  • 许多拥有子命令的命令,新增了HELP子命令,如:XINFO help、PUBSUB help、XGROUP help…

  • LOLWUT命令:没什么实际用处,根据不同的版本,显示不同的图案,类似安卓;

  • 如果不为了API向后兼容,我们将不再使用“slave”一词:查看原因 (opens new window)

  • Redis核心在许多方面进行了重构和改进。

6 Redis 6

6.1 众多新模块(modules)API

  Redis 6中模块API开发进展非常大,因为Redis Labs为了开发复杂的功能,从一开始就用上Redis模块。Redis可以变成一个框架,利用Modules来构建不同系统,而不需要从头开始写然后还要BSD许可。Redis一开始就是一个向编写各种系统开放的平台。如:Disque作为一个Redis Module使用足以展示Redis的模块系统的强大。集群消息总线API、屏蔽和回复客户端、计时器、模块数据的AOF和RDB等。

6.2 更好的过期循环(expire cycle)

  Redis 6重新编写了Redis活动到期周期,以更快地回收已到期的key。

6.3 多线程IO

redis 6.0 以前线程执行模式,如下操作在一个线程中执行完成

redis 6.0 线程执行模式:
可以通过如下参数配置多线程模型:
如:

 io-threads 4  // 这里说 有三个IO 线程,还有一个线程是main线程,main线程负责IO读写和命令执行操作

      默认情况下,如上配置,有三个IO线程, 这三个IO线程只会执行 IO中的write 操作,也就是说,read 和 命令执行 都由main线程执行。最后多线程将数据写回到客户端。

开启了如下参数:

io-threads-do-reads yes // 将支持IO线程执行 读写任务。

或者下图更详细:

6.3.1 Redis6.0为什么要引入多线程呢?

         Redis将所有数据放在内存中,内存的响应时长大约为100纳秒,对于小数据包,Redis服务器可以处理80,000到100,000 QPS,这也是Redis处理的极限了,对于80%的公司来说,单线程的Redis已经足够使用了。

         但随着越来越复杂的业务场景,有些公司动不动就上亿的交易量,因此需要更大的QPS。常见的解决方案是在分布式架构中对数据进行分区并采用多个服务器,但该方案有非常大的缺点,例如要管理的Redis服务器太多,维护代价大;某些适用于单个Redis服务器的命令不适用于数据分区;数据分区无法解决热点读/写问题;数据偏斜,重新分配和放大/缩小变得更加复杂等等。

        从Redis自身角度来说,因为读写网络的read/write系统调用占用了Redis执行期间大部分CPU时间,瓶颈主要在于网络的 IO 消耗, 优化主要有两个方向:

  • 提高网络 IO 性能,典型的实现比如使用 DPDK 来替代内核网络栈的方式

  • 使用多线程充分利用多核,典型的实现比如 Memcached。

       协议栈优化的这种方式跟 Redis 关系不大,支持多线程是一种最有效最便捷的操作方式。所以总结起来,redis支持多线程主要就是两个原因:

  • 可以充分利用服务器 CPU 资源,目前主线程只能利用一个核

  • 多线程任务可以分摊 Redis 同步 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个基本就没什么意义了。

      如果开启多线程,至少要4核的机器,且Redis实例已经占用相当大的CPU耗时的时候才建议采用,否则使用多线程没有意义。所以估计80%的公司开发人员看看就好。

6.3.2 **Redis6.0多线程的实现机制 **

流程简述如下:

1、主线程负责接收建立连接请求,获取 socket 放入全局等待读处理队列

2、主线程处理完读事件之后,通过 RR(Round Robin) 将这些连接分配给这些 IO 线程

3、主线程阻塞等待 IO 线程读取 socket 完毕

4、主线程通过单线程的方式执行请求命令,请求数据读取并解析完成,但并不执行

5、主线程阻塞等待 IO 线程将数据回写 socket 完毕

6、解除绑定,清空等待队列

该设计有如下特点:

1、IO 线程要么同时在读 socket,要么同时在写,不会同时读或写

2、IO 线程只负责读写 socket 解析命令,不负责命令处理

**开启多线程后,是否会存在线程并发安全问题 **

        从上面的实现机制可以看出,Redis的多线程部分只是用来处理网络数据的读写和协议解析,执行命令仍然是单线程顺序执行。所以我们不需要去考虑控制 key、lua、事务,LPUSH/LPOP 等等的并发及线程安全问题。

6.3.3 Redis6.0的多线程和Memcached多线程模型进行对比

      前些年memcached 是各大互联网公司常用的缓存方案,因此redis 和 memcached 的区别基本成了面试官缓存方面必问的面试题,最近几年memcached用的少了,基本都是 redis。不过随着Redis6.0加入了多线程特性,类似的问题可能还会出现,接下来我们只针对多线程模型来简单比较一下。

      如上图所示:Memcached 服务器采用 master-woker 模式进行工作,服务端采用 socket 与客户端通讯。主线程、工作线程 采用 pipe管道进行通讯。主线程采用 libevent 监听 listen、accept 的读事件,事件响应后将连接信息的数据结构封装起来,根据算法选择合适的工作线程,将连接任务携带连接信息分发出去,相应的线程利用连接描述符建立与客户端的socket连接 并进行后续的存取数据操作。

Redis6.0与Memcached多线程模型对比:

  • 相同点:都采用了 master线程-worker 线程的模型
  • 不同点:Memcached 执行主逻辑也是在 worker 线程里,模型更加简单,实现了真正的线程隔离,符合我们对线程隔离的常规理解而 Redis 把处理逻辑交还给 master 线程,虽然一定程度上增加了模型复杂度,但也解决了线程并发安全等问题


 ​更多关于redis 6.0多线程的讲解,请查看:https://www.cnblogs.com/madashu/p/12832766.html

6.3.4 Redis作者是如何点评 “多线程”这个新特性的?

    关于多线程这个特性,在6.0 RC1时,Antirez曾做过说明:

    Redis支持多线程有2种可行的方式:

  1. 第一种就是像“memcached”那样,一个Redis实例开启多个线程,从而提升GET/SET等简单命令中每秒可以执行的操作。这涉及到I/O、命令解析等多线程处理,因此,我们将其称之为“I/O threading”。
  2. 另一种就是允许在不同的线程中执行较耗时较慢的命令,以确保其它客户端不被阻塞,我们将这种线程模型称为“Slow commands threading”。

     经过深思熟虑,Redis不会采用“I/O threading”,redis在运行时主要受制于网络和内存,所以提升redis性能主要是通过在多个redis实例,特别是redis集群。接下来我们主要会考虑改进两个方面:

  • 1. Redis集群的多个实例通过编排能够合理地使用本地实例的磁盘,避免同时重写AOF。
  • 2.提供一个Redis集群代理,便于用户在没有较好的集群协议客户端时抽象出一个集群。

      补充说明一下,Redis和memcached一样是一个内存系统,但不同于Memcached。多线程是复杂的,必须考虑使用简单的数据模型,执行LPUSH的线程需要服务其他执行LPOP的线程。

      我真正期望的实际是“slow operations threading”,在redis6或redis7中,将提供“key-level locking”,使得线程可以完全获得对键的控制以处理缓慢的操作。

详见:http://antirez.com/news/126

6.3.5.Redis线程中经常提到IO多路复用,如何理解?

      这是IO模型的一种,即经典的Reactor设计模式,有时也称为异步阻塞IO。

      多路指的是多个socket连接,复用指的是复用一个线程

      多路复用主要有三种技术:select,poll,epoll。epoll是最新的也是目前最好的多路复用技术。采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络IO的时间消耗),且Redis在内存中操作数据的速度非常快(内存内的操作不会成为这里的性能瓶颈),主要以上两点造就了Redis具有很高的吞吐量。

6.4 SSL支持

​ 连接支持SSL协议,更加安全

6.5 ACL支持

       具体可参考:Redis 6.0 新特性详解-阿里云开发者社区

       当有了ACL之后,你就可以控制比如:这个连接只允许使用RPOP,LPUSH这些命令,其他命令都无法调用。

      Redis提供了一个新的命令ACL来维护Redis的访问控制信息,详情见:https://redis.io/topics/acl

  Redis 6开始支持ACL,该功能通过限制对命令和key的访问来提高安全性。ACL的工作方式是在连接之后,要求客户端进行身份验证(用户名和有效密码);如果身份验证阶段成功,则连接与指定用户关联,并且该用户具有限制。
  在默认配置中,Redis 6的工作方式与Redis的旧版本完全相同,每个新连接都能够调用每个可能的命令并访问每个键,因此ACL功能与旧版本向后兼容。客户和应用程序。依旧使用requirepass配置密码的,但现在只是为默认用户设置密码。

6.5.1 ACL使用

 1) ACL <subcommand> arg arg ... arg. Subcommands are:
 2) LOAD                             -- Reload users from the ACL file.
 3) SAVE                             -- Save the current config to the ACL file.
 4) LIST                             -- Show user details in config file format.
 5) USERS                            -- List all the registered usernames.
 6) SETUSER <username> [attribs ...] -- Create or modify a user.
 7) GETUSER <username>               -- Get the user details.
 8) DELUSER <username> [...]         -- Delete a list of users.
 9) CAT                              -- List available categories.
10) CAT <category>                   -- List commands inside category.
11) GENPASS [<bits>]                 -- Generate a secure user password.
12) WHOAMI                           -- Return the current connection username.
13) LOG [<count> | RESET]            -- Show the ACL log entries.

  Redis6中auth命令在Redis 6中进行了扩展,因此现在可以在两个参数的形式中使用它:

  #before Redis 6
  AUTH <password>
  #Redis 6
  AUTH <username> <password>

  默认情况下,有一个用户定义,称为default。可以使用ACL LIST命令来查看,默认配置的Redis实例的配置是:

#无密码
127.0.0.1:6379> ACL LIST
1) "user default on nopass ~* +@all"
#有密码
127.0.0.1:6379> ACL LIST
1) "user default on #ce306e0ee195cc817620c86d7b74126d0d66c077b66f66c10f1728cf34a214d3
127.0.0.1:6379> ACL WHOAMI
"default"
127.0.0.1:6379> ACL USERS
1) "default"

       "user default on nopass ~* +@all"

  每行的开头都是“ user”,后面跟用户名,on表示用户是启用的,否则是禁用的。nopass表示无密码,否则表示有密码认证。(~*)表示能够访问所有的key,+ @ all表示能够调用所有可能的命令。

6.5.2 ACL规则

启用和禁止用户

  • on:启用用户:可以以该用户身份进行认证。
  • off:禁用用户:不再可以与此用户进行身份验证,但是已经过身份验证的连接仍然可以使用。如果默认用户标记为off,则无论默认用户配置如何,新连接都将开始不进行身份验证,并且要求用户使用AUTH选项发送AUTH以进行身份验证。

允许和禁止命令

  • +<command>:将命令添加到用户可以调用的命令列表中。
  • -<command>:将命令从用户可以调用的命令列表中删除。
  • +@<category>:添加该类别中要由用户调用的所有命令,有效类别为@ admin,@ set,@ sortedset等,通过调用ACL CAT命令查看完整列表。特殊类别@all表示所有命令,包括当前在服务器中存在的命令,以及将来将通过模块加载的命令。
127.0.0.1:6379> ACL CAT
 1) "keyspace"
 2) "read"
 3) "write"
 4) "set"
 5) "sortedset"
 6) "list"
 7) "hash"
 8) "string"
 9) "bitmap"
10) "hyperloglog"
11) "geo"
12) "stream"
13) "pubsub"
14) "admin"
15) "fast"
16) "slow"
17) "blocking"
18) "dangerous"
19) "connection"
20) "transaction"
21) "scripting"
  • -@<category>:从客户端可以调用的命令列表中删除命令。
  • +<command>|subcommand:允许使用本来禁用的命令的特定子命令。该语法不允许使用-<command>|subcommand,例如-DEBUG|SEGFAULT,只能以“ +”开头的加法运算符。如果命令整体上已处于活动状态,则此ACL将导致错误。
  • allcommands+ @ all的别名。
  • nocommands-@ all的别名。

允许或禁止访问某些Key

  • ~:添加可以在命令中提及的键模式。例如~ allkeys 允许所有键。

    • resetkeys:使用当前模式覆盖所有允许的模式。如: ~foo: ~bar: resetkeys ~objects: ,客户端只能访问匹配 object: 模式的 KEY。

为用户配置有效密码

  • :将此密码添加到用户的有效密码列表中。例如,>mypass将“mypass”添加到有效密码列表中。该命令会清除用户的nopass标记。每个用户可以有任意数量的有效密码。
  • <:从有效密码列表中删除此密码。若该用户的有效密码列表中没有此密码则会返回错误信息。
  • :将此SHA-256哈希值添加到用户的有效密码列表中。该哈希值将与为ACL用户输入的密码的哈希值进行比较。允许用户将哈希存储在users.acl文件中,而不是存储明文密码。仅接受SHA-256哈希值,因为密码哈希必须为64个字符且小写的十六进制字符。
  • !:从有效密码列表中删除该哈希值。当不知道哈希值对应的明文是什么时很有用。
  • nopass:移除该用户已设置的所有密码,并将该用户标记为nopass无密码状态:任何密码都可以登录。resetpass命令可以清除nopass这种状态。
  • resetpass:情况该用户的所有密码列表。而且移除nopass状态。resetpass之后用户没有关联的密码同时也无法使用无密码登录,因此resetpass之后必须添加密码或改为nopass状态才能正常登录。
  • reset:重置用户状态为初始状态。执行以下操作resetpass,resetkeys,off,-@all。

6.5.3 创建和编辑用户

127.0.0.1:6379> ACL SETUSER zijie
OK
`SETUSER`命令采用用户名和ACL规则列表以应用于用户。但是在上面的示例中,没有指定任何规则。如果用户不存在,这将使用默认属性来创建用户。如果用户已经存在,则上面的命令将不执行任何操作。
默认的用户状态:
127.0.0.1:6379> acl list
1) "user default on #ce306e0ee195cc817620c86d7b74126d0d66c077b66f66c10f1728cf34a214d3 ~* +@all"
2) "user zijie off -@all"   

刚创建的用户zijie是:

  • 处于禁用状态,AUTH不起作用。
  • 无法访问任何命令。
  • 无法访问任何key。
  • 没有设置密码。

    127.0.0.1:6379> ACL SETUSER zijie2 on >123 ~* +get
    OK
    127.0.0.1:6379> acl list
    1) "user default on #ce306e0ee195cc817620c86d7b74126d0d66c077b66f66c10f1728cf34a214d3 ~* +@all"
    2) "user zijie off -@all"
    3) "user zijie2 on #a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3 ~cached:* -@all +get"
    127.0.0.1:6379> ACL SAVE
    OK

  用户可以执行某些操作,但是会拒绝执行其他操作,权限动态生效:

127.0.0.1:6379> auth zijie2 123
OK
127.0.0.1:6379> get name
"zijie"
127.0.0.1:6379> set name:2 a
(error) NOPERM this user has no permissions to run the 'set' command or its subcommand
#ACL LIST 查看用户配置
127.0.0.1:6379> ACL LIST
1) "user default on #ce306e0ee195cc817620c86d7b74126d0d66c077b66f66c10f1728cf34a214d3 ~* +@all"
2) "user zijie off -@all"
3) "user zijie2 on #a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3 ~* -@all +get"
#用户名区分大小写,ACL GETUSER 易读性高于ACL LIST
127.0.0.1:6379> ACL GETUSER zijie2
1) "flags"
2) 1) "on"
2) "allkeys"
3) "passwords"
4) 1) "a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3"
5) "commands"
6) "-@all +get"
7) "keys"
8) 1) "*"
#使用RESP3易读性更高
127.0.0.1:6379> ACL GETUSER zijie2
1# "flags" => 1~ "on"
2~ "allkeys"
2# "passwords" => 1) "a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3"
3# "commands" => "-@all +get"
4# "keys" => 1) "*"

  使用另一个ACL SETUSER命令向用户添加多个模式:

127.0.0.1:6379> ACL SETUSER zijie3 on >123 ~zijie* +get
OK
127.0.0.1:6379> ACL SETUSER zijie3 on >123 ~zijie* +set
OK
127.0.0.1:6379> ACL SETUSER zijie3 on >123 ~zijie* +@read
OK
127.0.0.1:6379> ACL LIST
1) "user default on #ce306e0ee195cc817620c86d7b74126d0d66c077b66f66c10f1728cf34a214d3 ~* +@all"
2) "user zijie off -@all"
3) "user zijie2 on #a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3 ~* -@all +get"
4) "user zijie3 on #a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3 ~zijie* ~name* ~xx* -@all +@read +@hash +@bitmap +@geo -bitfield -hmset +set -geoadd -hincrby -hset -hincrbyfloat -georadiusbymember -hdel -hsetnx -bitop -georadius -setbit"
#删除用户
ACL DELUSER zijie3

6.5.4 命令类别

  ACL有类似"资源组“的概念,给一类命令一个别名,来达到快速授权。

127.0.0.1:6379> acl cat
 1) "keyspace"
 2) "read"
 3) "write"
 4) "set"
 5) "sortedset"
 6) "list"
 7) "hash"
 8) "string"
 9) "bitmap"
10) "hyperloglog"
11) "geo"
12) "stream"
13) "pubsub"
14) "admin"
15) "fast"
16) "slow"
17) "blocking"
18) "dangerous"
19) "connection"
20) "transaction"
21) "scripting"
127.0.0.1:6379> acl cat dangerous
 1) "flushdb"
 2) "sort"
 3) "swapdb"
 4) "replicaof"
 5) "shutdown"
 6) "replconf"
 7) "keys"
 8) "lastsave"
 9) "psync"
10) "cluster"
11) "module"
12) "acl"
13) "bgrewriteaof"
14) "info"
15) "debug"
16) "bgsave"
17) "sync"
18) "flushall"
19) "save"
20) "pfdebug"
21) "latency"
22) "role"
23) "slaveof"
24) "migrate"
25) "pfselftest"
26) "config"
27) "monitor"
28) "restore"
29) "slowlog"
30) "restore-asking"
31) "client"
127.0.0.1:6379> ACL SETUSER zijie4 on +@all -@dangerous >123 ~*
OK
127.0.0.1:6379> ACL GETUSER zijie4
1) "flags"
2) 1) "on"
2) "allkeys"
3) "passwords"
4) 1) "a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3"
5) "commands"
6) "+@all -@admin -@dangerous"
7) "keys"
8) 1) "*"

授权子命令

ACL SETUSER zijie5 -client +client|setname +client|getname 

6.5.5 使用外部ACL文件

  Redis6因为引入了权限机制,会有不同分工的用户;所以又引入了额外的配置项以及配置文件,通过ACL LOAD/ACL SAVE报错和加载acl文件。

[root@zijie ~]# cat /etc/Redis6.conf  | grep acl
aclfile /usr/local/Redis/users.acl
acllog-max-len 128
#Redis6开始密码加密存储
[root@zijie ~]# cat /usr/local/Redis/users.acl
user default on #ce306e0ee195cc817620c86d7b74126d0d66c077b66f66c10f1728cf34a214d3 ~* +@all
user zijie off -@all
user zijie2 on #a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3 ~* -@all +get
user zijie3 off ~zijie* ~name* -@all
#获取安全密码
127.0.0.1:6379> ACL GENPASS
"c550b646ef8f7f91908628db0d983b4ca061fcf36433baa770ea9ce09da64ef4"

6.5.6 哨兵和副本的ACL规则

  如果不想为Redis副本和Redis Sentinel实例提供对Redis实例的完全访问权限,则以下是一组命令,为了使一切正常工作,必须允许这些命令。
  对于Sentinel,允许用户在主实例和副本实例中访问以下命令:
  Sentinel不需要访问数据库中的任何密钥,因此ACL规则如下(注意:不需要AUTH,因为始终允许使用AUTH):

ACL setuser sentinel-user >somepassword +client +subscribe +publish +ping +info +multi +slaveof +config +client +exec on

  Redis副本需要在主实例上将以下命令列入白名单:

  • PSYNC,REPLCONF,PING

  不需要访问任何密钥,因此这转化为以下规则:

ACL setuser replica-user >somepassword +psync +replconf +ping on

  无需将副本配置为允许主服务器能够执行任何命令集:从副本的角度来看,主服务器始终被认证为root用户。

6.5.7 ACL LOG

  记录拒绝的命令,密钥访问和身份验证。

127.0.0.1:6379> acl log
 1)  1) "count"
     2) (integer) 1
     3) "reason"
     4) "auth"
     5) "context"
     6) "toplevel"
     7) "object"
     8) "auth"
     9) "username"
    10) "default"
    11) "age-seconds"
    12) "7.3860000000000001"
    13) "client-info"
    14) "id=18 addr=127.0.0.1:51882 fd=8 name= age=2 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=32742 obl=0 oll=0 omem=0 events=r cmd=auth user=default"

6.7  RESP3

       RESP(Redis Serialization Protocol)是 Redis 服务端与客户端之间通信的协议。
  RESP3 是 RESP version 2 的更新版本。RESP v2 大致从 Redis 2.0 开始支持(其实 1.2 就支持了,只不过 Redis 2.0 是第一个仅支持此协议的版本)。

       RESP(Redis Serialization Protocol)是 Redis 服务端与客户端之间通信的协议。Redis 6之前使用的是 RESP2,而Redis 6开始在兼容RESP2的基础上,开始支持RESP3。

     在Redis 6中我们可以使用HELLO命令在RESP2和RESP3协议之间进行切换

#使用RESP2协议
HELLO 2
#使用RESP3协议
HELLO 3
推出RESP3的目的:
  1. 一是因为希望能为客户端提供更多的语义化响应(semantical replies),降低客户端的复杂性,以开发使用旧协议难以实现的功能;
  2. 另一个原因是为了实现 Client side caching(客户端缓存)功能。

详细见:https://github.com/antirez/RESP3/blob/master/spec.md

例子1:

127.0.0.1:6380> HSET myhash a 1 b 2 c 3
(integer) 3
127.0.0.1:6380> HGETALL myhash
1) "a"
2) "1"
3) "b"
4) "2"
5) "c"
6) "3"
127.0.0.1:6380> HELLO 3
1# "server" => "redis"
2# "version" => "6.0"
3# "proto" => (integer) 3
4# "id" => (integer) 5
5# "mode" => "standalone"
6# "role" => "master"
7# "modules" => (empty array)
127.0.0.1:6380> HGETALL myhash
1# "a" => "1"
2# "b" => "2"
3# "c" => "3"

例子2:

   127.0.0.1:6379> hello 2
   1) "server"
   2) "Redis"
   3) "version"
   4) "6.0.5"
   5) "proto"
   6) (integer) 2
   7) "id"
   8) (integer) 7
   9) "mode"
    10) "standalone"
    11) "role"
    12) "master"
    13) "modules"
    14) (empty array)
    127.0.0.1:6379> hello 3
    1# "server" => "Redis" // 服务名称
    2# "version" => "6.0.5" // 版本号
    3# "proto" => (integer) 3 // 支持的最高协议
    4# "id" => (integer) 7 // 客户端连接 ID
    5# "mode" => "standalone" //  模式:"standalone", "sentinel", "cluster"
    6# "role" => "master" //  "master" 或 "replica"
    7# "modules" => (empty array) // 加载的模块列表

6.8 客户端缓存

      ​ 基于 RESP3 协议实现的客户端缓存功能。为了进一步提升缓存的性能,将客户端经常访问的数据cache到客户端。减少TCP网络交互。不过该特性目前合并到了unstable 分支,作者说等6.0 GA版本之前,还要修改很多。

       客户端缓存重新设计中引入了广播模式(broadcasting mode)。在使用广播模式时,服务器不再尝试记住每个客户端请求的 key。 取而代之的是,客户订阅 key 的前缀:每次修改匹配前缀的 key 时,这些订阅的客户端都会收到通知。这意味着会产生更多的消息(仅适用于匹配的前缀),但服务器端无需进行任何内存操作。

​       客户端缓存的功能是该版本的全新特性,服务端能够支持让客户端缓存values,Redis作为一个本身作为一个缓存数据库,自身的性能是非常出色的,但是如果可以在Redis客户端再增加一层缓存结果,那么性能会更加的出色。Redis实现的是一个服务端协助的客户端缓存,叫做tracking。客户端缓存的命令是:

CLIENT TRACKING ON|OFF [REDIRECT client-id] [PREFIX prefix] [BCAST] [OPTIN] [OPTOUT] [NOLOOP]

​       当tracking开启时, Redis会"记住"每个客户端请求的key,当key的值发现变化时会发送失效信息给客户端。失效信息可以通过 RESP3 协议发送给请求的客户端,或者转发给一个不同的连接(支持RESP2+ Pub/Sub)。

更多信息见:http://antirez.com/news/130

客户端缓存:

      redis 6 提供了服务端追踪key的变化,客户端缓存数据的特性,这需要客户端实现

执行流程为:

  1. 当客户端访问某个key时,服务端将记录key 和 client ;
  2. 客户端拿到数据后,进行客户端缓存;
  3. 这时,当key再次被访问时,key将被直接返回,避免了与redis 服务器的再次交互,节省服务端资源;
  4. 当数据被其他请求修改时,服务端将主动通知客户端失效的key;
  5. 客户端进行本地失效,下次请求时,重新获取最新数据。

目前只有lettuce对其进行了支持:

<dependency>
   <groupId>io.lettuce</groupId>
   <artifactId>lettuce-core</artifactId>
   <version>6.0.0.RELEASE</version>
</dependency>

public static void main(String[] args) throws InterruptedException {
    RedisClient redisClient = RedisClient.create("redis://192.168.109.200");

    Map<String, String> clientCache = new ConcurrentHashMap<>();

    StatefulRedisConnection<String, String> myself = redisClient.connect();

    CacheFrontend<String, String> frontend =
            ClientSideCaching.enable(CacheAccessor.forMap(clientCache),
            myself,
            TrackingArgs.Builder.enabled().noloop());

    String key="csk";
    int count = 0;
    while (true){

        System.out.println(frontend.get(key));
        TimeUnit.SECONDS.sleep(3);
        if (count++ == Integer.MAX_VALUE){
            myself.close();
            redisClient.shutdown();
        }
    }
}

注:https://redis.io/topics/client-side-caching

6.9 集群代理

6.1 说明

     本文来介绍下如何使用官方自带的proxy:redis-cluster-proxy ​

    GitHub - RedisLabs/redis-cluster-proxy: A proxy for Redis clusters.

但是项目没有更新,还是alpha阶段,不能适用于生产环境:

      因为 Redis Cluster 内部使用的是P2P中的Gossip协议,每个节点既可以从其他节点得到服务,也可以向其他节点提供服务,没有中心的概念,通过一个节点可以获取到整个集群的所有信息。所以如果应用连接Redis Cluster可以配置一个节点地址,也可以配置多个节点地址。       但需要注意如果集群进行了上下节点的的操作,其应用也需要进行修改,这样会导致需要重启应用,非常的不友好。从Redis 6.0开始支持了Prxoy,可以直接用Proxy来管理各个集群节点。

       通过使用 redis-cluster-proxy 可以与组成Redis集群的一组实例进行通讯,就像是单个实例一样。Redis群集代理是多线程的,使用多路复用通信模型,因此每个线程都有自己的与群集的连接,该连接由属于该线程本身的所有客户端共享。

      在某些特殊情况下(例如MULTI事务或阻塞命令),多路复用将被禁用;并且客户端将拥有自己的集群连接。这样客户端仅发送诸如GET和SET之类的简单命令就不需要Redis集群的专有连接。

redis-cluster-proxy的主要功能特点:

  • 路由:每个查询都会自动路由到集群的正确节点
  • 多线程
  • 支持多路复用和专用连接模型
  • 在多路复用上下文中,可以确保查询执行和答复顺序
  • 发生ASK | MOVED错误后自动更新集群的配置:当答复中发生此类错误时,代理通过获取集群的更新配置并重新映射所有插槽来自动更新集群。 更新完成后所有查询将重新执行,因此,从客户端的角度来看,一切正常进行(客户端将不会收到ASK | MOVED错误:他们将在收到请求后直接收到预期的回复) 群集配置已更新)。
  • 跨槽/跨节点查询:支持许多命令,这些命令涉及属于不同插槽(甚至不同集群节点)的多个键。这些命令会将查询分为多个查询,这些查询将被路由到不同的插槽/节点。 这些命令的回复处理是特定于命令的。 某些命令(例如MGET)将合并所有答复,就好像它们是单个答复一样。 其他命令(例如MSET或DEL)将汇总所有答复的结果。 由于这些查询实际上破坏了命令的原子性,因此它们的用法是可选的(默认情况下禁用)。
  • 一些没有特定节点/插槽的命令(例如DBSIZE)将传递到所有节点,并且将对映射的回复进行映射缩减,以便得出所有回复中包含的所有值的总和。
  • 可用于执行某些特定于代理的操作的附加PROXY命令

6.2 为什么要使用反向代理?

如果没有反向代理,一台Redis可能需要跟很多个客户端连接:

        看着是不是很慌?看没关系,主要是连接需要消耗线程资源,没有代理的话,Redis要将很大一部分的资源用在与客户端建立连接上,redis的高可用和可扩展无论是自带的Redis Sentinel还是Redis Cluster都要求客户端进行额外的支持,而目前基本上没有合适的客户端能够做这些事情,客户端来做这些事情也并不合适,它会让维护变得特别困难。

        因此在客户端和redis服务端之间加一层代理成了一种理想的方案,代理屏蔽后端Redis实现细节向客户端提供redis服务,可以完美的解决Redis的高可用和扩展性问题,同时代理的引入也使得Redis维护变得更加简单。

客户端分区方案 

        客户端 就已经决定数据会被 存储 到哪个 redis 节点或者从哪个 redis 节点 读取数据。其主要思想是采用 哈希算法 将 Redis 数据的 key 进行散列,通过 hash 函数,特定的 key会 映射到特定的 Redis 节点上。

       客户端分区方案 的代表为 Redis Sharding,Redis Sharding 是 Redis Cluster 出来之前,业界普遍使用的 Redis 多实例集群 方法。Java 的 Redis 客户端驱动库 Jedis,支持 Redis Sharding 功能,即 ShardedJedis 以及 结合缓存池 的 ShardedJedisPool。 

优点  

        不使用 第三方中间件,分区逻辑 可控,配置 简单,节点之间无关联,容易 线性扩展,灵活性强。 

缺点  

        客户端无法动态增删 服务节点,客户端需要自行维护 分发逻辑,客户端之间 无连接共享,会造成 连接浪费。 

集群刷新:

  新版客户端比如lettuce已经支持集群刷新,解决了部分redis节点宕机集群节点变化的问题:

  • # 开启cluster自适应刷新 周期600秒
  • spring.redis.lettuce.cluster.refresh.adaptive=true spring.redis.lettuce.cluster.refresh.period=600000

于是乎,有了代理:

6.3 如何使用代理?

很简单,将请求连接到调度代理器上,由Proxy负责将请求转发到后面的Redis服务实例,图示:

又有了新的问题,Proxy挂了可咋整?

       所以Proxy又需要做集群,甚至前面可以加一层负载均衡,负载均衡嘛,单机也存在单点故障等问题,一个Director肯定不行,搞不好又挂了,所以整一个主备,备机通过KeepAlived来检测主LVS健康状况,出了问题顶上去。

6.4 Redis代理插件

Redis代理插件有很多,这儿简单介绍几款,但是大部分已经停止维护,原因不明。

官网:

     GitHub - RedisLabs/redis-cluster-proxy: A proxy for Redis clusters.停止维护

但是项目没有更新,还是alpha阶段,不能适用于生产环境:

原因:

只要获取信息的redis-cluster节点宕机,proxy就无法使用,目前这个问题不解决,还是别在生产环境用; 

其他redis集群:

predixy高性能全特征redis代理,支持Redis Sentinel和Redis Cluster,停止维护
twemproxy快速、轻量级memcached和redis代理,维护中
codisredis集群代理解决方案停止维护
redis-cerberusRedis Cluster代理停止维护

看情况,目前只有twemproxy在维护,大家如果要是用就选择它吧。

codis参考:Codis作者黄东旭细说分布式Redis架构设计和踩过的那些坑们 - 墨天轮

6.5 代理详细功能对比

特性predixytwemproxycodisredis-cerberus
高可用Redis Sentinel或Redis Cluster一致性哈希Redis SentinelRedis Cluster
可扩展Key哈希分布或Redis ClusterKey哈希分布Key哈希分布Redis Cluster
开发语言C++CGOC++
多线程
事务Redis Sentinel模式单Redis组下支持不支持不支持不支持
BLPOP/BRPOP/BLPOPRPUSH支持不支持不支持支持
Pub/Sub支持不支持不支持支持
Script支持load不支持不支持不支持
Scan支持不支持不支持不支持
Select DB支持不支持支持Redis Cluster只有一个DB
Auth支持定义多个密码,给予不同读写及管理权限和Key访问空间不支持同redis不支持
读从节点支持,可定义丰富规则读指定的从节点不支持支持,简单规则支持,简单规则
多机房支持支持,可定义丰富规则调度流量不支持有限支持有限支持
统计信息丰富丰富丰富简单

简单来说,predixy既支持Redis Sentinel也支持Redis Cluster

  • 后端为Redis Sentinel监控的一组Redis,功能完全等同于原始Redis
  • 后端为Redis Sentinel监控的多组Redis,则有部分功能受限
  • 后端为Redis Cluster,功能完全等同于Redis Cluster

参考:redis 6.0 redis-cluster-proxy集群代理尝试_mb607022e25a607的技术博客_51CTO博客

6.10 Disque module

       这个本来是作者几年前开发的一个基于 Redis 的消息队列工具,但多年来作者发现 Redis 在持续开发时,他也要持续把新的功能合并到这个Disque 项目里面,这里有大量无用的工作。因此这次他在 Redis 的基础上通过 Modules 功能实现 Disque。

      如果业务并不需要保持严格消息的顺序,这个 Disque 能提供足够简单和快速的消息队列功能。

6.11 其余特性

6.11.1 无盘复制&PSYNC2

  现在,Redis用于复制的 RDB 文件如果不再有用,将立即被删除。不过,在某些环境中,最好不要将数据放在磁盘上,而只放在内存中。

repl-diskless-sync no
repl-diskless-sync-delay 5
repl-diskless-load disabled

  复制协议 PSYNC2 现在得到了改进。Redis 将能够更频繁地部分重新同步,因为它能够修整协议中的最终 PING,从而使副本和主副本更有可能找到一个公共的偏移量。

6.11.2 Redis-benchmark支持集群

  从Redis6开始Redis-benchmark支持集群。

6.11.3 Redis-cli 优化、重写 Systemd 支持

6.11.4 Redis 集群代理与 Redis 6 一同发布(但在不同的 repo)

  在 Redis 集群中,客户端会非常分散,Redis6为此引入了一个集群代理,可以为客户端抽象 Redis 群集,使其像正在与单个实例进行对话一样。同时在简单且客户端仅使用简单命令和功能时执行多路复用。

6.11.5 RDB更快加载

  Redis 6.0,RDB 文件的加载速度比之前变得更快了。根据文件的实际组成(较大或较小的值),大概可以获得 20-30% 的改进。除此之外,INFO 也变得更快了,当有许多客户端连接时,这会消耗很多时间,不过现在终于消失了。

6.11.6 SRANDMEMBER和类似的命令具有更好的分布

6.11.7 STRALGO 命令

  STRALGO 实现了复杂的字符串算法。 目前唯一实现的是 LCS(最长的公共子序列)。

127.0.0.1:6379> STRALGO LCS keys name zijie
"1"
127.0.0.1:6379> STRALGO LCS keys name zijie  len
(integer) 1

6.11.8 带有超时的 Redis 命令更易用

  除了 BLPOP 命令,其他用于接受秒的命令现在都接受十进制数字,而且实际分辨率也得到了改进,以使其永远不会比当前的“HZ”值更差,因为其不管连接客户端的数量。

Redis 7

       近日,Redis 开源社区发布了7.0的两个预览版。在这两个预览版中,有很多Redis 7.0中新增加的特性,新增加的命令或已有命令的新加参数,一些性能上的优化和提高,还有一些API的改变,并且修复了以前版本中的一些bug,下面让我们具体来看一下这些方面的内容。

Redis 7.0 包括了以下一些重要的变化:

  1. 将AOF文件的存储方式改为在一个文件夹下存储多个文件。
  2. 将持久化文件RDB的版本升级为10,与之前的RDB文件版本不再兼容。
  3. 在读取老的RDB文件格式的时候将ziplist转换为listpack,这种转换发生于两种情况之下:从磁盘读取文件或者从一个主节点进行复制文件的时候。
  4. 在redis.conf配置文件中,protected-mode 默认更改为yes,只有当你希望你的客户端在没有授权的情况下可以连接到Redis server的时候可以将protected-mode设置为no。
  5. 在ACL中,pub/sub channel默认是被阻塞的。
  6. 在从节点中,TTL的时间标识的是绝对时间,不再是相对时间,从而保证了过期数据被及时删除。
  7. 不再支持 gopher协议。
  8. 当在配置文件中设置replica-serve-stale-data=no, 当主节点不再提供服务时,PING命令得不到返回值。

Redis 7.0 新特性其中的几个例子:

  1. RedisFunctions:一种新的方式用于Redis server端脚本,它不同于以前版本支持的Lua脚本, Redis Functions可支持持久化,可复制,并且在节点重启之后可以直接从server端读取。 我们会在后续的博客中详细介绍Redis Functions的使用。 如果现在大家就想知道更多详情,可以参考链接https://redis.io/topics/funct...
  2. 集群支持显示主机名,而不仅仅显示ip地址。
  3. 使用多个AOF文件降低了AOF重写期间的内存使用。
  4. 在Lua脚本中支持了Function的标志。
  5. 在AOF文件中增加了数据更新时间点的标识,使得用户可以恢复某一时间点的数据。
  6. Lua脚本支持RESP3 版本的并且可以通过redis.REDIS_VERSION, redis.REDIS_VERSION_NUM得到Redis的版本。
  7. 增加了对stream consumer组滞后的追踪和报告 。
  8. 增加了API以便于可以在functions和Lua脚本中明确地查看ACL。

Redis 7.0 新增14个用户端命令和 15个已有命令的相关参数选项,其中包括:ZMPOP, BZMPOP,LMPOP, BLMPOP等新命令,对于EXPIRE和SET命令,新增了更多的命令参数选项。

例如,ZMPOP的格式如下: ZMPOP numkeys key [key ...] MIN|MAX [COUNT count],而BZMPOP是ZMPOP的阻塞版本。

下面是一个使用ZMPOP的例子:

redis> ZMPOP 1 notsuchkey MIN
(nil)
redis> ZADD myzset 1 "one" 2 "two" 3 "three"
(integer) 3
redis> ZMPOP 1 myzset MIN
1) "myzset"
2) 1) 1) "one"
      2) "1"
redis> ZRANGE myzset 0 -1 WITHSCORES
1) "two"
2) "2"
3) "three"
4) "3"
redis> ZMPOP 1 myzset MAX 
1) "myzset"
2) 1) 1) "three"
      2) "3"
redis> ZRANGE myzset 0 -1 WITHSCORES
1) "two"
2) "2"

Redis 7.0 新增10个管理和监控相关的命令及其相关参数选项,其中包括:COMMAND LIST,COMMAND INFO,CLUSTER DELSLOTSRANGE and CLUSTER ADDSLOTSRANGE等。

Redis 7.0 新增配置选项:

  1. maxmemory-clients 可以限定所有客户端使用的内存总和的最大值。
  2. cluster-port用户可以自定义集群的绑定端口。
  3. 对于Config Set 和Get命令,支持在一次调用过程中传递多个配置参数。例如,现在我们可以在执行一次Config Set命令中更改多个参数: config set maxmemory 10000001 maxmemory-clients 50% port 26381。
  4. 还有shutdown-timeout,latency-tracking,cluster-link-sendbuf-limit等相关配置选项。

Redis 7.0 在以下方面有了明显的性能提升:

  1. 数据类型Hash,List, Zset的底层数据结构用listpack替换了ziplist
  2. 列表(List)的数据类型可以存储超过4GB的单个元素
  3. 降低了copy-on-write期间的内存使用
  4. 在使用大量散列(Hash)或者有序集合(Zset)时节省了大量的内存
  5. 在集群模式下,节省了大量的内存并且降低了系统整体的延迟时间
  6. 在集群中,当一个主节点重启之后,从节点不再需要做完全同步,只需要做部分同步即可
  7. 当Redis启动时,总是建立一个AOF文件用于持久化
  8. 降低了长期没有响应客户(idle, stale client)的内存使用
  9. 降低了在客户回复数据包中的对于写的系统调用次数,也同时降低了TCP packet的数目

Radis 7.0 还对模块(Module)的API进行了部分修改如下:

  1. 新增了对RESP3类型的应答的API支持
  2. 新增了在RM_Call对RESP3回复消息的解析的API支持
  3. 增加了对ACL进行验证的支持
  4. 还增加API:RM_CreateSubcommand,RM_KeyExists,RM_TrimStringAllocation, RM_SetCommandInfo等

当然,Redis 7.0 前两个预览版中不止以上提到的内容,还有更多的新特性和新功能我们会在后续的博客中继续为大家介绍。

其他

  • 新的Expire算法:用于定期删除过期key的函数activeExpireCycle被重写,以便更快地收回已经过期的key;
  • 提供了许多新的Module API;
  • 从服务器也支持无盘复制:在用户可以配置的特定条件下,从服务器现在可以在第一次同步时直接从套接字加载RDB到内存;
  • SRANDMEMBER命令和类似的命令优化,使其结果具有更好的分布;
  • 重写了Systemd支持;
  • 官方redis-benchmark工具支持cluster模式;
  • 提升了RDB日志加载速度;

文章来源

Redis 6.0 新特性详解-阿里云开发者社区

Redis 6.0 新特性-多线程连环13问! - 简书

为什么Redis集群要使用反向代理? - 等不到的口琴 - 博客园

https://blog.csdn.net/sinat_14840559/article/details/108326178

Redis进阶 - 版本特性: Redis4.0、5.0、6.0特性整理 | Java 全栈知识体系

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐