Redis 数据结构详解
Redis 支持多种数据结构,每种数据结构都有其特定的用途和优势。本文档详细介绍 Redis 的各种数据结构及其应用场景。
目录
基本数据结构
String(字符串)
String 是 Redis 最基本的数据类型,可以存储字符串、整数或浮点数。
特点
- 最大存储 512MB
- 二进制安全
- 支持原子性操作
- 时间复杂度 O(1)
常用命令
# 基本操作
SET key value [EX seconds] [PX milliseconds] [NX|XX]
GET key
MSET key value [key value ...]
MGET key [key ...]
# 数值操作
INCR key
INCRBY key increment
DECR key
DECRBY key decrement
# 字符串操作
APPEND key value
STRLEN key
GETRANGE key start end
SETRANGE key offset value
应用场景
- 缓存数据
- 计数器
- 分布式锁
- 会话存储
- 简单键值存储
示例
# 缓存用户信息
SET user:1001:profile '{"name":"张三","age":25}' EX 3600
# 页面访问计数
INCR page:views:homepage
# 分布式锁
SET lock:resource "locked" NX EX 30
Hash(哈希)
Hash 是一个键值对集合,适合存储对象属性。
特点
- 每个 Hash 可存储 2^32 - 1 个键值对
- 字段名和值都是字符串
- 支持部分字段操作
- 内存效率高
常用命令
# 基本操作
HSET key field value [field value ...]
HGET key field
HMSET key field value [field value ...]
HMGET key field [field ...]
HGETALL key
# 字段管理
HDEL key field [field ...]
HEXISTS key field
HKEYS key
HVALS key
HLEN key
# 数值操作
HINCRBY key field increment
HINCRBYFLOAT key field increment
应用场景
- 存储对象属性
- 用户配置
- 购物车
- 配置管理
示例
# 存储用户信息
HSET user:1001 name "张三" age 25 email "zhangsan@example.com"
# 获取用户信息
HGET user:1001 name
HMGET user:1001 name age email
# 用户积分
HINCRBY user:1001 points 100
List(列表)
List 是一个双向链表,支持在两端进行插入和删除操作。
特点
- 有序集合
- 支持重复元素
- 双向操作
- 最大长度 2^32 - 1
常用命令
# 基本操作
LPUSH key element [element ...]
RPUSH key element [element ...]
LPOP key [count]
RPOP key [count]
LRANGE key start stop
# 列表管理
LLEN key
LINDEX key index
LINSERT key BEFORE|AFTER pivot element
LREM key count element
LSET key index element
LTRIM key start stop
应用场景
- 消息队列
- 任务队列
- 最新列表
- 栈和队列实现
示例
# 消息队列
LPUSH task:queue "task1" "task2" "task3"
RPOP task:queue
# 最新文章列表
LPUSH articles:latest "article1" "article2"
LRANGE articles:latest 0 9
# 用户行为记录
LPUSH user:1001:actions "login" "view_page" "purchase"
Set(集合)
Set 是一个无序的唯一元素集合。
特点
- 元素唯一
- 无序存储
- 支持集合运算
- 最大成员数 2^32 - 1
常用命令
# 基本操作
SADD key member [member ...]
SREM key member [member ...]
SMEMBERS key
SISMEMBER key member
SCARD key
# 集合运算
SINTER key [key ...]
SUNION key [key ...]
SDIFF key [key ...]
SINTERSTORE destination key [key ...]
SUNIONSTORE destination key [key ...]
SDIFFSTORE destination key [key ...]
# 随机操作
SRANDMEMBER key [count]
SPOP key [count]
应用场景
- 标签系统
- 好友关系
- 去重统计
- 权限管理
示例
# 标签系统
SADD article:123:tags "redis" "database" "nosql"
SADD user:1001:interests "programming" "music" "travel"
# 好友关系
SADD user:1001:friends "user:1002" "user:1003"
SINTER user:1001:friends user:1002:friends
# 去重统计
SADD unique:visitors "192.168.1.1" "192.168.1.2"
Sorted Set(有序集合)
Sorted Set 是一个有序的唯一元素集合,每个元素都有一个分数。
特点
- 元素唯一
- 按分数排序
- 支持范围查询
- 最大成员数 2^32 - 1
常用命令
# 基本操作
ZADD key [NX|XX] [CH] [INCR] score member [score member ...]
ZREM key member [member ...]
ZSCORE key member
ZRANK key member
ZREVRANK key member
# 范围查询
ZRANGE key start stop [WITHSCORES]
ZREVRANGE key start stop [WITHSCORES]
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]
# 统计操作
ZCARD key
ZCOUNT key min max
ZLEXCOUNT key min max
# 集合运算
ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
应用场景
- 排行榜
- 优先级队列
- 范围查询
- 时间线
示例
# 游戏排行榜
ZADD leaderboard 100 "player1" 200 "player2" 150 "player3"
ZREVRANGE leaderboard 0 9 WITHSCORES
# 优先级任务队列
ZADD tasks 1 "low_priority_task" 5 "high_priority_task" 3 "medium_priority_task"
ZRANGE tasks 0 -1
# 时间线
ZADD timeline 1640995200 "event1" 1640995300 "event2"
ZRANGEBYSCORE timeline 1640995200 1640995400
高级数据结构
Bitmap(位图)
Bitmap 是基于 String 类型的位操作,每个位只能是 0 或 1。
特点
- 基于 String 类型
- 位级操作
- 内存效率极高
- 支持位运算
常用命令
# 位操作
SETBIT key offset value
GETBIT key offset
BITCOUNT key [start end]
BITPOS key bit [start] [end]
# 位运算
BITOP AND destkey key [key ...]
BITOP OR destkey key [key ...]
BITOP XOR destkey key [key ...]
BITOP NOT destkey key
应用场景
- 用户签到
- 在线状态
- 布隆过滤器
- 大数据统计
示例
# 用户签到统计
SETBIT user:signin:2024:01:01 1001 1
SETBIT user:signin:2024:01:01 1002 1
BITCOUNT user:signin:2024:01:01
# 在线用户统计
SETBIT online:users 1001 1
GETBIT online:users 1001
HyperLogLog(基数统计)
HyperLogLog 是一种概率数据结构,用于估算集合的基数。
特点
- 固定内存使用(12KB)
- 误差率约 0.81%
- 支持合并操作
- 只支持添加和计数
常用命令
# 基本操作
PFADD key element [element ...]
PFCOUNT key [key ...]
PFMERGE destkey sourcekey [sourcekey ...]
应用场景
- 独立访客统计
- 去重计数
- 大数据分析
- 实时统计
示例
# 页面独立访客统计
PFADD page:home:visitors "user1" "user2" "user3"
PFADD page:home:visitors "user2" "user4" "user5"
PFCOUNT page:home:visitors
# 合并多个页面的访客统计
PFMERGE total:visitors page:home:visitors page:about:visitors
GEO(地理信息)
GEO 用于存储和查询地理位置信息。
特点
- 基于 Sorted Set 实现
- 支持距离计算
- 支持范围查询
- 使用 WGS84 坐标系
常用命令
# 地理位置操作
GEOADD key longitude latitude member [longitude latitude member ...]
GEOPOS key member [member ...]
GEODIST key member1 member2 [unit]
GEORADIUS key longitude latitude radius unit [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC]
GEORADIUSBYMEMBER key member radius unit [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC]
GEOHASH key member [member ...]
应用场景
- 附近的人
- 位置服务
- 地理围栏
- 距离计算
示例
# 添加城市坐标
GEOADD cities 116.3974 39.9093 "北京" 121.4737 31.2304 "上海"
# 计算距离
GEODIST cities "北京" "上海" km
# 查找附近城市
GEORADIUS cities 116.4 39.9 500 km WITHCOORD WITHDIST
Stream(流)
Stream 是 Redis 5.0 引入的数据类型,用于处理流数据。
特点
- 有序的日志数据结构
- 支持消费者组
- 持久化存储
- 支持范围查询
常用命令
# 流操作
XADD key ID field value [field value ...]
XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] ID [ID ...]
XRANGE key start end [COUNT count]
XREVRANGE key end start [COUNT count]
# 消费者组操作
XGROUP CREATE key groupname id-or-$ [MKSTREAM]
XREADGROUP GROUP group consumer [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] ID [ID ...]
XACK key group ID [ID ...]
应用场景
- 消息队列
- 事件溯源
- 日志收集
- 实时数据处理
示例
# 添加消息到流
XADD events * user_id 1001 action login timestamp 1640995200
# 创建消费者组
XGROUP CREATE events processors $ MKSTREAM
# 消费消息
XREADGROUP GROUP processors consumer1 COUNT 1 STREAMS events >
数据结构选择指南
根据使用场景选择
| 场景 | 推荐数据结构 | 原因 |
|---|---|---|
| 简单缓存 | String | 简单高效 |
| 对象存储 | Hash | 结构化存储 |
| 队列 | List | 有序操作 |
| 去重 | Set | 自动去重 |
| 排行榜 | Sorted Set | 有序且唯一 |
| 签到统计 | Bitmap | 内存效率高 |
| 独立访客 | HyperLogLog | 固定内存 |
| 地理位置 | GEO | 专业地理功能 |
| 消息流 | Stream | 流式处理 |
根据操作类型选择
| 操作类型 | 推荐数据结构 | 说明 |
|---|---|---|
| 简单读写 | String | O(1) 复杂度 |
| 部分更新 | Hash | 字段级操作 |
| 有序插入 | List | 双向链表 |
| 集合运算 | Set | 内置运算 |
| 范围查询 | Sorted Set | 分数排序 |
| 位操作 | Bitmap | 位级操作 |
| 基数统计 | HyperLogLog | 概率统计 |
| 地理查询 | GEO | 地理计算 |
性能对比
内存使用效率
| 数据结构 | 内存效率 | 说明 |
|---|---|---|
| String | 中等 | 简单存储 |
| Hash | 高 | 字段共享 |
| List | 低 | 链表开销 |
| Set | 中等 | 哈希表 |
| Sorted Set | 低 | 跳表+哈希表 |
| Bitmap | 极高 | 位级存储 |
| HyperLogLog | 极高 | 固定 12KB |
| GEO | 低 | 基于 Sorted Set |
操作复杂度
| 操作 | String | Hash | List | Set | Sorted Set |
|---|---|---|---|---|---|
| 插入 | O(1) | O(1) | O(1) | O(1) | O(log N) |
| 查询 | O(1) | O(1) | O(N) | O(1) | O(log N) |
| 删除 | O(1) | O(1) | O(N) | O(1) | O(log N) |
| 范围查询 | O(N) | O(N) | O(N) | O(N) | O(log N+M) |
最佳实践
1. 数据结构选择原则
- 简单优先:能用简单数据结构解决的不用复杂的
- 内存效率:考虑内存使用,选择合适的数据结构
- 操作复杂度:根据操作频率选择复杂度合适的数据结构
- 业务需求:根据具体业务场景选择最合适的数据结构
2. 性能优化建议
- 避免大对象:单个键值不要过大
- 合理使用过期时间:避免内存泄漏
- 批量操作:使用 MGET、MSET 等批量命令
- 管道操作:使用 Pipeline 减少网络往返
3. 内存优化技巧
- 使用 Hash 存储对象:比多个 String 键更节省内存
- 使用 Bitmap 进行统计:内存效率极高
- 合理设置过期时间:避免数据堆积
- 使用 HyperLogLog 进行基数统计:固定内存使用
4. 常见陷阱
- List 长度限制:注意 List 的最大长度
- Set 内存使用:大 Set 会消耗大量内存
- Sorted Set 分数精度:注意浮点数精度问题
- Bitmap 偏移量:注意偏移量的范围限制
5. 监控和维护
- 监控内存使用:定期检查内存使用情况
- 监控键数量:避免键数量过多
- 定期清理:清理过期和无用的数据
- 性能监控:监控各种操作的性能指标
总结
Redis 提供了丰富的数据结构,每种都有其特定的用途和优势。选择合适的数据结构对于构建高性能的 Redis 应用至关重要。在实际使用中,应该:
- 根据业务需求选择最合适的数据结构
- 考虑内存使用和操作复杂度
- 遵循最佳实践,避免常见陷阱
- 定期监控和维护 Redis 实例
通过合理使用这些数据结构,可以构建出高效、可扩展的 Redis 应用系统。
