一个系统中的不同层之间的访问速度不一样,所以我们才需要缓存,这样就可以把一些需要频繁访问的数据放在缓存中,以加快它们的访问速度。
为了让你能更好地理解,我以计算机系统为例,来解释一下。下图是计算机系统中的三层存储结构,以及它们各自的常用容量和访问性能。最上面是处理器,中间是内存,最下面是磁盘。
CPU、内存和磁盘这三层的访问速度从几十ns到100ns,再到几ms,性能的差异很大。
想象一下,如果每次CPU处理数据时,都要从ms级别的慢速磁盘中读取数据,然后再进行处理,那么,CPU只能等磁盘的数据传输完成。这样一来,高速的CPU就被慢速的磁盘拖累了,整个计算机系统的运行速度会变得非常慢。
所以,计算机系统中,默认有两种缓存:
跟内存相比,LLC的访问速度更快,而跟磁盘相比,内存的访问是更快的。所以,我们可以看出来缓存的第一个特征:在一个层次化的系统中,缓存一定是一个快速子系统,数据存在缓存中时,能避免每次从慢速子系统中存取数据。对应到互联网应用来说,Redis就是快速子系统,而数据库就是慢速子系统了。
知道了这一点,你就能理解,为什么我们必须想尽办法让Redis提供高性能的访问,因为,如果访问速度很慢,Redis作为缓存的价值就不大了。
LLC的大小是MB级别,page cache的大小是GB级别,而磁盘的大小是TB级别。这其实包含了缓存的第二个特征:缓存系统的容量大小总是小于后端慢速系统的,我们不可能把所有数据都放在缓存系统中。 表明,缓存的容量终究是有限的,缓存中的数据量也是有限的,肯定是没法时刻都满足访问需求的。所以,缓存和后端慢速系统之间,必然存在数据写回和再读取的交互过程。简单来说,缓存中的数据需要按一定规则淘汰出去,写回后端系统,而新的数据又要从后端系统中读取进来,写入缓存。 而Redis本身是支持按一定规则淘汰数据的,相当于实现了缓存的数据淘汰,其实,这也是Redis适合用作缓存的一个重要原因。
缓存介于应用程序和物理数据源之间。
缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写数据,在特定的时刻或事件会同步缓存和物理数据源的数据。 通常直接查询 MySQL,但在高并发下,大量查询 MySQL 数据库会导致数据库性能变慢,解决方案就是在应用层与 MySQL 之间搭建一个 Cache 层,让请求先访问 Cache,就能大大降低MySQL的压力,还能提高系统的性能。
缓存虽然无需考虑安全性,但需结合业务影响考虑何时失效,和 MySQL 的数据一致性容忍度如何。
业务应用在访问Redis缓存中的数据时,数据不一定存在,因此,处理的方式也不同。
作缓存时,会将其部署在DB之前,业务应用访问数据时,会先查询Redis是否保存对应数据。根据数据是否存在缓存中:
使用Redis缓存时,我们基本操作如下:
这些操作应由谁做呢?
和Redis缓存的使用方式相关。接下来,我就来和你聊聊Redis作为旁路缓存的使用操作方式。
Redis内部使用一个redisObject对象来标识所有的key和value数据,redisObject最主要的信息:
raw列为对象的编码方式
网络I/O模型上看,Redis使用单线程的I/O复用模型,自己封装了一个简单的AeEvent事件处理框架,主要实现了epoll、kqueue和select。对于单纯只有I/O操作来说,单线程可以将速度优势发挥到最大,但是Redis也提供了一些简单的计算功能,比如排序、聚合等,对于这些操作,单线程模型实际会严重影响整体吞吐量,CPU计算过程中,整个I/O调度都是被阻塞住的,在这些特殊场景的使用中,需要额外的考虑。
相较于memcached的预分配内存管理,Redis使用现场申请内存
的方式来存储数据,并且很少使用free-list等方式来优化内存分配,会在一定程度上存在内存碎片
。
Redis跟据存储命令参数,会把带过期时间的数据单独存放在一起,并把它们称为临时数据,非临时数据是永远不会被剔除的,即便物理内存不够,导致swap也不会剔除任何非临时数据(但会尝试剔除部分临时数据)。
有两种,一种采用strings存储,另外使用hashes存储。那使用哪种更好呢:
在某些特殊情况,比如初始化数据或缓存出现异常,没有将数据进行同步时,这时需要进行全量的数据同步。 全量同步方式有两种:
即Pipeline 管道批量插入。通过pipeline指令完成。 Redis 是一种基于客户端-服务端模型以及请求 / 响应协议的 TCP 服务。 当请求进来后,都是经过服务器进行返回。若服务器没有响应及时,则其他请求进入等待。 这时服务器也无法处理新请求,如何解决这种现象? 答案就是管道:将多个命令发送到服务器,而不用等待响应,最后在一个步骤中读取该响应。MySQL 的批量插入就是这样。
比如平时有个商家入驻了:查询=》审核=》当后台系统审核新商家后,将数据写入 Redis,核心代码如下
后台修改饭馆信息时(审核通过后),要进行修改,关键代码:
当客户端的用户给饭馆投票时(喜欢 / 不喜欢餐厅),记得修改餐厅的 likeVotes 或 dislikeVotes 字段。 跟修改饭馆不一样的地方就是,只需要修改其中 likeVotes 和 dislikeVotes 属性,不需要整体进行修改
当用户查询餐厅时,若餐厅没有,会查询数据,然后再更新缓存:
先查询缓存 =》缓存没有 =》 查询数据库(再更新缓存)
参考