前言
Redis 作为高性能内存数据库的标杆,其核心优势之一在于提供了丰富的数据类型。这些数据类型不仅满足了多样化的业务需求,还通过高效的数据结构实现了卓越的性能。本文将深入剖析 Redis 的 5 大基础数据类型(String、List、Set、Hash、Sorted Set),结合底层原理、常用操作、代码示例和典型场景,帮助开发者全面掌握 Redis 数据类型的核心知识。
一、String(字符串):最基础的键值存储
1. 数据结构与存储原理
Redis 的 String 基于简单动态字符串(SDS)
实现,支持自动扩容,最大可存储 512MB 数据。SDS 通过预分配空间减少内存重分配次数,同时记录字符串长度,实现 O (1) 时间复杂度的长度查询。
- 存储类型:可存储字符串、整数、浮点数
- 典型应用:缓存、计数器、分布式锁
2. 核心操作命令
(1)基础读写
# 设置值(覆盖已有值)
127.0.0.1:6379> SET key "value"
OK
# 获取值(key不存在时返回nil)
127.0.0.1:6379> GET key
"value"
# 追加值(key不存在时创建新字符串)
127.0.0.1:6379> APPEND key " appended"
(integer) 11 # 返回新字符串长度
(2)数值操作(仅整数支持自增自减)
# 初始化为整数
127.0.0.1:6379> SET counter 10
OK
# 自增1(返回新值)
127.0.0.1:6379> INCR counter
(integer) 11
# 自减1
127.0.0.1:6379> DECR counter
(integer) 10
# 按步长增减(支持负数)
127.0.0.1:6379> INCRBY counter 5
(integer) 15
127.0.0.1:6379> DECRBY counter 3
(integer) 12
(3)扩展操作
# 获取字符串长度
127.0.0.1:6379> STRLEN key
(integer) 11
# 批量设置/获取(减少网络IO)
127.0.0.1:6379> MSET user:1:name "Alice" user:1:age 25
OK
127.0.0.1:6379> MGET user:1:name user:1:age
1) "Alice"
2) "25"
3. 典型应用场景
- 缓存数据库结果:
// 伪代码:缓存用户信息 String userKey = "user:1001"; redisTemplate.opsForValue().set(userKey, jsonUser, 3600, TimeUnit.SECONDS);
- 分布式锁(利用 SET 的原子性):
SET lock:resource "1" NX PX 5000 # NX表示仅当key不存在时设置,PX设置过期时间
二、List(列表):有序可重复的链表结构
1. 数据结构与特性
List 是基于双向链表实现的有序集合,支持从头部(左)或尾部(右)快速插入 / 删除元素。索引访问时间复杂度为 O (n),适合频繁的两端操作。
- 存储特点:有序、允许重复、元素按插入顺序排列
- 底层实现:压缩列表(ziplist,元素少且小)或双向链表(linkedlist,元素多或大)
2. 核心操作命令
(1)元素插入
# 左插入(头部添加)
127.0.0.1:6379> LPUSH list "b" "a" # 结果:["a", "b"]
(integer) 2
# 右插入(尾部添加)
127.0.0.1:6379> RPUSH list "c" # 结果:["a", "b", "c"]
(integer) 3
(2)元素删除
# 左弹出(删除并返回头部元素)
127.0.0.1:6379> LPOP list
"a" # 剩余:["b", "c"]
# 右弹出(删除并返回尾部元素)
127.0.0.1:6379> RPOP list
"c" # 剩余:["b"]
(3)范围查询与修剪
# 获取全量元素(索引0到-1表示所有元素)
127.0.0.1:6379> LRANGE list 0 -1
1) "b"
# 修剪列表(保留索引0-1的元素)
127.0.0.1:6379> LTRIM list 0 1
OK
3. 典型应用场景
- 消息队列(生产者 - 消费者模型):
# 生产者右推消息 RPUSH message_queue "msg1" "msg2" # 消费者左弹出消息(先进先出) LPOP message_queue
- 最新动态列表:
通过LPUSH+LRANGE 0 9
实现最多保留 10 条最新动态。
三、Set(集合):无序唯一的哈希集合
1. 数据结构与特性
Set 基于哈希表实现,元素无序且唯一,添加 / 删除 / 查询的时间复杂度均为 O (1)。当元素为整数且数量较少时,底层会使用更节省内存的整数集合(intset)。
- 核心特性:去重、高效的集合运算(并 / 交 / 差集)
2. 核心操作命令
(1)元素操作
# 添加元素(重复添加自动忽略)
127.0.0.1:6379> SADD set "a" "b" "a"
(integer) 2 # 实际新增2个元素
# 判断元素是否存在
127.0.0.1:6379> SISMEMBER set "a"
(integer) 1 # 存在返回1,不存在返回0
# 删除元素
127.0.0.1:6379> SREM set "b"
(integer) 1
(2)集合运算
# 创建两个集合
127.0.0.1:6379> SADD set1 "a" "b" "c"
(integer) 3
127.0.0.1:6379> SADD set2 "c" "d" "e"
(integer) 3
# 并集(所有元素)
127.0.0.1:6379> SUNION set1 set2
1) "a"
2) "b"
3) "c"
4) "d"
5) "e"
# 交集(共同元素)
127.0.0.1:6379> SINTER set1 set2
1) "c"
# 差集(set1有但set2没有的元素)
127.0.0.1:6379> SDIFF set1 set2
1) "a"
2) "b"
3. 典型应用场景
- 唯一计数:
通过SADD
记录用户 ID,SCARD
获取独立用户数。 - 标签系统:
为每个用户添加标签(Set),通过交集查找有共同标签的用户。
四、Hash(哈希):结构化数据的存储方案
1. 数据结构与特性
Hash 用于存储键值对集合,每个字段(field)对应一个值(value),适合存储对象的多个属性。底层实现为哈希表,当字段和值均为整数且数量较少时,使用压缩列表(ziplist)
优化内存。
- 典型场景:存储对象(如用户信息、商品详情)
2. 核心操作命令
(1)字段读写
# 单个字段设置/获取
127.0.0.1:6379> HSET user id 1 name "Bob" age 30
(integer) 3 # 成功设置3个字段
127.0.0.1:6379> HGET user name
"Bob"
# 批量字段设置/获取
127.0.0.1:6379> HMSET user email "bob@example.com" city "New York"
OK
127.0.0.1:6379> HMGET user name email
1) "Bob"
2) "bob@example.com"
(2)字段管理
# 获取所有字段和值
127.0.0.1:6379> HGETALL user
1) "id"
2) "1"
3) "name"
4) "Bob"
5) "age"
6) "30"
7) "email"
8) "bob@example.com"
9) "city"
10) "New York"
# 删除字段
127.0.0.1:6379> HDEL user age
(integer) 1
# 检查字段是否存在
127.0.0.1:6379> HEXISTS user age
(integer) 0 # 0表示不存在
3. 典型应用场景
- 存储对象数据:
// 伪代码:存储用户对象 Map<String, String> userMap = new HashMap<>(); userMap.put("name", "Alice"); userMap.put("age", "25"); redisTemplate.opsForHash().putAll("user:1002", userMap);
- 计数器优化:
对单个对象的多个计数器(如用户的点赞数、评论数),使用 Hash 的字段独立计数,避免大量 String 键的开销。
五、Sorted Set(有序集合):带权重的排序神器
1. 数据结构与特性
Sorted Set 每个元素关联一个分数(score),通过跳表(SkipList)
和哈希表实现:
- 跳表用于按分数排序,支持快速范围查询
- 哈希表用于按元素值快速查找分数
- 元素唯一,分数可重复,适合需要排序的场景
2. 核心操作命令
(1)元素插入与查询
# 添加元素(score为排序依据)
127.0.0.1:6379> ZADD scores 85 "Alice" 90 "Bob" 78 "Charlie"
(integer) 3
# 按分数升序获取前2名(索引0-1)
127.0.0.1:6379> ZRANGE scores 0 1 WITHSCORES # WITHSCORES显示分数
1) "Charlie"
2) "78"
3) "Alice"
4) "85"
# 按分数降序获取前2名
127.0.0.1:6379> ZREVRANGE scores 0 1 WITHSCORES
1) "Bob"
2) "90"
3) "Alice"
4) "85"
(2)范围与分数操作
# 获取分数在80-90之间的元素
127.0.0.1:6379> ZRANGEBYSCORE scores 80 90 WITHSCORES
1) "Alice"
2) "85"
# 增加元素分数(原子操作)
127.0.0.1:6379> ZINCRBY scores 5 "Alice"
"90" # Alice的分数变为90
3. 典型应用场景
- 排行榜系统:
# 每日游戏积分排名(保留30天数据) ZADD daily_rank:20231001 1500 "userA" 2000 "userB" ZREVRANGE daily_rank:20231001 0 9 WITHSCORES # 取前10名
- 带权重的任务队列:
通过分数控制任务优先级,高分任务优先处理。
六、数据类型对比与选型建议
数据类型 | 存储结构 | 核心特性 | 典型场景 | 时间复杂度(常用操作) |
---|---|---|---|---|
String | SDS 动态字符串 | 简单键值、数值操作 | 缓存、计数器、分布式锁 | GET/SET: O(1) |
List | 双向链表 / 压缩列表 | 有序、可重复、两端快速操作 | 消息队列、最新列表 | LPUSH/RPOP: O(1) |
Set | 哈希表 / 整数集合 | 无序、唯一、集合运算 | 去重、标签匹配、交集分析 | SADD/SISMEMBER: O(1) |
Hash | 哈希表 / 压缩列表 | 键值对集合、结构化存储 | 对象存储、多字段计数 | HSET/HGET: O(1) |
Sorted Set | 跳表 + 哈希表 | 有序、带分数排序 | 排行榜、优先级队列 | ZADD/ZRANGE: O(logN) |
结语
Redis 的基础数据类型是其强大功能的基石,每种类型都针对特定场景设计了高效的数据结构。掌握它们的核心特性、操作命令和适用场景,是合理使用 Redis 的关键。在实际开发中,应根据数据特征(如是否需要排序、去重、结构化存储)选择合适的数据类型,并结合 Redis 的持久化、集群等特性,构建高性能、高可用的应用系统。
通过深入理解这些数据类型,开发者可以充分发挥 Redis 的内存优势,在缓存加速、实时统计、分布式协调等场景中实现优雅的解决方案。