Redis 6.0 除了多线程,这个功能也贼牛逼!

码农沉思录

共 6793字,需浏览 14分钟

 ·

2020-07-28 15:40

Redis 6.0 的新特性也是在一步步的讨论和优化中确定的。

很多的特性已经在之前的 RC 等版本中介绍过了。

但是正式 GA 版中也有一些新的变化:

  • SSL 

  • ACL 更好,命令支持

  • RESP3 

  • Client side caching [5]:重新设计

  • Threaded I/O

  • Diskless replication on replicas 

  • Cluster support in Redis-benchmark and improved redis-cli cluster support

  • Disque in beta as a module of Redis  开始侵入消息队列领域

  • Redis Cluster Proxy 

  • 支持 RDB 不再使用时可立即删除,针对不落盘的场景

  • PSYNC2: 优化的复制协议

  • 超时设置支持更友好

  • 更快的 RDB 加载,20% ~ 30%的提升

  • STRALGO,新的字符串命令,目前只有一个实现 LCS (longest common subsequence)

@antirez 提到只是 Redis 历史上最大的一次版本更新,所以谨慎建议在应用的产品中还是多多测试评估,并且承诺一旦遇到大的 bug 就会紧急发布 6.0.1 版。果不其然,一天后就发布了 6.0.1 版,修复了一个 allocator 的 bug,这个 bug 是为了优化而引入的,现在暂时去掉了。

I just released Redis 6.0.1. Unfortunately there was a bug in Redis 6.0.0 introduced just a few days before the release, that only happens when using the non-default allocator (libc malloc in this case triggers it). Optimization reverted, 6.0.1 released. Sorry for the issue.

本文主要关注Client side caching(客户端缓存)这一特性。

smallnest/RESP3[9] 是 Redis RESP3 协议的解析库,你可以使用它和 Redis 底层通讯,或者包装它实现新版的 Redis client 库或者 Redis Server 端。

一年前,当 @antirez 参加完纽约 Redis 大会后,5:30 就在旅店中醒来了,面对曼哈顿街头的美丽景色,在芸芸众生中思索 Redis 的未来。,包括客户端缓存。

其实,客户端缓存特性是收到 Redis Conf 2018 的 Ben Malec 的影响,一下子打开了 @antirez 思路。我们知道, 很多公司使用 Redis 做缓存系统,并且很好的提高了数据访问的性能,但是很多企业为了进一步应对热点数据,还是会在 redis 的 client 端缓存一部分热点数据,用来应对吃瓜事件。比如在微博我们经常遇到的是明星出轨、明星分分合合、突发事件等等,每年都会有几次突发的事件,微博除了使用 Redis 做缓存避免直接访问数据库,还会在前面加更多的 cache 层,比如 L1 cache等,采用 memcached 等产品作为热数据的缓存。那么就有一个问题,如何能够及时的同步这些 cache 和 redis 的数据呢?Ben 提供了非常有意思的想法。

伫立在曼哈顿的街头,@antirez 陷入了沉思,后来回到旅馆他开始实现初版的客户端的缓存。当然,最终 Redis 6.0 中实现和这个初版的实现差别很大,但是还显然,从客户端的演化过程中我们还是能看到@antirez 对这个特性所在的权衡(tradeoff)。关于这个历史本文不做太多的介绍,因为我们更关注于这个特性最终是什么样子的。

Redis 实现的是一个服务端协助的客户端缓存,叫做 tracking。客户端缓存的命令是:

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

tracking开启时, Redis 会"记住"每个客户端请求的 key,当 key 的值发现变化时会发送失效信息给客户端(invalidation message)。失效信息可以通过 RESP3 协议发送给请求的客户端,或者转发给一个不同的连接(支持 RESP2+ Pub/Sub)。当广播模式(broadcasting)开启时,参与 tracking的客户端会收到它通过前缀订阅的 key 的相关的通知,即使它没请求过对应的 key。同时还提供了 OPTINOPTOUT等模式。

失效消息:当一个 key 的数据有修改的时候,需要告诉客户端它以前缓存的数据失效了,这时 redis 会主动发送一条失效消息

REDIRECT : 将失效消息转发给另外一个客户端。当我们不使用 RESP3 而是使用老的 RESP2 和 Redis 通讯时,client 本身不支持处理失效消息,所以可以开启一个支持 Pub/Sub 客户端处理失效消息。当然如果客户端支持 RESP3 也可以将失效消息转发给另外一个客户端。这个 cace 我们放在最后演示。

BCAST: 使用广播模式开始 tracking。在这种模式下客户端需要设置将 track 的 key 的前缀,这些 key 的失效消息会广播给所有参与的客户端,不管这些客户端是否请求/缓存额这些 key。不开始广播模式时,Redis 只会 track 那些只读命令请求的 key,并且只会报告一次失效消息。

PREFIX : 只应用了广播模式,注册一个 key 的前缀。所有以这个前缀开始的 key 有修改时,都会发送失效消息。可以注册多个前缀。如果不设置前缀,那么广播模式会 track 每一个 key。

OPTIN: 当广播模式没有激活时,正常 不会track 只读命令的 key,除非它们在 CLIENT CACHING yes之后被调用。

OPTOUT: 当广播模式没有激活时,正常 track 只读命令的 key,除非它们在 CLIENT CACHING off之后被调用。

NOLOOP: 不发送 client 自己修改的 key。

下面让我们一一介绍每个选项。

测试环境搭建

首先让我们介绍 RESP3 协议相关的选项, REDIRECT放在最后介绍。

在尝试之前,你首先需要安装一个 redis 6.x 的版本,目前时 6.0.1。在官方网站上有源代码的下载,编译安装也很简单:

  1. make distclean

  2. make

  3. make test

  4. sudo make install

相信很快就有编译好的二进制包可以下载。

启动 server, 它会在 6379 端口启动一个服务:

  1. redis-server

使用 redis-cli访问,默认访问本机的 6379 实例:

  1. redis-cli

当然你可以通过 -h查看额外的参数配置,比如使用其它端口等等,这里我们使用最简单的例子,重点是了解客户端缓存的特性。

有时候为了更好的观察 redis 的返回结果,我们使用 telnet而不是 redis-cli作为 client 连接 redis,因为 redis-cli 对结果做了处理,尤其是失效消息,你可能无法观测到。

BCAST 广播模式 ( client tracking on)

启动 redis server:

启动 redis-cli:

当然,我们使用 telnet 来测试,方便观察 redis 的返回结果,刚才 redis-cli 用来更新 key 值,辅助测试。连接上之后发送 hello3开启 RESP3 协议:

  1. telnet client

  2.  ~ telnet localhost 6379

  3. Trying ::1...

  4. Connected to localhost.

  5. Escape character is '^]'.

  6. hello 3

  7. %7

  8. $6

  9. server

  10. $5

  11. redis

  12. $7

  13. version

  14. $5

  15. 6.0.1

  16. ......

之后尝试开启 tracking并读取 a的值:

  1. telnet client

  2. client tracking on

  3. +OK

  4. set a 1

  5. +OK

  6. get a

  7. $1

  8. 1

这个时候如果使用 redis-cli 作为另外一个 client 更新 a的值,telnet 这个 client 应该能获得通知:

  1. redis-cli client

  2. 127.0.0.1:6379> set a 2

  3. OK

观察 telnet,它收到了一个失效消息:

  1. telnet client

  2. >2

  3. $10

  4. invalidate

  5. *1

  6. $1

  7. a

注意它采用 RESP3 中的 PUSH 类型( >)。

如果这个使用你再使用 redis-cli 更新 a的值,telnet 不会再收到失效消息。除非 telnet client 再 geta一次,重新 tracking a 的值。

可以随时取消 tracking:

  1. telnet client

  2. client tracking off

tracking 特定前缀的 key ( client tracking on)

上面的方式会 tracking 所有的 key,如果你只想跟踪特定的 key, 目前 redis 提供了一种方式,也就是前缀匹配的方式。你可以只 tracking 特定前缀的 key。它值应用了广播模式。

使用 telnet client 设定前缀和开启 tracking:

  1. telnet client

  2. hello 3

  3. .......

  4. client tracking on prefix a bcast

  5. +OK

  6. client tracking on prefix user bcast

  7. +OK

我们 tracking 两个前缀,以 a开头的所有的 key 和以 user开头的所有的 key,所有 a开头的所有的 key 和以 user开头的所有的 key(包括 auser)的 key 变动时它应该都收到消息。

然后我们使用 redis-cli 更新三个 key: abcuser:32432723213feed:6532343243432:

  1. redis-cli

  2. 127.0.0.1:6379> set abc 100

  3. OK

  4. 127.0.0.1:6379> set user:32432723213 good

  5. OK

  6. 127.0.0.1:6379> set feed:6532343243432 abc

  7. OK

telnet client 收到 abcuser:32432723213的失效消息,而不会收到 feed:6532343243432的失效消息:

  1. telnet client

  2. >2

  3. $10

  4. invalidate

  5. *1

  6. $3

  7. abc

  8. >2

  9. $10

  10. invalidate

  11. *1

  12. $16

  13. user:32432723213

你可以通过 client tracking off停止客户端缓存。目前貌似不能只停止对单个的前缀的 tracking。即使你使用 client tracking off prefix user也是取消对所有的 key 的 tracking

  1. src/networking.c

  2. ......

  3. } else if (!strcasecmp(c->argv[2]->ptr,"off")) {

  4.    disableTracking(c);

  5. } else {

  6. ......

选择加入

如果使用 OPTIN,可以有选择的开启 tracking。只有你发送 client caching yes之后的下一条的只读命令的 key 才会 tracking, 否则其它的只读命令中的 key 不会 tracking

首先我们开始 optin,读取 a 的指,这个时候使用 redis-cli client 修改 a 的值为 1000,我们并没有收到 a的失效消息。

  1. telnet client

  2. client tracking on optin

  3. +OK

  4. get a

  5. $1

  6. 2

接下来我们发送 client caching yes,紧接着获取 a 的值,这个时候如果再修改 a 的值,你就可以收到一条 a 的失效消息:

  1. telnet client

  2. client caching yes

  3. +OK

  4. get a

  5. $4

  6. 1000

  7. >2

  8. $10

  9. invalidate

  10. *1

  11. $1

  12. a

必须是紧跟着 client caching yes吗?是的,比如发送下面的命令,只会 tracking b,而不是 a:

  1. telnet client

  2. client caching yes

  3. +OK

  4. get b

  5. _

  6. get a

  7. $4

  8. 2000

选择退出

如果使用 OPTOUT,你也可以有选择的退出 tracking。只有你发送 client caching off之后的下一条的只读命令的 key 才会停止 tracking, 否则其它的只读命令中的 key 都会被 tracking

可以看到它和 OPTIN正好相反,你可以根据你的场景来选择。

比如下面的例子,开启 OPTOUT之后,对任意的 key 的变动都收到失效消息:

  1. telnet client

  2. client tracking on optout

  3. +OK

  4. get a

  5. $4

  6. 3000

  7. >2

  8. $10

  9. invalidate

  10. *1

  11. $1

  12. a

这个时候如果我们想排除 b这个 key,可以只针对它进行设置:

  1. telnet client

  2. client caching no

  3. +OK

  4. get b

  5. $1

  6. 3

之后对 b 的变动并不会收到 b 的失效消息。

注意: OPTINOPTOUT是针对的非 BCAST 场景,也就是只有你发送了 key 的只读命令后,才会跟踪相应的 key。而广播模式是无论你是否发送过 key 的只读命令,只要 redis 修改了 key,都会发送相应 key(或者匹配前缀的 key)的失效消息。

NOLOOP

正常设置时,失效消息是发给所有参与的 client,但是如果设置了 NOLOOP,则不会发送给更新这个 key 的 client。

  1. telnet client

  2. client tracking on bcast noloop

  3. +OK

  4. set a 1

  5. +OK

  6. client tracking off

  7. +OK

  8. client tracking on bcast

  9. +OK

  10. set a 1

  11. +OK

  12. >2

  13. $10

  14. invalidate

  15. *1

  16. $1

  17. a

注意,取消 tracking 只需调用 client tracking off即可。

REDIRECT

最后,让我们看一下转发消息的处理。这是为了兼容 RESP2 协议一个处理方式,将失效消息转发给另外一个 client。

首先我们查看 redis-cli 的 client id:

  1. redis-cli

  2. 127.0.0.1:6379> client list

  3. id=4 addr=127.0.0.1:61017 fd=8 name= age=33103 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=client user=default

使用 telnet 连接 redis,查看 client id:

  1. telnet client

  2. client id

  3. :12

telnet 客户端开启订阅失效消息:

  1. telnet client

  2. SUBSCRIBE __redis__:invalidate

  3. *3

  4. $9

  5. subscribe

  6. $20

  7. __redis__:invalidate

  8. :1

然后我们就可以将 redis-cli 的失效消息转发给 telnet client:

  1. redis-cli

  2. client tracking on bcast redirect 12

  3. 127.0.0.1:6379> set a 1000

  4. OK

可以看到 telnet 客户端收到了失效消息:

  1. telnet client

  2. *3

  3. $7

  4. message

  5. $20

  6. __redis__:invalidate

  7. *1

  8. $1

  9. a

如果你要转发的目的 client 开启了 RESP3 协议,你就不需要 RESP3 Pub/Sub 了,因为 RESP3 原生支持 Push 消息。


点个在看,赞?支持我吧
浏览 38
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报