Redis 拾遗

85 阅读6分钟

Redis 可以说是现在软件开发中最常用的中间件了,可以用来做 Cache、分布式锁、队列等用途,高级特性里面的 Geo、Bitmap、HyperLogLog 等更是具有“神奇”的能力,合理的应用可以大大提升我们开发系统的能力。同时 Redis 提供了完善的高可用方案,可以很好的支持 AP。但是 Redis 使用的不当也会造成一些问题,所以想要驯服 Redis,需要注意一些常见的问题。

基本数据类型

String

String 类型应该是我们最常用的数据类型,其底层实现是简单动态字符串 SDS(Simple Dynamic String)。当字符串长度小于 1M 时,扩容都是加倍现有的空间,如果超过 1M,扩容时一次只会多扩 1M 的空间,最大长度为 512M。

应用场景

字符串类型

当作为字符串类型时候经常用于缓存或者共享对象使用,比如 Session、分布式锁等。String 类型也是二进制安全的,也可以用来存放小文件。

整数类型

在 SET 的时候,如果值为整数值,那么 redisObject 的 encoding 则为 int(可以使用 OBJECT ENCODING key 命令来查看)。可以应用于秒杀、限流、计数等场景。

使用示例

字符串类型

**

## 普通字符串使用
127.0.0.1:6379> SET abc def
OK
127.0.0.1:6379> GET abc
"def"
## 锁使用使用
## 当已经存在 key 将返回 0
127.0.0.1:6379> SETNX abc edf
(integer) 0
127.0.0.1:6379> SETNX def def
(integer) 1
整数类型

**

## 整数类型使用
127.0.0.1:6379> SET abc 123
OK
## 原子加 1
127.0.0.1:6379> INCR abc
(integer) 124
### 原子减1
127.0.0.1:6379> DECR abc
(integer) 123
## 原子加 5
127.0.0.1:6379> INCRBY abc 5
(integer) 128

List

List 是简单的字符串列表,按照插入顺序排序。一个列表最多可以包含 2^{32}-1 个元素(大约40亿)。

应用场景

List 根据其特性可以很容易实现队列、栈数据结构。当 LPUSH + RPOP 就实现了队列数据结构,而使用 LPUSH + LPOP 就可以实现栈数据结构。

BLPOP、BRPOP 会移出并获取列表的第一个或最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止,可以用来实现消息队列功能。

使用示例

**

## 队列右边加入元素
127.0.0.1:6379> RPUSH list 1 2
(integer) 2
## 获取队列中所有元素
127.0.0.1:6379> LRANGE list 0 -1
1) "1"
2) "2"
## 队列左边加入元素
127.0.0.1:6379> LPUSH list 0
(integer) 3
127.0.0.1:6379> LRANGE list 0 -1
1) "0"
2) "1"
3) "2"
    
## 移除列表最后一个元素,并返回
127.0.0.1:6379> RPOP list
"2"
127.0.0.1:6379> LRANGE list 0 -1
1) "0"
2) "1"
## 移除列表第一个元素,并返回
127.0.0.1:6379> LPOP list
"0"
127.0.0.1:6379> LRANGE list 0 -1
1) "1"
    
## 使用 LTRIM 可以对列表进行裁剪,不在指定区间之内的元素都将被删除
127.0.0.1:6379> RPUSH list 2 3 4
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
127.0.0.1:6379> LTRIM list 1 2
OK
127.0.0.1:6379> LRANGE list 0 -1
1) "2"
2) "3"

Hash

使用场景

Hash 也是最常用到的数据结构之一了,主要用来做数据归并。比如当缓存用户信息的时候,可以将用户ID、姓名、性别等其他信息,归并到同一个 key,在取用的时候也很方便,可以统一取出,也可以指定字段取出。

Hash 也支持 HSETNX 对指定字段设置值(当字段不存在时候),与普通的 SETNX 类似。但是 Hash 不支持字段级别过期时间,只能给整个 key 加过期时间。

使用示例

**

## 使用 HMSET 一次设置多个字段
127.0.0.1:6379> HMSET hash id 123 name jack sex man login_times 5
OK
127.0.0.1:6379> HGETALL hash
1) "name"
2) "jack"
3) "id"
4) "123"
5) "sex"
6) "man"
7) "login_times"
8) "5"
## Hash 也支持使用 HINCRBY 对字段进行加值
127.0.0.1:6379> HINCRBY hash login_times 1
(integer) 6
    
## Hash 也支持 HSETNX 对指定字段设置值(当字段不存在时候),与普通的 SETNX 类似
127.0.0.1:6379> HSETNX hash lock_key lock_value
(integer) 1
127.0.0.1:6379> HSETNX hash lock_key lock_value
(integer) 0

Set

Set 是 String 类型的去重无序集合,集合中成员是唯一的。

使用场景

Set 提供了随机移除、集合交集、集合并集、集合差集等操作。结合这些特性,可以用于如抽奖这样的随机事件,可以实现查找共同好友、可能认识的人、获取人脉等操作。

使用示例

**

## 向集合中添加对象
127.0.0.1:6379> SADD set1 a b c d
(integer) 4
127.0.0.1:6379> SADD set2 c d e f
(integer) 4
## 查看在 set1 没在 set2 的元素,差集
127.0.0.1:6379> SDIFF set1 set2
1) "a"
2) "b"
## 查看既在 set1 又在 set2 的元素,交集
127.0.0.1:6379> SINTER set1 set2
1) "c"
2) "d"
## 查看在 set1 或在 set2 的元素,并集
127.0.0.1:6379> SUNION set1 set2
1) "c"
2) "d"
3) "b"
4) "a"
5) "e"
6) "f"
    
## 随机获取指定数量的元素,不会从集合中删除元素
127.0.0.1:6379> SRANDMEMBER set1 2
1) "a"
2) "b"
127.0.0.1:6379> SRANDMEMBER set1 2
1) "a"
2) "d"
## 随机移除指定数量的元素,会从集合中删除元素
127.0.0.1:6379> SPOP set1 2
1) "b"
2) "c"
127.0.0.1:6379> SMEMBERS set1
1) "a"
2) "d"   
    
## SDIFF、SINTER、SUNION 都支持后面加上 STORE,将计算结果存入指定的 key
127.0.0.1:6379> SDIFFSTORE set3 set1 set2
(integer) 1
127.0.0.1:6379> SMEMBERS set3
1) "a"

Sorted Set

Sorted Set 和 Set 一样也是 string 类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个 double 类型的分数,通过分数来为集合中的成员进行从小到大的排序。

压缩表

在键值对少于 128 且每个元素小于 64 字节时候,使用压缩表(ziplist)数据结构来存储数据。数据结构如下:

压缩表

  • zlbytes:记录整个压缩列表占用的字节数
  • zltail:记录压缩列表表尾节点距离压缩列表起始地址有多少字节
  • zllen:记录了压缩列表包含的节点数量
  • entryN:压缩列表的节点,节点长度由节点保存的内容决定
  • zlend:特殊值 0xFF,用于标记压缩列表的末端

  • previous_entry_length:记录了压缩表中前一个节点的长度
  • encoding:记录了节点 content 属性所保存数据的类型以及长度,最两位标识类型,其余为长度
  • content:保存节点的值,节点值可以是一个字节数组或者整数
跳表

在键值较多的时候将使用跳表(skiplist)数据结构来存储数据,跳表结构简单,结构如下所示。跳表与二叉查找树类似,都是为了提高查询效率,将 O(n) 的查询效率降低到 O(logn)。

跳表

使用场景

由于 Sorted Set 是一个有序集合,所以天然的适合进行排序相关的场景。如排行榜、具有优先级特性的队列功能等。

使用示例

**

## 向集合中添加元素
127.0.0.1:6379> ZADD sortedset 100 a 200 b 300 c 250 d
(integer) 4
127.0.0.1:6379> ZRANGE sortedset 0 -1 WITHSCORES
1) "a"
2) "100"
3) "b"
4) "200"
5) "d"
6) "250"
7) "c"
8) "300"
    
## 获取指定区间分数的成员数
127.0.0.1:6379> ZCOUNT sortedset 200 300
(integer) 3
    
## 对指定成员的分数加上增量值
127.0.0.1:6379> ZINCRBY sortedset 100 b
"300"
127.0.0.1:6379> ZRANGE sortedset 0 -1 WITHSCORES
1) "a"
2) "100"
3) "d"
4) "250"
5) "b"
6) "300"
7) "c"
8) "300"

## 返回集合中指定成员的排行,从 0 开始
127.0.0.1:6379> ZRANK sortedset d
(integer) 1
    
## 按照排行移除排行 0-1 的元素,利用此命令可以实现优先级队列
## ZREMRANGEBYSCORE 按照分数移除
## ZREMRANGEBYLEX 按照 key 字典区间移除
## ZREM 按照元素 key 移除
127.0.0.1:6379> ZREMRANGEBYRANK sortedset 0 1
(integer) 2
127.0.0.1:6379> ZRANGE sortedset 0 -1 WITHSCORES
1) "b"
2) "300"
3) "c"
4) "300"