Redis缓存设计原理及实战

CPU、内存和磁盘这三层的访问速度从几十ns到100ns,再到几ms,性能的差异很大。

想象一下,如果每次CPU处理数据时,都要从ms级别的慢速磁盘中读取数据,然后再进行处理,那么,CPU只能等磁盘的数据传输完成。这样一来,高速的CPU就被慢速的磁盘拖累了,整个计算机系统的运行速度会变得非常慢。

所以,计算机系统中,默认有两种缓存:

  • CPU里面的末级缓存,即LLC,用来缓存内存中的数据,避免每次从内存中存取数据

  • 内存中的高速页缓存,即page cache,用来缓存磁盘中的数据,避免每次从磁盘中存取数据

跟内存相比,LLC的访问速度更快,而跟磁盘相比,内存的访问是更快的。所以,我们可以看出来缓存的第一个特征:在一个层次化的系统中,缓存一定是一个快速子系统,数据存在缓存中时,能避免每次从慢速子系统中存取数据。对应到互联网应用来说,Redis就是快速子系统,而数据库就是慢速子系统了。

知道了这一点,你就能理解,为什么我们必须想尽办法让Redis提供高性能的访问,因为,如果访问速度很慢,Redis作为缓存的价值就不大了。

LLC的大小是MB级别,page cache的大小是GB级别,而磁盘的大小是TB级别。这其实包含了缓存的第二个特征:缓存系统的容量大小总是小于后端慢速系统的,我们不可能把所有数据都放在缓存系统中。

表明,缓存的容量终究是有限的,缓存中的数据量也是有限的,肯定是没法时刻都满足访问需求的。所以,缓存和后端慢速系统之间,必然存在数据写回和再读取的交互过程。简单来说,缓存中的数据需要按一定规则淘汰出去,写回后端系统,而新的数据又要从后端系统中读取进来,写入缓存。

而Redis本身是支持按一定规则淘汰数据的,相当于实现了缓存的数据淘汰,其实,这也是Redis适合用作缓存的一个重要原因。

缓存介于应用程序和物理数据源之间。

  • 作用

降低应用对物理数据源访问的频次,提高应用的运行性能。

缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写数据,在特定的时刻或事件会同步缓存和物理数据源的数据。

通常直接查询 MySQL,但在高并发下,大量查询 MySQL 数据库会导致数据库性能变慢,解决方案就是在应用层与 MySQL 之间搭建一个 Cache 层,让请求先访问 Cache,就能大大降低MySQL的压力,还能提高系统的性能。

缓存虽然无需考虑安全性,但需结合业务影响考虑何时失效,和 MySQL 的数据一致性容忍度如何。

业务应用在访问Redis缓存中的数据时,数据不一定存在,因此,处理的方式也不同。

Redis缓存处理请求

==========================================================================

作缓存时,会将其部署在DB之前,业务应用访问数据时,会先查询Redis是否保存对应数据。根据数据是否存在缓存中:

  • 缓存命中

Redis中有相应数据,就直接读取Redis,性能非常快

  • 缓存缺失

Redis中没有保存相应数据,就从后端数据库中读取数据,性能就会变慢。一旦缓存缺失,就得将缺失数据写入Redis,该过程就是缓存更新。涉及缓存和DB数据一致性问题。

使用Redis缓存时,我们基本操作如下:

  • 应用读取数据时,需要先读取Redis

  • 发生缓存缺失时,需要从DB读数据

  • 发生缓存缺失时,还需要更新缓存

这些操作应由谁做呢?

和Redis缓存的使用方式相关。接下来,我就来和你聊聊Redis作为旁路缓存的使用操作方式。

Redis数据模型

========================================================================

Redis内部使用一个redisObject对象来标识所有的key和value数据,redisObject最主要的信息:

  • type代表一个value对象具体是何种数据类型

  • encoding是不同数据类型在Redis内部的存储方式

比如——type=string代表value存储的是一个普通字符串,那么对应的encoding可以是raw或是int,如果是int则代表Redis内部是按数值类型存储和表示这个字符串。

raw列为对象的编码方式

  • 字符串可以被编码为raw(一般字符串)或Rint(为了节约内存,Redis会将字符串表示的64位有符号整数编码为整数来进行储存)

  • 列表可以被编码为ziplist或linkedlist,ziplist是为节约大小较小的列表空间而作的特殊表示

  • 集合可以被编码为intset或者hashtable,intset是只储存数字的小集合的特殊表示

  • hash表可以编码为zipmap或者hashtable,zipmap是小hash表的特殊表示

  • 有序集合可以被编码为ziplist或者skiplist格式

  • ziplist用于表示小的有序集合

  • skiplist则用于表示任何大小的有序集合

网络I/O模型上看,Redis使用单线程的I/O复用模型,自己封装了一个简单的AeEvent事件处理框架,主要实现了epoll、kqueue和select。对于单纯只有I/O操作来说,单线程可以将速度优势发挥到最大,但是Redis也提供了一些简单的计算功能,比如排序、聚合等,对于这些操作,单线程模型实际会严重影响整体吞吐量,CPU计算过程中,整个I/O调度都是被阻塞住的,在这些特殊场景的使用中,需要额外的考虑。

相较于memcached的预分配内存管理,Redis使用现场申请内存的方式来存储数据,并且很少使用free-list等方式来优化内存分配,会在一定程度上存在内存碎片

Redis跟据存储命令参数,会把带过期时间的数据单独存放在一起,并把它们称为临时数据,非临时数据是永远不会被剔除的,即便物理内存不够,导致swap也不会剔除任何非临时数据(但会尝试剔除部分临时数据)。

缓存数据结构抉择

=======================================================================

有两种,一种采用strings存储,另外使用hashes存储。那使用哪种更好呢:

  • strings

存储较简单,固定的数据。比如存储一个简单的用户信息 (用户名、昵称、头像、年龄等)。存储时需要将数据进行序列化,获取时要反序列化。在数据量较小的情况下还是可以忽略这种开销。但如果存储的的数据可能某些属性会有些变化,比如餐厅数据中,它有 likeVotes(喜欢) 和 dislikeVotes(不喜欢) 的数量,这类变的数据,那么我们采用hashes会更好,而且存储的时候没有序列化开销

  • 官方推荐使用hashes

实战 - 将数据加入缓存

===========================================================================

添加方式


全量添加

在某些特殊情况,比如初始化数据或缓存出现异常,没有将数据进行同步时,这时需要进行全量的数据同步。

全量同步方式有两种:

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
roid开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!**

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 17
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值