Redis 共有 5 种基本数据类型:String(字符串)、List(列表)、Set(集合)、Hash(散列)、Zset(有序集合)。
这 5 种数据类型是直接提供给用户使用的,是数据的保存形式,其底层实现主要依赖这 8 种数据结构:简单动态字符串(SDS)、LinkedList(双向链表)、Dict(哈希表/字典)、SkipList(跳跃表)、Intset(整数集合)、ZipList(压缩列表)、QuickList(快速列表)。
Redis 5 种基本数据类型对应的底层数据结构实现如下表所示:
| String | List | Hash | Set | Zset |
|---|---|---|---|---|
| SDS | LinkedList/ZipList/QuickList | Dict、ZipList | Dict、Intset | ZipList、SkipList |
Redis 3.2 之前,List 底层实现是 LinkedList 或者 ZipList。 Redis 3.2 之后,引入了 LinkedList 和 ZipList 的结合 QuickList,List 的底层实现变为 QuickList。从 Redis 7.0 开始, ZipList 被 ListPack 取代。
你可以在 Redis 官网上找到 Redis 数据类型/结构非常详细的介绍:
未来随着 Redis 新版本的发布,可能会有新的数据结构出现,通过查阅 Redis 官网对应的介绍,你总能获取到最靠谱的信息。

String(字符串)
介绍
String 是 Redis 中最简单同时也是最常用的一个数据类型。一个 Redis 中字符串 value 最多可以是 512M。
String 是一种二进制安全的数据类型,可以用来存储任何类型的数据比如字符串、整数、浮点数、图片(图片的 base64 编码或者解码或者图片的路径)、序列化后的对象。

虽然 Redis 是用 C 语言写的,但是 Redis 并没有使用 C 的字符串表示,而是自己构建了一种 简单动态字符串(Simple Dynamic String,SDS)。相比于 C 的原生字符串,Redis 的 SDS 不光可以保存文本数据还可以保存二进制数据,并且获取字符串长度复杂度为 O(1)(C 字符串为 O(N)),除此之外,Redis 的 SDS API 是安全的,不会造成缓冲区溢出。
String 的数据结构为简单动态字符串 (Simple Dynamic String, 缩写 SDS),是可以修改的字符串。内部通过 char 数组来存放字符。
Redis String 是二进制安全的。二进制安全其本质上将操作输入作为原始的、无任何特殊格式意义的数据流。C 语言字符串在实现时不是二进制安全的,因为在读入字符串时会将字符串中 \0 后面的内容忽略,即数据流是有特殊意义的。Redis SDS API 都会以二进制的方式来处理数据,并不会赋予假设、限制、过滤等等。因此 Redis String 可以保存任何格式的二进制数据,如图片。
Redis String 采用预分配冗余空间的方式来减少内存的频繁分配。当字符串长度小于 1M 时,扩容都是加倍现有的空间,如果超过 1M,扩容时一次只会多扩 1M 的空间。需要注意的是字符串最大长度为 512M。
Redis 在进行 incr 或 decr 等操作时,会判断能否将字符串转换为数字的形式,如果可以则转换为数字再操作;否则报错。
常用命令
| 命令 | 介绍 |
|---|---|
| SET key value | 设置指定 key 的值 |
| SETNX key value | 只有在 key 不存在时设置 key 的值 |
| GET key | 获取指定 key 的值 |
| MSET key1 value1 key2 value2 …… | 设置一个或多个指定 key 的值 |
| MGET key1 key2 … | 获取一个或多个指定 key 的值 |
| STRLEN key | 返回 key 所储存的字符串值的长度 |
| INCR key | 将 key 中储存的数字值增一 |
| DECR key | 将 key 中储存的数字值减一 |
| EXISTS key | 判断指定 key 是否存在 |
| DEL key(通用) | 删除指定的 key |
| EXPIRE key seconds(通用) | 给指定 key 设置过期时间 |
更多 Redis String 命令以及详细使用指南,请查看 Redis 官网对应的介绍:https://redis.io/commands/?group=string 。
基本操作:
> SET key value
OK
> GET key
"value"
> EXISTS key
(integer) 1
> STRLEN key
(integer) 5
> DEL key
(integer) 1
> GET key
(nil)批量设置:
> MSET key1 value1 key2 value2
OK
> MGET key1 key2 # 批量获取多个 key 对应的 value
1) "value1"
2) "value2"mget key1 [key2..]→ 获取多个 value。某个 key 不存在,那么这个 key 对应的 value 返回特殊值 nil。mset key value [key value ...]→ 同时设置一个或多个 key-value 对。msetnx key value [key value ...]→ key 均不存在,添加并返回 1。否则返回 0。
计数器(字符串的内容为整数的时候可以使用):
> SET number 1
OK
> INCR number # 将 key 中储存的数字值增一
(integer) 2
> GET number
"2"
> DECR number # 将 key 中储存的数字值减一
(integer) 1
> GET number
"1"incrby key increment→ 将 key 所储存的值加上给定的增量值(increment)。同上。incrbyfloat key increment→ 将 key 所储存的值加上给定的浮点增量值(increment)。同上。指数符号改成非指数符号。忽略多余 0。需要时转为整数。decr key→ 将 key 中储存的数字值减一。同上。decrbt key decrement→ key 所储存的值减去给定的减量值(decrement)。同上。
设置过期时间(默认为永不过期):
> EXPIRE key 60
(integer) 1
> SETEX key 60 value # 设置值并设置过期时间
OK
> TTL key
(integer) 56应用场景
需要存储常规数据的场景
- 举例:缓存 Session、Token、图片地址、序列化后的对象(相比较于 Hash 存储更节省内存)。
- 相关命令:
SET、GET。
需要计数的场景
- 举例:用户单位时间的请求数(简单限流可以用到)、页面单位时间的访问数。
- 相关命令:
SET、GET、INCR、DECR。
分布式锁
利用 SETNX key value 命令可以实现一个最简易的分布式锁(存在一些缺陷,通常不建议这样实现分布式锁)。
List(列表)
介绍
Redis 中的 List 其实就是链表数据结构的实现。我在线性数据结构 :数组、链表、栈、队列 这篇文章中详细介绍了链表这种数据结构,我这里就不多做介绍了。
许多高级编程语言都内置了链表的实现比如 Java 中的 LinkedList,但是 C 语言并没有实现链表,所以 Redis 实现了自己的链表数据结构。Redis 的 List 的实现为一个 双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。

常用命令
| 命令 | 介绍 |
|---|---|
| RPUSH key value1 value2 … | 在指定列表的尾部(右边)添加一个或多个元素 |
| LPUSH key value1 value2 … | 在指定列表的头部(左边)添加一个或多个元素 |
| LSET key index value | 将指定列表索引 index 位置的值设置为 value |
| LPOP key | 移除并获取指定列表的第一个元素(最左边) |
| RPOP key | 移除并获取指定列表的最后一个元素(最右边) |
| LLEN key | 获取列表元素数量 |
| LRANGE key start end | 获取列表 start 和 end 之间的元素 |
更多 Redis List 命令以及详细使用指南,请查看 Redis 官网对应的介绍:https://redis.io/commands/?group=list 。
通过 RPUSH/LPOP 或者 LPUSH/RPOP 实现队列:
> RPUSH myList value1
(integer) 1
> RPUSH myList value2 value3
(integer) 3
> LPOP myList
"value1"
> LRANGE myList 0 1
1) "value2"
2) "value3"
> LRANGE myList 0 -1
1) "value2"
2) "value3"通过 RPUSH/RPOP 或者 LPUSH/LPOP 实现栈:
> RPUSH myList2 value1 value2 value3
(integer) 3
> RPOP myList2 # 将 list的最右边的元素取出
"value3"lpop key
存在,移出并第一个元素;不存在,返回 nil。rpop key
存在,移出并最后一个元素;不存在,返回 nil。lpush key value1 [value2]
存在,插入头部;不存在,先创建后插入头部;非列表类型,返回错误rpush key value1 [value2]
存在,插入尾部;不存在,先创建后插入尾部;非列表类型,返回错误lpushx key value
列表存在,插入头部;不存在,不操作。rpushx key value
列表存在,插入尾部;不存在,不操作。blpop key1 [key2 ] timeout
t 时间内,执行 lpop 操作。成功,第一元素 key,第二元素 value。超时,返回 nil。brpop key1 [key2 ] timeout
t 时间内,执行 rpop 操作。成功,第一元素 key,第二元素 value。超时,返回 nil。
我专门画了一个图方便大家理解 RPUSH , LPOP , lpush , RPOP 命令:

通过 LRANGE 查看对应下标范围的列表元素:
> RPUSH myList value1 value2 value3
(integer) 3
> LRANGE myList 0 1
1) "value1"
2) "value2"
> LRANGE myList 0 -1
1) "value1"
2) "value2"
3) "value3"通过 LRANGE 命令,你可以基于 List 实现分页查询,性能非常高!
通过 LLEN 查看链表长度:
> LLEN myList
(integer) 3出队后再入队
rpoplpush source destination
移除列表的最后一个元素,并将该元素添加到另一个列表并返回brpoplpush source destination timeout
从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它;如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
索引相关 (支持正负索引)
lindex key index
get 索引。正-正序,负-逆序。lset key index value
set 索引。当索引参数超出范围,或对一个空列表进行 LSET 时,返回一个错误。lrange key start stop
获得区间元素。索引范围可超。ltrim key start stop
只保留区间元素。索引范围可超。
其他命令
llen key
长度。不存在,0.linsert key before|after pivot value
在 povot 前或者后插入元素,不存在则不操作。lrem key count value
移除与参数 VALUE 相等的元素。count=0,移除全部;>0,表头开始移除;<0,表尾开始移除。
应用场景
信息流展示
- 举例:最新文章、最新动态。
- 相关命令:
LPUSH、LRANGE。
消息队列
List 可以用来做消息队列,只是功能过于简单且存在很多缺陷,不建议这样做。
相对来说,Redis 5.0 新增加的一个数据结构 Stream 更适合做消息队列一些,只是功能依然非常简陋。和专业的消息队列相比,还是有很多欠缺的地方比如消息丢失和堆积问题不好解决。
Hash(哈希)
介绍
Redis 中的 Hash 是一个 String 类型的 field-value(键值对) 的映射表,特别适合用于存储对象,后续操作的时候,你可以直接修改这个对象中的某些字段的值。
Hash 类似于 JDK1.8 前的 HashMap,内部实现也差不多(数组 + 链表)。不过,Redis 的 Hash 做了更多优化。最多包含 个键值对。

常用命令
| 命令 | 介绍 |
|---|---|
| HSET key field value | 设置指定哈希表中指定字段的值 |
| HSETNX key field value | 只有指定字段不存在时设置指定字段的值 |
| HMSET key field1 value1 field2 value2 … | 同时将一个或多个 field-value (域-值)对设置到指定哈希表中 |
| HGET key field | 获取指定哈希表中指定字段的值 |
| HMGET key field1 field2 … | 获取指定哈希表中一个或者多个指定字段的值 |
| HGETALL key | 获取指定哈希表中所有的键值对 |
| HEXISTS key field | 查看指定哈希表中指定的字段是否存在 |
| HDEL key field1 field2 … | 删除一个或多个哈希表字段 |
| HLEN key | 获取指定哈希表中字段的数量 |
| HINCRBY key field increment | 对指定哈希中的指定字段做运算操作(正数为加,负数为减) |
hlen key
获取哈希表中字段的数量hkeys key
获取所有哈希表中的字段hvals key
获取哈希表中所有值。hdel key field1 [field2]
删除一个或多个哈希表字段hexists key field
查看哈希表 key 中,指定的字段是否存在。hincrby key field increment
为哈希表 key 中的指定字段的整数值加上增量 increment 。hincrbyfloat key field increment
为哈希表 key 中的指定字段的浮点数值加上增量 increment 。hscan key cursor [match pattern] [count count]
迭代哈希表中的键值对。
更多 Redis Hash 命令以及详细使用指南,请查看 Redis 官网对应的介绍:https://redis.io/commands/?group=hash 。
模拟对象数据存储:
> HMSET userInfoKey name "guide" description "dev" age 24
OK
> HEXISTS userInfoKey name # 查看 key 对应的 value中指定的字段是否存在。
(integer) 1
> HGET userInfoKey name # 获取存储在哈希表中指定字段的值。
"guide"
> HGET userInfoKey age
"24"
> HGETALL userInfoKey # 获取在哈希表中指定 key 的所有字段和值
1) "name"
2) "guide"
3) "description"
4) "dev"
5) "age"
6) "24"
> HSET userInfoKey name "GuideGeGe"
> HGET userInfoKey name
"GuideGeGe"
> HINCRBY userInfoKey age 2
(integer) 26应用场景
对象数据存储场景
- 举例:用户信息、商品信息、文章信息、购物车信息。
- 相关命令:
HSET(设置单个字段的值)、HMSET(设置多个字段的值)、HGET(获取单个字段的值)、HMGET(获取多个字段的值)。
Set(集合)
介绍
Redis 中的 Set 类型是一种无序集合,集合中的元素没有先后顺序但都唯一,有点类似于 Java 中的 HashSet 。你可以基于 Set 轻易实现交集、并集、差集的操作。

常用命令
| 命令 | 介绍 |
|---|---|
| SADD key member1 member2 … | 向指定集合添加一个或多个元素 |
| SMEMBERS key | 获取指定集合中的所有元素 |
| SCARD key | 获取指定集合的元素数量 |
| SISMEMBER key member | 判断指定元素是否在指定集合中 |
| SINTER key1 key2 … | 获取给定所有集合的交集 |
| SINTERSTORE destination key1 key2 … | 将给定所有集合的交集存储在 destination 中 |
| SUNION key1 key2 … | 获取给定所有集合的并集 |
| SUNIONSTORE destination key1 key2 … | 将给定所有集合的并集存储在 destination 中 |
| SDIFF key1 key2 … | 获取给定所有集合的差集 |
| SDIFFSTORE destination key1 key2 … | 将给定所有集合的差集存储在 destination 中 |
| SPOP key count | 随机移除并获取指定集合中一个或多个元素 |
| SRANDMEMBER key count | 随机获取指定集合中指定数量的元素 |
更多 Redis Set 命令以及详细使用指南,请查看 Redis 官网对应的介绍:https://redis.io/commands/?group=set 。
基本操作:
> SADD mySet value1 value2
(integer) 2
> SADD mySet value1 # 不允许有重复元素,因此添加失败
(integer) 0
> SMEMBERS mySet
1) "value1"
2) "value2"
> SCARD mySet
(integer) 2
> SISMEMBER mySet value1
(integer) 1
> SADD mySet2 value2 value3
(integer) 2mySet:value1、value2。mySet2:value2、value3。
交并差集
sinter key1 [key2]
返回给定所有集合的交集sinterstore destination key1 [key2]
返回给定所有集合的交集并存储在 destination 中sunion key1 [key2]
返回所有给定集合的并集sunionstore destination key1 [key2]
所有给定集合的并集存储在 destination 集合中sdiff key1 [key2]
返回第一个集合与其他集合之间的差异。sdiffstore destination key1 [key2]
返回给定所有集合的差集并存储在 destination 中
其他命令
scard key
获取集合的成员数sadd key member1 [member2]
向集合添加一个或多个成员srem key member1 [member2]
移除集合中一个或多个成员spop key
移除并返回集合中的一个随机元素smembers key
返回集合中的所有成员sismember key member
判断 member 元素是否是集合 key 的成员srandmember key [count]
返回集合中一个或多个随机数smove source destination member
将 member 元素从 source 集合移动到 destination 集合sscan key cursor [match pattern] [count count]
迭代集合中的元素
应用场景
需要存放的数据不能重复的场景
- 举例:网站 UV 统计(数据量巨大的场景还是
HyperLogLog更适合一些)、文章点赞、动态点赞等场景。 - 相关命令:
SCARD(获取集合数量) 。

需要获取多个数据源交集、并集和差集的场景
- 举例:共同好友(交集)、共同粉丝(交集)、共同关注(交集)、好友推荐(差集)、音乐推荐(差集)、订阅号推荐(差集+交集) 等场景。
- 相关命令:
SINTER(交集)、SINTERSTORE(交集)、SUNION(并集)、SUNIONSTORE(并集)、SDIFF(差集)、SDIFFSTORE(差集)。

需要随机获取数据源中的元素的场景
- 举例:抽奖系统、随机点名等场景。
- 相关命令:
SPOP(随机获取集合中的元素并移除,适合不允许重复中奖的场景)、SRANDMEMBER(随机获取集合中的元素,适合允许重复中奖的场景)。
Sorted Set(有序集合)
介绍
Sorted Set 类似于 Set,但和 Set 相比,Sorted Set 增加了一个权重参数 score,使得集合中的元素能够按 score 进行有序排列,还可以通过 score 的范围来获取元素的列表。有点像是 Java 中 HashMap 和 TreeSet 的结合体。
有序集合的每个成员都关联了一个 double 类型的评分(score), 这个评分(score)被用来按照从最低分到最高分的方式排序集合中的成员。
集合的成员是唯一的,但是评分可以是重复了。
因为元素是有序的, 所以你也可以很快的根据评分(score)或者次序(position)来获取一个范围的元素。
访问有序集合的中间元素也是非常快的,因此你能够使用有序集合作为一个没有重复成员的智能列表。
其实在 redis sorted sets 里面当 items 内容大于 64 的时候同时使用了 hash 和 skiplist 两种设计实现。这也会为了排序和查找性能做的优化。所以如上可知:
- 添加和删除都需要修改 skiplist,所以复杂度为 O(log(n))。
- 查找元素的话可以直接使用 hash,其复杂度为 O(1)
- 其他的 range 操作复杂度一般为 O (log (n))
当然如果是小于 64 的时候,因为是采用了 ziplist 的设计,其时间复杂度为 O (n)
SortedSet(zset) 是 Redis 提供的一个非常特别的数据结构,一方面它等价于Java的数据结构 Map<String, Double>,可以给每一个元素 value 赋予一个权重 score,另一方面它又类似于 TreeSet,内部的元素会按照权重 score 进行排序,可以得到每个元素的名次,还可以通过 score 的范围来获取元素的列表。

常用命令
| 命令 | 介绍 |
|---|---|
| ZADD key score1 member1 score2 member2 … | 向指定有序集合添加一个或多个元素 |
| ZCARD KEY | 获取指定有序集合的元素数量 |
| ZSCORE key member | 获取指定有序集合中指定元素的 score 值 |
| ZINTERSTORE destination numkeys key1 key2 … | 将给定所有有序集合的交集存储在 destination 中,对相同元素对应的 score 值进行 SUM 聚合操作,numkeys 为集合数量 |
| ZUNIONSTORE destination numkeys key1 key2 … | 求并集,其它和 ZINTERSTORE 类似 |
| ZDIFFSTORE destination numkeys key1 key2 … | 求差集,其它和 ZINTERSTORE 类似 |
| ZRANGE key start end | 获取指定有序集合 start 和 end 之间的元素(score 从低到高) |
| ZREVRANGE key start end | 获取指定有序集合 start 和 end 之间的元素(score 从高到底) |
| ZREVRANK key member | 获取指定有序集合中指定元素的排名(score 从大到小排序) |
更多 Redis Sorted Set 命令以及详细使用指南,请查看 Redis 官网对应的介绍:https://redis.io/commands/?group=sorted-set 。
基本操作:
> ZADD myZset 2.0 value1 1.0 value2
(integer) 2
> ZCARD myZset
2
> ZSCORE myZset value1
2.0
> ZRANGE myZset 0 1
1) "value2"
2) "value1"
> ZREVRANGE myZset 0 1
1) "value1"
2) "value2"
> ZADD myZset2 4.0 value2 3.0 value3
(integer) 2
myZset:value1(2.0)、value2(1.0) 。myZset2:value2(4.0)、value3(3.0) 。
获取指定元素的排名:
> ZREVRANK myZset value1
0
> ZREVRANK myZset value2
1求交集:
> ZINTERSTORE myZset3 2 myZset myZset2
1
> ZRANGE myZset3 0 1 WITHSCORES
value2
5求并集:
> ZUNIONSTORE myZset4 2 myZset myZset2
3
> ZRANGE myZset4 0 2 WITHSCORES
value1
2
value3
3
value2
5求差集:
> ZDIFF 2 myZset myZset2 WITHSCORES
value1
2插入删除
zadd key score1 member1 [score2 member2]
向有序集合添加一个或多个成员,或者更新已存在成员的分数zrem key member [member ...]
移除有序集合中的一个或多个成员zremrangebylex key min max
移除有序集合中给定的字典区间的所有成员zremrangebyrank key start stop
移除有序集合中给定的排名区间的所有成员zremrangebyscore key min max
移除有序集合中给定的分数区间的所有成员
范围查看
zrange key start stop [withscores]
通过索引区间返回有序集合指定区间内的成员zrangebyscore key min max [withscores] [limit]
通过分数返回有序集合指定区间内的成员zrevrange key start stop [withscores]
返回有序集中指定区间内的成员,通过索引,分数从高到低zrevrangebyscore key max min [withscores]
返回有序集中指定分数区间内的成员,分数从高到低排序zrangebylex key min max [limit offset count]
通过字典区间返回有序集合的成员
交并操作
zinterstore destination numkeys key [key ...]
计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 destination 中zunionstore destination numkeys key [key ...]
计算给定的一个或多个有序集的并集,并存储在新的 key 中
其他操作
zcard key
获取有序集合的成员数zcount key min max
计算在有序集合中指定区间分数的成员数zlexcount key min max
在有序集合中计算指定字典区间内成员数量zincrby key increment member
有序集合中对指定成员的分数加上增量 incrementzrank key member
返回有序集合中指定成员的索引zrevrank key member
返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序zscore key member
返回有序集中,成员的分数值zscan key cursor [match pattern] [count count]
迭代有序集合中的元素(包括元素成员和元素分值)
应用场景
需要随机获取数据源中的元素根据某个权重进行排序的场景
- 举例:各种排行榜比如直播间送礼物的排行榜、朋友圈的微信步数排行榜、王者荣耀中的段位排行榜、话题热度排行榜等等。
- 相关命令:
ZRANGE(从小到大排序)、ZREVRANGE(从大到小排序)、ZREVRANK(指定元素排名)。

需要存储的数据有优先级或者重要程度的场景 比如优先级任务队列。
- 举例:优先级任务队列。
- 相关命令:
ZRANGE(从小到大排序)、ZREVRANGE(从大到小排序)、ZREVRANK(指定元素排名)。
总结
| 数据类型 | 说明 |
|---|---|
| String | 一种二进制安全的数据类型,可以用来存储任何类型的数据比如字符串、整数、浮点数、图片(图片的 base64 编码或者解码或者图片的路径)、序列化后的对象。 |
| List | Redis 的 List 的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。 |
| Hash | 一个 String 类型的 field-value(键值对) 的映射表,特别适合用于存储对象,后续操作的时候,你可以直接修改这个对象中的某些字段的值。 |
| Set | 无序集合,集合中的元素没有先后顺序但都唯一,有点类似于 Java 中的 HashSet 。 |
| Zset | 和 Set 相比,Sorted Set 增加了一个权重参数 score,使得集合中的元素能够按 score 进行有序排列,还可以通过 score 的范围来获取元素的列表。有点像是 Java 中 HashMap 和 TreeSet 的结合体。 |