Appearance
Redis 数据结构详解
Redis 不仅是 key-value 存储,它提供了丰富的数据结构,每种结构都有独特的底层实现和适用场景。掌握这些数据结构,才能真正用好 Redis。
一、String(字符串)
1.1 基本操作
bash
SET key value [EX seconds] [PX milliseconds] [NX|XX]
GET key
GETSET key value # 设置新值返回旧值
SETNX key value # 不存在才设置(分布式锁基础)
SETEX key seconds value # 设置并指定过期时间
MSET k1 v1 k2 v2 # 批量设置
MGET k1 k2 # 批量获取
INCR key # 自增 1
INCRBY key 100 # 自增指定值
DECR key # 自减 1
APPEND key value # 追加
STRLEN key # 字符串长度1.2 使用场景
bash
# 缓存
SET user:1001 '{"name":"张三","age":25}' EX 3600
# 分布式锁
SET lock:order:1001 uuid NX EX 30
# 计数器
INCR article:1001:views
# 限流
INCR rate:user:1001
EXPIRE rate:user:1001 601.3 底层实现:SDS
String 底层用 SDS(Simple Dynamic String)而非 C 原生字符串,详见"底层数据结构"章节。
二、List(列表)
2.1 基本操作
bash
LPUSH key value # 左侧插入
RPUSH key value # 右侧插入
LPOP key # 左侧弹出
RPOP key # 右侧弹出
LRANGE key 0 -1 # 获取所有元素
LLEN key # 列表长度
LINDEX key 0 # 按索引获取
LTRIM key 0 99 # 只保留前 100 个元素
BRPOP key timeout # 阻塞弹出(消息队列基础)2.2 使用场景
bash
# 消息队列(生产者-消费者)
RPUSH queue:order "order:1001" # 生产者
BRPOP queue:order 30 # 消费者(阻塞等待)
# 最新列表(时间线)
LPUSH news:latest "新闻标题"
LTRIM news:latest 0 99 # 只保留最新 100 条
# 栈
LPUSH stack:task "task1"
LPOP stack:task2.3 底层实现:quicklist
Redis 3.2 后 List 统一使用 quicklist(ziplist 的链表),详见"底层数据结构"章节。
三、Set(集合)
3.1 基本操作
bash
SADD key member [member...] # 添加
SREM key member # 删除
SISMEMBER key member # 判断是否存在
SMEMBERS key # 获取所有成员
SCARD key # 元素个数
SRANDMEMBER key [count] # 随机获取(抽奖)
SPOP key # 随机弹出
# 集合运算
SINTER k1 k2 # 交集(共同好友)
SUNION k1 k2 # 并集
SDIFF k1 k2 # 差集(k1 有 k2 无)
SINTERSTORE dst k1 k2 # 交集结果存入新集合3.2 使用场景
bash
# 标签系统
SADD article:1001:tags "Java" "Redis" "微服务"
SADD article:1002:tags "Java" "Spring"
# 共同标签
SINTER article:1001:tags article:1002:tags → "Java"
# 抽奖
SADD lottery:2024 user:1 user:2 user:3 ... user:1000
SPOP lottery:2024 → 随机抽一个
# 点赞(去重)
SADD like:article:1001 user:101
SCARD like:article:1001 → 点赞数
SISMEMBER like:article:1001 user:101 → 是否点过赞
# 共同关注
SINTER follow:user:1 follow:user:2四、ZSet(有序集合)
4.1 基本操作
bash
ZADD key score member [score member...] # 添加
ZREM key member # 删除
ZSCORE key member # 获取分数
ZRANK key member # 获取排名(升序)
ZREVRANK key member # 获取排名(降序)
ZRANGE key 0 9 WITHSCORES # 按排名获取(升序)
ZREVRANGE key 0 9 WITHSCORES # 按排名获取(降序)
ZRANGEBYSCORE key min max # 按分数范围获取
ZCOUNT key min max # 分数范围内的数量
ZINCRBY key 10 member # 增加分数
ZCARD key # 元素个数
ZREMRANGEBYRANK key 0 99 # 删除排名范围4.2 使用场景
bash
# 排行榜
ZADD rank:game 1000 "player1" 900 "player2" 800 "player3"
ZREVRANGE rank:game 0 9 WITHSCORES # Top 10
ZRANK rank:game "player1" # player1 排名
ZINCRBY rank:game 50 "player1" # 加分
# 延时队列
ZADD delay:order (当前时间+延迟秒数) "order:1001"
# 定时任务扫描 0 到当前时间的元素
ZRANGEBYSCORE delay:order 0 (当前时间)4.3 底层实现:skiplist + dict
ZSet 同时使用跳表(按 score 排序)和字典(按 member 查 score),详见"底层数据结构"章节。
五、Hash(哈希)
5.1 基本操作
bash
HSET key field value # 设置单个字段
HMSET key f1 v1 f2 v2 # 设置多个字段
HGET key field # 获取单个字段
HMGET key f1 f2 # 获取多个字段
HGETALL key # 获取所有字段
HDEL key field # 删除字段
HEXISTS key field # 判断字段是否存在
HINCRBY key field 1 # 字段自增
HLEN key # 字段数量
HKEYS key # 所有字段名
HVALS key # 所有字段值5.2 使用场景
bash
# 对象存储
HSET user:1001 name "张三" age 25 email "[email protected]"
HGET user:1001 name → "张三"
HINCRBY user:1001 age 1 → 26
# 购物车
HSET cart:user:1001 product:101 2 # 商品 101,数量 2
HSET cart:user:1001 product:102 1 # 商品 102,数量 1
HINCRBY cart:user:1001 product:101 1 # 加 1 件
HDEL cart:user:1001 product:102 # 删除商品六、Bitmap(位图)
6.1 基本操作
bash
SETBIT key offset value # 设置位
GETBIT key offset # 获取位
BITCOUNT key [start end] # 统计 1 的个数
BITOP AND/OR/XOR/NOT dest k1 k2 # 位运算
BITPOS key bit [start end] # 第一个 0/1 的位置6.2 使用场景
bash
# 用户签到(一年 365 天,只需 46 字节)
SETBIT sign:user:1001:2024 0 1 # 第 1 天签到
SETBIT sign:user:1001:2024 100 1 # 第 101 天签到
BITCOUNT sign:user:1001:2024 # 签到天数
# 用户在线状态(1 亿用户只需 12MB)
SETBIT online:users 1001 1 # 用户 1001 上线
SETBIT online:users 1001 0 # 用户 1001 下线
BITCOUNT online:users # 在线用户数
# 布隆过滤器(配合多个哈希函数)
SETBIT bloom:filter hash1(value) 1
SETBIT bloom:filter hash2(value) 1
SETBIT bloom:filter hash3(value) 1七、HyperLogLog(基数统计)
7.1 基本操作
bash
PFADD key element [element...] # 添加元素
PFCOUNT key # 估算基数
PFMERGE dest k1 k2 # 合并多个 HLL7.2 使用场景
bash
# UV 统计(12KB 内存,误差 0.81%)
PFADD uv:article:1001 user:101 user:102 user:103
PFCOUNT uv:article:1001 → 3
# 合并多天 UV
PFADD uv:2024-01-01 user:1 user:2 user:3
PFADD uv:2024-01-02 user:2 user:3 user:4
PFMERGE uv:2024-01-01-02 uv:2024-01-01 uv:2024-01-02
PFCOUNT uv:2024-01-01-02 → 4(自动去重)注意: HyperLogLog 是概率算法,标准误差 0.81%,不能获取具体元素,只能统计基数。
八、GEO(地理位置)
8.1 基本操作
bash
GEOADD key longitude latitude member # 添加位置
GEOPOS key member # 获取位置
GEODIST key m1 m2 km # 计算距离
GEORADIUS key lng lat radius km [WITHDIST] [ASC|DESC] # 附近搜索
GEORADIUSBYMEMBER key member radius km # 以成员为中心搜索
GEOHASH key member # 获取 geohash8.2 使用场景
bash
# 添加商家位置
GEOADD shops 116.404 39.915 "shop:1" # 北京
GEOADD shops 121.473 31.230 "shop:2" # 上海
# 附近商家
GEORADIUS shops 116.404 39.915 5 km WITHDIST ASC
# → shop:1 距离 0.0km, shop:3 距离 2.5km ...
# 计算距离
GEODIST shops shop:1 shop:2 km
# → 1068.5 km底层原理: GEO 使用 ZSet 存储,经纬度通过 Geohash 算法编码为 52 位整数作为 score。
九、数据结构选型指南
| 需求 | 选型 | 理由 |
|---|---|---|
| 简单缓存 | String | 最简单,GET/SET 即可 |
| 对象存储 | Hash | 可单独更新字段,节省内存 |
| 消息队列 | List | BRPOP 阻塞等待,天然队列 |
| 去重集合 | Set | 自动去重,支持交并差 |
| 排行榜 | ZSet | 按 score 排序,自动排名 |
| 签到统计 | Bitmap | 极省内存,位运算高效 |
| UV 统计 | HyperLogLog | 12KB 搞定,误差 0.81% |
| 附近的人 | GEO | 内置经纬度计算 |
| 分布式锁 | String + SETNX | 原子操作,支持过期 |
