Skip to content

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 60

1.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:task

2.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                  # 合并多个 HLL

7.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                                      # 获取 geohash

8.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可单独更新字段,节省内存
消息队列ListBRPOP 阻塞等待,天然队列
去重集合Set自动去重,支持交并差
排行榜ZSet按 score 排序,自动排名
签到统计Bitmap极省内存,位运算高效
UV 统计HyperLogLog12KB 搞定,误差 0.81%
附近的人GEO内置经纬度计算
分布式锁String + SETNX原子操作,支持过期