Redis 笔记

Redis常用命令

1. String(字符串)

  • SET key value:设置键值对(若key已存在则覆盖)。
  • GET key:获取键对应的值(若key不存在返回nil)。
  • NCR key:将键的值递增1(仅适用于整数,若key不存在则初始化为0再递增)。
  • DECR key:将键的值递减1(仅适用于整数)。
  • APPEND key value:在键对应值的末尾追加字符串(若key不存在则创建)。
  • STRLEN key:获取键对应值的长度(字节长度,二进制安全)。
  • SETEX key seconds value:设置键值对并指定过期时间(秒,过期后自动删除)。
  • SETNX key value:仅在键不存在时设置值(用于实现分布式锁,返回1表示设置成功,0表示key已存在)。

2. Hash(哈希)

  • HSET key field value:设置哈希表中指定字段的值(若key不存在则创建,字段已存在则覆盖)。
  • HGET key field:获取哈希表中指定字段的值(若key或field不存在返回nil)。
  • HMSET key field1 value1 field2 value2:批量设置哈希表的多个字段值(效率高于多次调用HSET)。
  • HMGET key field1 field2:批量获取哈希表的多个字段值(返回列表,顺序与请求一致)。
  • HDEL key field1 field2:删除哈希表中的一个或多个字段(返回实际删除的字段数)。

3. List(列表)

  • LPUSH key value1 value2:将一个或多个值插入列表头部(左侧)(若key不存在则创建空列表)。
  • RPUSH key value1 value2:将一个或多个值插入列表尾部(右侧)(若key不存在则创建空列表)。
  • LPOP key:移除并返回列表头部的元素(若key不存在返回nil)。
  • RPOP key:移除并返回列表尾部的元素(若key不存在返回nil)。
  • LRANGE key start stop:获取列表指定范围内的元素(start/stop为索引,0表示头部,-1表示尾部,支持负数倒序)。
  • LLEN key:获取列表的长度(元素个数)。

4. Set(集合)

  • SADD key member1 member2:向集合中添加一个或多个成员(已存在的成员将被忽略,返回实际添加的成员数)。
  • SMEMBERS key:获取集合中的所有成员(返回列表,无序)。
  • SREM key member1 member2:移除集合中的一个或多个成员(返回实际移除的成员数)。
  • SISMEMBER key member:检查成员是否存在于集合中(存在返回1,不存在返回0)。
  • SINTER key1 key2:返回多个集合的交集(元素同时存在于所有集合中)。
  • SUNION key1 key2:返回多个集合的并集(元素存在于任一集合中)。
  • SDIFF key1 key2:返回多个集合的差集(元素存在于第一个集合但不在其他集合中)。

5. Zset(有序集合)
Zset 是有序唯一字符串集合(元素不重复),每个元素关联一个分数(score),按分数从小到大排序(分数可重复)。常用命令围绕有序操作

  • ZADD key score1 member1 score2 member2:向有序集合中添加一个或多个成员及分数(若key不存在则创建,成员已存在则更新分数,返回实际添加的成员数)。
  • ZRANGE key start stop [WITHSCORES]:按分数升序返回指定索引范围内的成员(WITHSCORES 表示同时返回分数)。
  • ZREVRANGE key start stop [WITHSCORES]:按分数降序返回指定索引范围内的成员(WITHSCORES表示同时返回分数)。
  • ZREM key member1 member2:移除有序集合中的一个或多个成员(返回实际移除的成员数)。
  • ZCOUNT key min max:统计有序集合中分数在[min, max]范围内的成员数。

Redis 数据类型(底层)

全局哈希表

Redis 使用了一个哈希表来保存所有键值对。

一个哈希表,其实就是一个数组,数组的每个元素称为一个哈希桶。每个哈希桶中保存了键值对数据。

哈希桶中的元素保存的并不是值本身,而是指向具体值的指针

在下图中,可以看到,哈希桶中的 entry 元素中保存了 key 和 value 指针,分别指向了实际的键和值,这样一来,即使值是一个集合,也可以通过 value 指针被查找到。

在这里插入图片描述

哈希表的最大好处很明显,就是让我们可以用 O(1) 的时间复杂度来快速查找到键值对——我们只需要计算键的哈希值,就可以知道它所对应的哈希桶位置,然后就可以访问相应的 entry 元素。

这个查找过程主要依赖于哈希计算,和数据量的多少并没有直接关系。也就是说,不管哈希表里有 10 万个键还是 100 万个键,我们只需要一次计算就能找到相应的键。

哈希冲突怎么解决?

两个 key 的哈希值和哈希桶计算对应关系时,正好落在了同一个哈希桶中。

Redis 解决哈希冲突的方式,就是链式哈希。链式哈希也很容易理解,就是指同一个哈希桶中的多个元素用一个链表来保存,它们之间依次用指针连接。

如下图所示:entry1、entry2 和 entry3 都需要保存在哈希桶 3 中,导致了哈希冲突。此时,entry1 元素会通过一个 next 指针指向 entry2 ,同样, entry2 也会通过next指针指向 entry3。这样一来,即使哈希桶 3 中的元素有 100 个,我们也可以通过 entry 元素中的指针,把它们连起来。这就形成了一个链表,也叫作哈希冲突链。
在这里插入图片描述

Rehash

如果哈希表里写入的数据越来越多,哈希冲突可能也会越来越多,这就会导致某些哈希冲突链过长,进而导致这个链上的元素查找耗时长,效率降低。

所以,Redis 会对哈希表做 rehash 操作。rehash 也就是增加现有的哈希桶数量,让逐渐增多的 entry 元素能在更多的桶之间分散保存,减少单个桶中的元素数量,从而减少单个桶中的冲突。

简单来说就是在第二步拷贝数据时,Redis 仍然正常处理客户端请求,每处理一个请求时,从哈希表 1 中的第一个索引位置开始,顺带着将这个索引位置上的所有 entries 拷贝到哈希表 2 中;等处理下

在这里插入图片描述

String

string 可以存储任意类型的数据,比如文本、数字、图片或者序列化的对象。

一个 string 类型的键最大可以存储 512 MB 的数据。

简单动态字符串 SDS

string 类型的底层实现是 SDS(simple dynamic string),它是一个动态字符串结构,由长度、空闲空间和字节数组三部分组成:

在这里插入图片描述

  • 字符串长度处理:Redis获取字符串长度,时间复杂度为O(1),而C语言中,需要从头开始遍历,复杂度为O(n);
  • 空间预分配:字符串修改越频繁的话,内存分配越频繁,就会消耗性能,而SDS修改和空间扩充,会额外分配未使用的空间,减少性能损耗。
  • 惰性空间释放:SDS 缩短时,不是回收多余的内存空间,而是free记录下多余的空间,后续有变更,直接使用free中记录的空间,减少分配。
  • 二进制安全:Redis可以存储一些二进制数据,在C语言中字符串遇到’\0’会结束,而 SDS中标志字符串结束的是len属性。

什么是二进制安全?
通俗讲,C语言中,用’\0’表示字符串的结束,如果字符串中本身就有’\0’字符,字符串就会被截断,即非二进制安全;
若通过某种机制,保证读写字符串时不损害其内容,则为二进制安全。

SDS 有 3 种编码类型:

  • embstr:占用 64Bytes 的空间,存储 44Bytes 的数据
  • raw:存储大于 44Bytes 的数据
  • int:存储整数类型

embstr 和 raw 存储字符串数据,int 存储整型数据

embstr 结构

embstr 结构存储小于等于44个字节的字符串,embstr 每次开辟 64byte 的空间。

在这里插入图片描述
raw 结构

在这里插入图片描述

embstr 和 raw 的转换

在存储字符串的时候,redis会根据数据的长度判断使用哪种结构:

  • 如果长度小于等于44个字节,就会选择embstr 结构
  • 如果长度大于44个byte,就会选择raw结构

在这里插入图片描述

127.0.0.1:6379> object encoding str
"embstr"

# str赋值44个字节的字符串
127.0.0.1:6379> set str 1234567890123456789012345678901234567890abcd
OK
127.0.0.1:6379> object encoding str
"embstr"

# str2赋值45个字节的字符串
127.0.0.1:6379> set str2 1234567890123456789012345678901234567890abcde
OK
127.0.0.1:6379> object encoding str2
"raw"

127.0.0.1:6379> set num 123
OK
127.0.0.1:6379> object encoding num
"int"

Hash

hash 是一个键值对集合,它可以存储多个字段和值,类似于编程语言中的 map 对象。一个 hash 类型的键最多可以存储 2^32 - 1 个字段。

Hash类型的底层实现有三种:

  • ziplist:压缩列表,当 hash 达到一定的阈值时,会自动转换为 hashtable 结构
  • listpack:紧凑列表,在 Redis7.0 之后,listpack 正式取代 ziplist。同样的,当 hash 达到一定的阈值时,会自动转换为 hashtable 结构
  • hashtable:哈希表,类似 map

ziplist(redis7.0之前使用)
listpack(redis7.0之后使用)

ziplist 结构

ziplist 是一种 紧凑的链表 结构,它将所有的字段和值顺序地存储在一块连续的内存中。
在这里插入图片描述

typedef struct {
   
   
  /* 当使用字符串时,slen表示为字符串长度 */
  unsigned char *sval;
  unsigned int slen;
  /* 当使用整形时,sval为NULL,lval为ziplistEntry的value */
  long long lval;
} ziplistEntry;
listpack 结构

在这里插入图片描述

hashTable

hashTable是一种散列表结构,它将字段和值分别存储在两个数组中,并通过哈希函数计算字段在数组中的索引
在这里插入图片描述

struct dict {
   
   
    dictType *type;
    dictEntry **ht_table[2];
    unsigned long ht_used[2];
    long rehashidx; /* 当进行rehash时,rehashidx为-1 */
    int16_t pauserehash; /* 如果rehash暂停,pauserehash则大于0,(小于0表示代码错误)*/
    signed char ht_size_exp[2]; /* 哈希桶的个数(size = 1<<exp) */
};

typedef struct dict {
   
   
    dictEntry **table;
    dictType *type;
    unsigned long size;
    unsigned long sizemask;
    unsigned long used;
    void *privdata;
} dict;

typedef struct dictEntry {
   
   
    void *key;
    void *val;
    struct dictEntry *next;
} dictEntry;

ziplist 和 hashTable 的转换

在这里插入图片描述

127.0.0.1:6379> hset h1 id 123456789012345678901234567890123456789012345678901234567890abcd
(integer) 1
127.0.0.1:6379> object encoding h1
"ziplist"
127.0.0.1:6379> hset h2 id 123456789012345678901234567890123456789012345678901234567890abcde
(integer) 1
127.0.0.1:6379> object encoding h2
"hashtable"

hashTable变得越来越长怎么办
扩容,hashTabel是怎么扩容的?使用的是渐进式扩容rehash。rehash会重新计算哈希值,且将哈希桶的容量扩大。

rehash 步骤
在这里插入图片描述

List(底层)

list 是一个有序的字符串列表,它按照插入顺序排序,并且支持在两端插入或删除元素。一个 list 类型的键最多可以存储 2^32 - 1 个元素。

redis3.2 以后,list 类型的底层实现只有一种结构,就是 quicklist

在讲解list结构之前,需要先说明一下list结构编码的更替,如下

  • 在Redis3.2之前,list使用的是linkedlist和ziplist
  • 在Redis3.2~Redis7.0之间,list使用的是quickList,是linkedlist和ziplist的结合
  • 在Redis7.0之后,list使用的也是quickList,只不过将ziplist转为listpack,它是listpack、linkedlist结合版
linkedlist 与 ziplist

在 Redis3.2 之前,linkedlistziplist 两种编码可以选择切换,它们之间的转换关系如图:
在这里插入图片描述
同样地,ziplist 转为 linkedlist 的条件可在 redis.conf 配置:

list-max-ziplist-entries 512
list-max-ziplist-value 64
quickList(ziplist、linkedlist结合版)

quicklist 存储了一个双向列表,每个列表的节点是一个ziplist,所以实际上 quicklist 并不是一个新的数据结构,它就是 linkedlist 和 ziplist 的结合,然后被命名为快速列表。
在这里插入图片描述

quickList(listpack、linkedlist结合版)

和Hash结构一样,因为ziplist有连锁更新问题,redis7.0 将 ziplist 替换为 listpack,下面是新 quickList 的结构图
在这里插入图片描述

Set(底层)

在讲解set结构之前,需要先说明一下set结构编码的更替,如下

  • 在 Redis7.2 之前,set 使用的是 intset 和 hashtable
  • 在 Redis7.2 之后,set 使用的是 intset
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值