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 是简单的字符串列表,按照插入顺序排序。一个列表最多可以包含 个元素(大约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"