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 之前,linkedlist 和 ziplist 两种编码可以选择切换,它们之间的转换关系如图:

同样地,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

最低0.47元/天 解锁文章
507






