Redis | 青训营笔记

79 阅读9分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 13 天

本次来讲一讲Redis

什么是Redis

简单来说,你就把他当作一个内存数据库就行了。为什么需要Redis?因为内存的访问速度远比磁盘的访问速度要快,而且快很多。当我们的业务量不断的增加时,磁盘数据库会扛不住压力的,导致数据库宕机。所以Redis就来当他的保护罩了。

Redis有两种用处:缓存与数据库。

在大作业里我使用Redis作为缓存。也就是所有的读数据请求都会到我们的redis中,而写请求才会到我们的db。在大作业中,给出的接口需要写的有注册用户,视频上传,点赞,发布评论,以及聊天。总体来说,和程序的读请求相比,写请求要少很多(点赞的话,没人会反读点吧)。为了提升整体系统的性能我将Redis作为缓存。

Redis提供了两种数据持久化机制分别是AOF和RDB。AOF是增量日志,而RDB是全量日志,这个是在磁盘上的,所以Redis也可以做到数据的持久化。

Redis的数据结构

String

String结构有三种内部编码:int,embstr和raw。

如果一个key的value是整型时,就会被编码为int类型,或者他的字面长度不超过20个,可以保存long类型的整型。如果超过long,就转为raw保存。浮点数也是按照字符串保存的。而且Redis会缓存10000以内整型数值,如果不同的key其value都在10000以内的话,那么他们是共享一个对象的。

而embstr和raw都是SDS的数据格式的:

image-20230215210657444.png 我们的字符串指针指向的是flags位置,但是通过这个指针我们可以向前偏移,从而获取到部分元数据信息。

当我们的数字长度大于20的时候,或者字符串长度大于44时,我们就会使用embstr模式。embstr的意思是,redis对象和这个字符串保存在同一块空间中,连续的。所以embstr是只读的。不能修改。

如果长度大于44,那么就是raw格式了。字符串保存的空间和redis对象可能就不是连续的了。

我们的SDS在存储空间不够的时候,扩展一倍的空间来存储,当大小超过1M后,每次扩容就只会增加1M,而不是加倍扩展了。

当我们缩短一个SDS字符串时,程序不会立即使用内存来重新分配来回收因为缩减字符串而空出的空间,我们的alloc暂时不变。等待后续的使用,这种模式称为惰性空间实放。

以上便是String结构。这里int类型我们保存还是int,所以我们可以对String使用Inr操作(+1操作)。

List

list是链表结构。

image-20230215212426146.png redis的链表有两个数据结构,一个是list结构,一个是listNode结构。list保存的是整体的链表信息,包含了头结点尾节点指针,长度等信息。我们通过key直接拿到的就是这个list结构。而list中的每一个节点就是listNode结构。

现在redis用的是quicklist链表。不过在这之前我们需要先了解一下ziplist。3.2版本后的redis将ziplist作为了一个单元嵌入到了quicklist中。

ziplist

在传统的数组中,例如我们创建一个int数据。但是数据值都是0255。那么我们的每个单元都存在3个字节的浪费。当然这种情况你可以说,你为什么不直接使用BYTE数组。但是如果我们的数据长度是在024位不等呢?而ziplist就是来存这些可变的长的数据。他的思路其实也很简单,就是你在每个单元前添加一个长度即可。

image-20230215213536590.png

但是这样的方式使得我们无法随机访问元素了。不过这是链表,我们无法随机访问也是合乎情理的。

在3.2之前(当然现在也是,不过是作为一个数据结构嵌在其他数据结构中),压缩列表(zip1ist)是列表和哈希的底层实现之一。 当一个列表只包含少量列表项,并且每个列表项要么就是小整数值,要么就是长度比较短的字符串,那么Redis就会使用压缩列表来做列表的底层实现。 当一个哈希只包含少量键值对,比且每个键值对的键和值要么就是小整数值,要么就是长度比较短的字符串,那么Redis就会使用压缩列表来做哈希的底层实现。

image-20230215214233071.png

上面是一个zl的具体结构。

  • zlbytes: 记录整个zl所使用的bytes,32位长
  • zltail: 记录节点到zl起始地址的距离,可以直接确定尾节点的位置,32位长
  • zllen: 保存了当前存在zl中的节点数,16位长
  • zlend: 0xFF标值末端,8位长

zl的起始位置是zlbytes的地址。

image-20230215214811445.png

每一个节点都包含这三个项。第一个是前一个节点的长度,没有这个数据的话,这个链表就是单向的,我们无法从尾部向前遍历。因为我们从尾部开始无法得到前一个节点的数据长度。

每个压缩列表节点可以保存一个字节数组或者一个整数值。其中,字节数组可以是以下三种长度中的一种。

长度小于等于(2^6-1)字节的字节数组、长度小于等于(2^14-1)字节的字节数组、长度小于等于(2^32-1)字节的字节数组

整数值可以是以下6种长度中的一种

  • 4位长,介于0至12之间的无符号整数
  • 1字节长的有符号整数
  • 3字节长的有符号整数
  • int16_t类型整数
  • int32_t类型整数
  • int64_t类型整数

节点的 previous_entry_length属性以字节为单位,记录了压缩列表中前一个节点的长度。 previous_entry_length属性的长度可以是1字节或者5字节。

  • 如果前一节点的长度小于254字节,那么 previous_entry_length属性的长度为1字节,前一节点的长度就保存在这一个字节里面。
  • 如果前一节点的长度大于等于254字节,那么 previous_entry_length属性的长度为5字节:其中属性的第一字节会被设置为0xFE(十进制值254),而之后的四个字节则用于保存前一节点的长度.

节点的encoding属性记录了节点的content属性所保存数据的类型以及长度。

  • 一字节、两字节或者五字节长,值的最高位为00、01或者10的是字节数组编码这种编码表示节点的 content属性保存着字节数组,数组的长度由编码除去最高两位之后的其他位记录。
  • 一字节长,值的最高位以11开头的是整数编码:这种编码表示节点的content属性保存着整数值,整数值的类型和长度由编码除去最高两位之后的其他位记录。

节点的content属性负责保存节点的值,节点值可以是一个字节数组或者整数,值的类型和长度由节点的encoding属性决定。

quicklist

redis现在使用的list实际上就是把双向循环链表中的data部分改成了ziplist,使得存储量增大。

image-20230215215645683.png

Hash

说实话我不是太理解hash这个概念。

我简单的将其看为对象类型。我们通过key得到实际上是一个结构化的对象。也就是得到的是一个字典。

image-20230215220029881.png

哈希类型的内部编码有两种,一个ziplist,一个哈希表。ziplist我们在上面已经讲过了。

当然ziplist只有在特殊的情况下才会使用:

  • 哈希类型元素的个数小于hash-max-ziplist-entries
  • 所有的值都小于hash-max-ziplist-value

才会使用ziplist。

Set

集合类型是一种无序且唯一的键值集合。他的存储数据也不会按照插入的顺序进行存储。

set中有两种内部实现,一种是整数集合,一种是哈希表。其条件和hash使用ziplist的条件类似。要元素个数小于set-maxintset-entries才会使用(关于value就不需要了,本来就是int集合)。

如果集合类型无法使用intset时,就会使用hashtable。

ZSet

有序集合是用ziplist或者skiplist组成的。数据少的时候,用的是ziplist,而且必须要满足以下条件:

  • 元素个数要小于128
  • 所有元素的长度都要小于64字节。

skiplist

这是个很有意思的结构。

实际上就是多层链表,正常的链表我们遍历的时步长位一个节点。但是skiplist在这个基础上增加了步长更长的链表。由于skiplist本身存储的时候就是有序的,所以我们可以类似于二分的效率来查找元素。

image-20230215224640920.png

这就是跳跃表,很简单的一个概念。

Redis使用样例

token

在redis中,我们可以给数据设置过期时间。到了时间后,这条记录就无法访问。而我们的token也是这样的一个机制,我们生成一个token存入数据库,到期后token不可用。

用户数据

我们使用hash来保存一个用户数据,从而使得我们可以使用用户的唯一标识(例如id)来从redis中直接获取这个object的全部信息。

排行榜

我们使用zset这个有序的集合来保存每个人的分数,自动就会出来一个排名。由于是双向链表。我们可以直接读取到顺序或者倒序的数据。

参考:

www.cnblogs.com/hunternet/p… blog.csdn.net/weixin_4191… blog.csdn.net/weixin_4151… blog.csdn.net/YTREE_BJ/ar… redis.io/docs/