Redis6引入新的RESP3协议,并以此为基础加入了客户端缓存的新特性,在此特性下,大大提高了应用程序的响应速度,并降低了数据库的压力,本篇就带大家来看一下Redis6的新特性:客户端缓存。

目录

什么是客户端缓存

什么是RESP3

客户端缓存的实现方式

默认模式

原理

应用

广播模式

原理

应用

重定向模式

OPTIN 和 OPTOUT

NOLOOP选项

失效表key上限

Redis6系列文章:

Redis系列(一)、CentOS7下安装Redis6.0.3稳定版

Redis系列(二)、数据类型之字符串String

Redis系列(三)、数据类型之哈希Hash

Redis系列(四)、数据类型之列表List

Redis系列(五)、数据类型之无序集合Set

Redis系列(六)、数据类型之有序集合ZSet(sorted_set)

Redis系列(七)、常用key命令

Redis系列(八)、常用服务器命令

Redis系列(九)、Redis的“事务”及Lua脚本操作

Redis系列(十)、详解Redis持久化方式AOF、RDB以及混合持久化

Redis系列(十一)、Redis6新特性之ACL安全策略(用户权限管理)

Redis系列(十二)、Redis6集群搭建及原理(主从、哨兵、集群)

Redis系列(十三)、pub/sub发布与订阅(对比List和Kafka)

Redis系列(十四)、Redis6新特性之RESP3与客户端缓存(Client side caching)

什么是客户端缓存

客户端缓存是一种用于创建高性能服务的技术,在此技术下,应用程序端将数据库中的数据缓存在应用端的内存中,当应用程序访问数据时直接从本机内存中读取,而无需连接数据库端,减少了网络IO,提升了应用程序的响应速度,同时也减少了数据库端的压力。

官网:https://redis.io/topics/client-side-caching

Why RESP3: http://antirez.com/news/125

没有客户端缓存:

应用端先查询Redis端,如果没有Redis缓存则到原数据库端查询,如果有则直接从Redis端查询数据,更新数据时直接更新MySQL端并同步到Redis内;

有客户端缓存:

应用端先查询本地缓存如Guava、Caffeine,若没有本地缓存则访问Redis缓存,如果Redis缓存中也没有则查询原数据库;

客户端缓存的优点:

1.降低了客户端的数据延迟,提升客户端的响应速度;

2.数据库端接收的查询减少,降低了数据库端的压力,因此在相同的数据集下可以使用更少的节点提供服务;

疑问:

为了实现客户端缓存,我们面临这样的问题,当进程中缓存了数据,而数据库端数据发生变更,该如何通知到进程,避免客户端显示失效的数据呢? 在Redis中可以使用上篇介绍过的发布订阅机制,向客户端发布数据失效的通知,但该模式下即使某些客户端中没有包含过期数据也会向所有客户端发送无效的消息,非常影响数据库的性能。

在之前的版本中,客户端缓存采用缓存槽(caching slot)的方式记录每个客户端内的key是否发生变化以及时同步,最新版中已弃用该方式,而是采用记录key的名称或前缀。

什么是RESP3

RESP全程RedisSerializationProtocol,是Redis服务端与客户端之间通信的协议。在Redis6之前的版本,使用的是RESP2协议,数据都是以字符串数组的形式返回给客户端,不管是list还是sorted set。因此客户端需要自行去根据类型进行解析,这样会增加了客户端实现的复杂性。

为了照顾老用户,Redis6在兼容 RESP2 的基础上,开始支持 RESP3,但未来会全面切换到RESP3之上。今天的客户端缓存在基于RESP3才能有更好的实现,可以在同一个连接中运行数据的查询和接收失效消息。而目前在RESP2上实现的客户端缓存,需要两个客户端连接以转发重定向的形式实现。
在Redis6中我们可以使用HELLO命令在RESP2和RESP3协议之间进行切换:

#使用RESP2协议

HELLO 2

#使用RESP3协议

HELLO 3

客户端缓存的实现方式

Redis客户端缓存被称为Tracking,在RESP3协议下,有两种模式:

默认模式:服务器记录客户端访问了哪些key,当其中的key发生变更时给客户端发送失效信息,消耗服务器端内存;

广播模式:客户端订阅访问过的key的前缀,当符合模式的key发生变更就会被通知(即使变更的key没有被客户端缓存),服务器端不记录客户端访问的key,因此不会消耗服务器端的内存;

默认模式

原理

服务器端会记录访问key的客户端列表并维护一个表,这个表被称为失效表(Invalidation Table),如果插入一个新的key,服务器端会给客户端发送失效信息并从客户端剔除该key,避免提供过时数据。

在失效表中不会记录key和客户端内对应指针的映射关系,只会记录key的指针和各客户端ID(每个Redis客户端都有一个唯一ID)的映射关系,当发送完失效信息后,客户端剔除key,服务端从失效表中删除key的指针和客户端ID的映射关系。

在失效表中key的命名空间只有一个,即是说,在db0~db15中相同的key名,在失效表中会记录在同一个命名空间内,即使客户端缓存的是db0内的key,如果db1内的同名key被更新,也会通知客户端剔除db0内的同名key。

客户端缓存的操作就是对key的内存地址进行操作:

1.当开启客户端缓存的客户端从Redis获取数据时,Redis服务端会调用enableTracking方法在上面的失效表中记录key和客户端ID的映射关系;

2.若key被修改,则Redis服务端会调用trackingInvalidateKey函数根据该key被缓存的客户端列表ID调用sendTrackingMessage函数向它们发送失效消息。(发送失效消息前会检查客户端的Client_Tracking和NOLOOP状态)

3.服务端发送完失效消息后会从失效表中将该key与客户端ID的映射关系删除;

4.由于客户端可能会在开启之后关闭了缓存功能,在失效表中删除key和该客户端ID之间的映射关系比较消耗性能,因此服务端采用懒删除的方式,只是将该客户端的Client_Tracking相关标志位删除;

应用

上面提到我们可以使用HELLO命令切换RESP3协议,在此协议下我们使用tracking命令开启track追踪,此时服务端会记录客户端在连接的生命周期内的只读的key,当客户端开启track追踪后,key的数据会被缓存在客户端内存中:

#开启RESP3协议

HELLO 3

#开启tracking客户端缓存

client tracking on

#关闭tracking客户端缓存

client tracking off

为了演示失效消息的通知,这里使用telnet测试客户端缓存,然后在另一个redis-cli对key做操作:

# 使用telnet连接客户端
telnet wykd 6379#auth命令登录服务器(如果没有密码可以忽略)
auth default wyk123456#开启RESP3
hello 3#开启客户端缓存tracking
client tracking on#查询一个key 同时该key会被缓存
get name#在另一个redis客户端中 修改/删除/过期/淘汰  该key
set name new_values#在telnet窗口会受到key失效的消息如下:
get nam   #客户端缓存key$3
wyk>2     # 失效消息
$10
invalidate
*1
$4
name#关闭客户端缓存tracking,关闭后不会再收到key的失效消息
client tracking off

当开启了tracking后,客户端缓存的key如果在别处被修改为与原值一样,也会收到失效消息;

当客户端缓存失效后,该key再被修改时,客户端不会再收到消息,也就是再查询该key之后才会在客户端缓存key的值;

当客户端缓存的key因过期策略或内存淘汰策略被驱逐时,服务端也会发送失效消息给开启了tracking的客户端:

当开启了tracking的客户端获取的key不存在时,如果在另一个客户端新增/修改了该key,那个tracking的客户端也会收到失效消息,可见如果key不存在也会在客户端缓存中缓存空值,这种结果因人而异,个人认为这样不太好,一是客户端会徒增大量的无用缓存,而是服务端的失效表会维护更多的key->clientID的映射关系。

广播模式

原理

另一个客户端缓存的实现方式是广播模式(broadcasting),广播不会消耗服务端的内存,而是向各客户端发送更多的失效消息。广播模式与默认模式类似,不同的是广播模式下维护的是前缀表,在前缀表中存储客户端订阅的key前缀与客户端ID之间的映射关系。

在这种模式下,有以下的主要行为:

1.客户端使用BCAST选项开启客户端缓存的广播模式,并使用PREFIX指定一个或多个前缀。如果不指定前缀则默认客户端接收所有的key的失效消息,如果指定则只会接收匹配该前缀的key的失效消息;

2.在广播模式下,服务端维护的不是失效表,而是前缀表(Prefix Table),每个前缀映射一些客户端ID;

3.每次修改跟任意前缀匹配的键时,所有订阅该前缀的客户端都将收到失效消息;

4.服务端的CPU消耗与订阅的key前缀数量成正比,订阅的key前缀数量越多服务器端压力越大;

5.服务器可以为订阅特定前缀的客户端创建单个回复,并向所有的客户端发送相同的回复来进行优化,有助于降低CPU使用率。

应用

同样,在广播模式下也需要开启RESP3协议,这里我们仍然使用刚才的telnet会话进行演示。

使用下面的命令开启广播模式的客户端缓存,上面提到广播模式下服务端维护一个前缀表,记录key的前缀和客户端id的映射关系,因此我们也可以在客户端指定需要接收失效消息的key前缀:

#telnet访问redis客户端(略)

#开启RESP3

hello 3

#开启广播模式的客户端缓存tracking,默认会收到所有的key的失效信息

client tracking on bcast

#开启广播模式的客户端缓存tracking,只接受指定前缀'wyk'的key的失效信息

client tracking on bcast prefix wyk

广播模式下,只要符合客户端设置的key前缀的key发生新增、修改、删除、过期、淘汰等动作,即使该key没有被该客户端缓存,也会收到key的失效消息;

重定向模式

为了兼容RESP2协议,在Redis6中客户端缓存可以以重定向(Redirect)的方式实现,不再使用RESP3原生支持的PUSH消息,而是将消息通过Pub/Sub通知给另外一个客户端连接:

#查看客户端id

client id

#用于接收失效消息的客户端订阅频道

subscribe _redis_:invalidate

#客户端开启Tracking客户端缓存 并指定需要接收失效消息的客户端ID

client tracking on bcast redirect receive_client_id

OPTIN和OPTOUT

在默认模式或重定向模式下,我们可以有选择的对需要的key进行缓存,而由于广播模式是匹配key前缀,因此不能使用此命令。

#RESP3 默认模式

#切换RESP3协议

hello 3

#开启客户端缓存optin选项

client tracking on optin

#此命令后面第一个只读key会被缓存

client caching yes

#RESP2 重定向模式

hello 2

#开启客户端缓存optin选项,1234是接收失效消息的客户端id

client tracking on REDIRECT 1234 OPTIN

#此命令后面第一个只读key会被缓存

client caching yes

OPTIN:只有执行client caching yes之后的第一个key才会被缓存;

OPTOUT:与OPTIN相反,执行client caching on之后的第一个只读key不会被缓存;

注意:在redis6.0.3版本中optin和optout选项时灵时不灵,可能还有BUG;

NOLOOP选项

我们的客户端修改自己已缓存的key的时候也会收到这个key的过期信息,事实上这个客户端是不需要收到该消息的,这造成了浪费,因此我们可以使用NOLOOP选项将该客户端设置为:本客户端修改的key不会收到相关的失效信息。

#开启客户端缓存的NOLOOP选项

client tracking on noloop

开启noloop选项的客户端,如果在该客户端上修改它已经缓存的key,自己不会收到该key的失效消息:

没开启noloop选项的客户端,如果在该客户端上修改它已经缓存的key,自己也会收到该key的失效消息:

失效表key上限

可以使用 tracking_table_max_keys参数修改服务端失效表内记录的缓存的key的数量,当失效表内记录的缓存key达到配置的数量时会随机从失效表内移除缓存:

#查询最大缓存的数量

config get tracking-table-max-keys

#设置最大缓存数量为300

config set tracking-table-max-keys 300

参考文章:

Redis server-assisted client side caching

Why RESP3 will be the only protocol supported by Redis 6

Redis客户端缓存设计(In-Process caching)

带你100% 地了解 Redis 6.0 的客户端缓存

希望本文对你有帮助,请点个赞鼓励一下作者吧~ 谢谢!

Redis系列(十四)、Redis6新特性之RESP3与客户端缓存(Client side caching)相关推荐

  1. Git使用 从入门到入土 收藏吃灰系列 (十四) 清除git仓库的所有提交记录

    文章目录 一.前言 二.清除git仓库的所有提交记录 本节速览 清除git仓库的所有提交记录 一.前言 参考安装Git 详细安装教程 参考视频B站 Git最新教程通俗易懂,这个有点长,感觉讲的精华不多 ...

  2. Redis系列(四)--内存淘汰机制(含单机版内存优化建议)

    每台redis的服务器的内存都是有限的,而且也不是所有的内存都用来存储信息.而且redis的实现并没有在内存这块做太多的优化,所以实现者为了防止内存过于饱和,采取了一些措施来管控内存. 文章结构: ( ...

  3. Reflex WMS入门系列十四:在Reflex系统上创建一个Receipt

    Reflex WMS入门系列十四:在Reflex系统上创建一个Receipt 很多情况下,使用Reflex WMS系统的企业都会使用某个ERP系统,比如SAP,Oracle EBS等,用以支持企业供应 ...

  4. Redis6新特性之ACL安全策略(用户权限管理)

    介绍 在Redis6之前的版本,我们只能使用requirepass参数给default用户配置登录密码,同一个redis集群的所有开发都共享default用户,难免会出现误操作把别人的key删掉或者数 ...

  5. jQuery 1.4 版本的十五个新特性-转载

    jQuery 1.4 最近发布了. 超乎大家的预期,这次并非简单的修修补补,1.4 包含了很多新特性.功能增强和性能提升!本文即向您介绍这些可能对你十分有用的新特性和优化增强. 你可以立刻下载jQue ...

  6. Redis系列教程(四):Redis为什么是单线程、及高并发快的3大原因详解

    Redis的高并发和快速原因 1.redis是基于内存的,内存的读写速度非常快: 2.redis是单线程的,省去了很多上下文切换线程的时间: 3.redis使用多路复用技术,可以处理并发的连接.非阻塞 ...

  7. Android N 完全不同以往的四个新特性

    Google最近发布了Android的下一个版本,Android N的开发者预览版.此次预览版,可以让我们开发者在正式发布之前就测试代码,包括一些新的API,甚至于也可以提前反馈那些对于我们来说有些困 ...

  8. 移动周刊第 177 期:Android 新特性介绍、iOS 客户端框架演进

    写在前面 本期移动周刊第 177 期如约而至,聚焦 Android.iOS.VR/AR/MR.直播等前沿移动开发技术,收录一周最热点,解读开发技巧,我们希望从中能够让你有一些收获,如果你有好的文章以及 ...

  9. 云计算实战系列十四(MySQL基础)

    一.Mysql开篇 1.1.MySQL数据库介绍 什么是数据库DB? 数据库无处不在 DB的全称是database,即数据库的意思.数据库实际上就是一个文件集合,是一个存储数据的仓库,数据库是按照特定 ...

最新文章

  1. OpenCV之gpu 模块. 使用GPU加速的计算机视觉:GPU上的相似度检测(PNSR 和 SSIM)
  2. PAT_B_1094_Java(20分)
  3. Perl 第二章 簡單變量
  4. python函数运行没有结果_python之函数
  5. java小学毕业学的会吗_Java的一些概念
  6. Log4j日志使用记录
  7. el 能否定义作用域变量_EL表达式语法简介及其使用
  8. yandex安装插件教程,研究了一下午终于可以用了
  9. 微信小程序添加体验成员,根据微信号搜索不到
  10. [配置] 修改路由器的名称
  11. 7.选择结构之switch结构
  12. 幽默笑话,哥们误会了,木子家原创
  13. unity 所有版本下载地址
  14. 高质量无损图片压缩算法
  15. 易安卓读取HTML,易安卓(E4A)怎么保存设置?
  16. 深圳金融展 聚焦明朝万达数据防泄密
  17. EAS客户端提示找不到第三方类可能的原因
  18. EF中的TPH、TPT、TPC
  19. 【Elasticsearch】基本操作
  20. 【高考志愿】计算机专业志愿填报指南

热门文章

  1. python背诵技巧_15条常用Python小技巧
  2. Flink专题-BaseTransform
  3. flink check-point save-point理解
  4. webpack编译过程
  5. 【解题报告】Leecode. 575. 分糖果——Leecode每日一题系列
  6. 消除左递归c++代码_【每日算法Day 85】图解算法:一行代码解决约瑟夫环的变体...
  7. 5最后一条记录_在一堆数据中,如何获取最后一次记录?
  8. 应付账款账龄分析模板_超全的财务会计表单模板分享
  9. 红帽7破解ROOT密码(简单易懂)
  10. js最简单数组去重_js简单数组去重