news 2026/7/1 16:44:35

redis数据类型及使用场景

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
redis数据类型及使用场景

redis数据类型及使用场景

一、数据类型与场景总览

数据类型底层结构核心特性典型场景性能边界
StringSDS / int / embstr二进制安全,最大 512MB缓存、计数、分布式锁、SessionO(1)
Hashziplist / hashtable字段级操作,节省内存对象缓存、购物车、配置存储O(1) 字段访问
Listquicklist双向链表,有序消息队列、时间线、栈/队列O(1) 头尾,O(N) 中间
Setintset / hashtable唯一性,集合运算标签、关注关系、抽奖、去重O(1) 增删查
ZSetskiplist + dict有序,按分数排名排行榜、延迟队列、滑动窗口限流O(log N)
Bitmapraw string位操作,极省空间签到、在线状态、布隆过滤器O(1) 位操作
HyperLogLogsparse / dense基数估算,固定 12KBUV 统计、海量去重计数O(1),误差 0.81%
Geosorted set经纬度存储,距离计算附近的人、位置服务O(log N) 添加
Streamradix tree + listpack持久化消息队列,消费者组事件溯源、消息队列、日志收集O(log N) 添加
JSONReJSON 模块路径查询,原子更新文档存储、配置管理、嵌套对象依赖路径深度

二、String:最基础也最万能

2.1 典型场景

┌─────────────────────────────────────────────────────────────────┐ │ 场景 1:对象缓存(KV 缓存) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ SET user:1001 '{"id":1001,"name":"Alice","level":5}' EX 3600 │ │ GET user:1001 │ │ │ │ 适用:用户信息、商品详情、配置项等读多写少的完整对象 │ │ 注意:Value 较大时(> 10KB),考虑 Hash 字段拆分或压缩 │ │ │ ├─────────────────────────────────────────────────────────────────┤ │ 场景 2:计数器(原子操作) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ INCR article:1001:views # 文章阅读量 +1 │ │ INCRBY user:1001:credits 10 # 用户积分 +10 │ │ DECR stock:sku:8888 # 库存扣减 │ │ │ │ 原子性保证:INCR/DECR 是单命令,无需事务 │ │ 注意:计数上限 2^63-1,溢出后报错 │ │ │ ├─────────────────────────────────────────────────────────────────┤ │ 场景 3:分布式锁(Redlock 算法) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ SET lock:order:1001 my-thread-id NX EX 30 │ │ # NX = 仅不存在时才设置(互斥) │ │ # EX 30 = 30 秒过期,防止死锁 │ │ │ │ 释放锁: │ │ if redis.call("get", KEYS[1]) == ARGV[1] then │ │ return redis.call("del", KEYS[1]) │ │ else return 0 end │ │ │ │ 陷阱:时钟漂移、主从切换导致锁丢失(Redisson 已解决) │ │ │ ├─────────────────────────────────────────────────────────────────┤ │ 场景 4:Session 存储 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ SET session:abc123 '{"userId":1001,"loginTime":...}' EX 1800 │ │ │ │ 替代 Tomcat 本地 Session,支持分布式部署 │ │ 配合 Spring Session 自动集成 │ │ │ └─────────────────────────────────────────────────────────────────┘

2.2 实战陷阱

陷阱说明解决
大 Key单个 String > 10MB拆分为 Hash 字段,或压缩(Snappy/LZ4)
热 Key单个 Key 被百万 QPS 访问本地缓存 + 随机后缀打散(如config:1~config:10
批量操作循环 GET 1000 次改用 MGET,或 Pipeline

三、Hash:对象字段级操作

3.1 典型场景

┌─────────────────────────────────────────────────────────────────┐ │ 场景 1:用户信息对象(替代 JSON String) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ String 方案(不好): │ │ SET user:1001 '{"name":"Alice","age":25,"city":"Beijing"}' │ │ 修改年龄:GET → 解析 JSON → 修改 → 序列化 → SET(全量覆盖) │ │ │ │ Hash 方案(推荐): │ │ HSET user:1001 name Alice age 25 city Beijing │ │ 修改年龄:HSET user:1001 age 26 # 仅修改字段,O(1) │ │ 获取城市:HGET user:1001 city │ │ │ │ 内存优势:ziplist 编码时,字段少且短比 String 省 50%+ 内存 │ │ 编码转换:字段数 > 512 或字段值 > 64B 时,ziplist → hashtable │ │ │ ├─────────────────────────────────────────────────────────────────┤ │ 场景 2:购物车(字段 = SKU,值 = 数量) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ HSET cart:user:1001 sku:8888 2 │ │ HSET cart:user:1001 sku:9999 1 │ │ HGETALL cart:user:1001 # 获取整个购物车 │ │ HINCRBY cart:user:1001 sku:8888 1 # 加购 │ │ HDEL cart:user:1001 sku:8888 # 删除商品 │ │ │ │ 过期控制:Hash 不支持整体过期,需配合顶层 Key 或使用 String JSON │ │ │ ├─────────────────────────────────────────────────────────────────┤ │ 场景 3:配置项分组存储 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ HSET config:db host localhost port 3306 user admin │ │ HSET config:cache ttl 3600 maxSize 10000 │ │ │ │ 优势:配置按组管理,批量读取(HGETALL)或单字段更新 │ │ │ └─────────────────────────────────────────────────────────────────┘

3.2 Hash vs String 选型决策

┌─────────────────────────────────────────────────────────────────┐ │ 决策树:对象缓存选 Hash 还是 String? │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 对象字段是否经常单独更新? │ │ │ │ │ YES → 用 Hash(HSET/HGET 字段级操作) │ │ │ │ │ NO → 对象是否很大(> 10KB)? │ │ │ │ │ YES → 用 String + 压缩(Snappy) │ │ │ │ │ NO → 是否需要整体过期? │ │ │ │ │ YES → String(SET EX 原子过期) │ │ │ │ │ NO → Hash(内存更省) │ │ │ └─────────────────────────────────────────────────────────────────┘

四、List:有序队列

4.1 典型场景

┌─────────────────────────────────────────────────────────────────┐ │ 场景 1:消息队列(轻量级,非高可靠场景) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 生产者:LPUSH queue:email '{"to":"user@x.com","content":"..."}' │ │ 消费者:BRPOP queue:email 30 # 阻塞等待 30 秒 │ │ │ │ 特点: │ │ - 单消费者:LPUSH + BRPOP 简单可靠 │ │ - 多消费者:BRPOP 竞争消费,每条消息只被一个消费者处理 │ │ - 无 ACK 机制:消费者崩溃消息丢失(对比 Stream 的消费者组) │ │ │ │ 适用:日志收集、异步任务、通知推送(允许少量丢失) │ │ 不适用:金融交易、订单处理(需 Stream 或专业 MQ) │ │ │ ├─────────────────────────────────────────────────────────────────┤ │ 场景 2:时间线/Feed 流(最新 N 条) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ LPUSH timeline:user:1001 '{"id":123,"text":"hello"}' │ │ LTRIM timeline:user:1001 0 99 # 只保留最新 100 条 │ │ LRANGE timeline:user:1001 0 19 # 获取首页 20 条 │ │ │ │ 特点:O(1) 头尾操作,LTRIM 原子裁剪 │ │ 对比:ZSet 可实现按时间分数排序,但 List 更轻量 │ │ │ ├─────────────────────────────────────────────────────────────────┤ │ 场景 3:栈(Stack)和队列(Queue) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 栈(后进先出):LPUSH + LPOP │ │ 队列(先进先出):LPUSH + BRPOP │ │ 双端队列:LPUSH/RPUSH + LPOP/RPOP │ │ │ └─────────────────────────────────────────────────────────────────┘

4.2 List 的陷阱

陷阱说明解决
LINDEX/LSET 中间操作O(N) 遍历,大 List 性能灾难避免,或用 ZSet
无 ACK 机制消费者崩溃消息丢失重要业务用 Stream
阻塞连接占用BRPOP 长时间阻塞消耗连接池控制超时,或使用 Stream

五、Set:唯一集合

5.1 典型场景

┌─────────────────────────────────────────────────────────────────┐ │ 场景 1:标签系统(Tag) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ SADD article:1001:tags java redis distributed-system │ │ SADD article:1002:tags java spring microservice │ │ │ │ 查找共同标签:SINTER article:1001:tags article:1002:tags │ │ → {"java"} │ │ │ │ 查找文章 1001 的所有标签:SMEMBERS article:1001:tags │ │ │ │ 查找有 "redis" 标签的所有文章(反向索引): │ │ SADD tag:redis article:1001 article:1005 article:1010 │ │ │ ├─────────────────────────────────────────────────────────────────┤ │ 场景 2:关注关系(社交图谱) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ user:1001 关注了 user:1002, 1003, 1004 │ │ SADD following:user:1001 1002 1003 1004 │ │ │ │ user:1002 的粉丝:SADD followers:user:1002 1001 1005 │ │ │ │ 共同关注:SINTER following:user:1001 following:user:1002 │ │ 可能认识:SDIFF following:user:1002 following:user:1001 │ │ (user:1002 关注但 user:1001 没关注的) │ │ │ │ 注意:大数据量时(百万粉丝),Set 内存消耗大,考虑: │ │ - 分片:followers:user:1002:0, followers:user:1002:1... │ │ - 或改用 ZSet(带时间分数,支持范围查询) │ │ │ ├─────────────────────────────────────────────────────────────────┤ │ 场景 3:抽奖/随机选取(SRANDMEMBER/SPOP) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ SADD lottery:pool user1 user2 user3 ... user10000 │ │ SRANDMEMBER lottery:pool 3 # 随机抽 3 个(不删除) │ │ SPOP lottery:pool 3 # 随机抽 3 个(从池子移除) │ │ │ │ 适用:抽奖、随机推荐、A/B 测试分组 │ │ │ ├─────────────────────────────────────────────────────────────────┤ │ 场景 4:UV 去重(配合 HyperLogLog 的精确版) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ SADD uv:2024-01-01 user1 user2 user3 ... │ │ SCARD uv:2024-01-01 # 精确 UV 数 │ │ │ │ 内存问题:百万 UV = 百万个字符串,内存爆炸 │ │ 优化:用 HyperLogLog 估算(12KB 固定内存,误差 0.81%) │ │ │ └─────────────────────────────────────────────────────────────────┘

六、ZSet:有序集合(最强大类型)

6.1 典型场景

┌─────────────────────────────────────────────────────────────────┐ │ 场景 1:排行榜(Leaderboard) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ZADD leaderboard 1500 "player:Alice" │ │ ZADD leaderboard 2300 "player:Bob" │ │ ZADD leaderboard 1800 "player:Carol" │ │ │ │ 获取前 10 名:ZREVRANGE leaderboard 0 9 WITHSCORES │ │ → Bob(2300), Carol(1800), Alice(1500)... │ │ │ │ 获取 Alice 的排名:ZREVRANK leaderboard "player:Alice" │ │ → 2(从 0 开始) │ │ │ │ 获取 Alice 的分数:ZSCORE leaderboard "player:Alice" │ │ → 1500 │ │ │ │ 更新分数:ZINCRBY leaderboard 100 "player:Alice" │ │ │ ├─────────────────────────────────────────────────────────────────┤ │ 场景 2:延迟队列(Delayed Queue) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 分数 = 执行时间戳(毫秒) │ │ ZADD delay:queue 1705312800000 "task:sendEmail:1001" │ │ ZADD delay:queue 1705316400000 "task:refund:2002" │ │ │ │ 定时轮询(每秒): │ │ ZRANGEBYSCORE delay:queue 0 1705312805000 LIMIT 0 1 │ │ # 获取当前时间前到期的任务 │ │ │ │ 取出执行:ZPOPMIN delay:queue 1 # 原子弹出最小分数 │ │ │ │ 对比 List 的 BRPOP:ZSet 支持任意时间精度,List 只能 FIFO │ │ │ ├─────────────────────────────────────────────────────────────────┤ │ 场景 3:滑动窗口限流(Sliding Window Rate Limit) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 记录用户请求时间戳: │ │ ZADD ratelimit:user:1001 1705312800000 "req:1" │ │ ZADD ratelimit:user:1001 1705312800100 "req:2" │ │ ZADD ratelimit:user:1001 1705312800200 "req:3" │ │ │ │ 清理过期窗口(60 秒前): │ │ ZREMRANGEBYSCORE ratelimit:user:1001 0 1705312740000 │ │ │ │ 检查窗口内请求数: │ │ ZCARD ratelimit:user:1001 # < 100 则允许,否则拒绝 │ │ │ │ 优势:精确滑动窗口,而非粗糙的固定窗口 │ │ 注意:高并发时 ZADD + ZREMRANGEBYSCORE + ZCARD 非原子,需 Lua 脚本 │ │ │ ├─────────────────────────────────────────────────────────────────┤ │ 场景 4:地理位置(Geo,底层是 ZSet) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ GEOADD locations 116.40 39.90 "Beijing" │ │ GEOADD locations 121.47 31.23 "Shanghai" │ │ GEOADD locations 113.26 23.13 "Guangzhou" │ │ │ │ 获取北京坐标:GEOPOS locations "Beijing" │ │ │ │ 计算北京到上海距离:GEODIST locations "Beijing" "Shanghai" km │ │ │ │ 查找北京 500km 内的城市: │ │ GEORADIUS locations 116.40 39.90 500 km WITHDIST WITHCOORD │ │ → 北京(0km), 天津(110km)... │ │ │ │ 原理:GeoHash 编码为 52 位整数作为 ZSet 分数,范围查询即地理范围 │ │ │ └─────────────────────────────────────────────────────────────────┘

七、Bitmap:位图(极致空间效率)

7.1 典型场景

┌─────────────────────────────────────────────────────────────────┐ │ 场景 1:用户签到(365 天仅需 46 字节/用户) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 第 1 天签到:SETBIT sign:user:1001 0 1 │ │ 第 2 天签到:SETBIT sign:user:1001 1 1 │ │ 第 365 天签到:SETBIT sign:user:1001 364 1 │ │ │ │ 检查第 100 天是否签到:GETBIT sign:user:1001 99 │ │ 统计本月签到天数:BITCOUNT sign:user:1001 0 30 │ │ │ │ 内存:1 亿用户 × 365 天 / 8 = 4.56GB(对比 MySQL 存储省 100 倍) │ │ │ ├─────────────────────────────────────────────────────────────────┤ │ 场景 2:在线状态(亿级用户) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 用户 1001 上线:SETBIT online:20240101 1001 1 │ │ 用户 1001 下线:SETBIT online:20240101 1001 0 │ │ │ │ 统计今日在线人数:BITCOUNT online:20240101 │ │ 检查用户是否在线:GETBIT online:20240101 1001 │ │ │ │ 注意:用户 ID 必须连续或映射为连续整数,否则稀疏 Bitmap 浪费空间 │ │ 稀疏优化:Redis 4.0+ 引入 BITFIELD 或改用 Roaring Bitmap(外部) │ │ │ ├─────────────────────────────────────────────────────────────────┤ │ 场景 3:布隆过滤器(Bloom Filter) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 原理:多个 Hash 函数映射到 Bitmap 位,判断"可能存在"或"肯定不存在" │ │ │ │ 添加元素: │ │ for hash in hashFunctions(element): │ │ SETBIT bloom:filter hash 1 │ │ │ │ 检查存在: │ │ for hash in hashFunctions(element): │ │ if GETBIT bloom:filter hash == 0: return False │ │ return True # 可能存在(有误判),或肯定存在 │ │ │ │ 误判率:位数组越大、Hash 函数越多,误判率越低 │ │ 适用:缓存穿透防护、URL 去重、垃圾邮件过滤 │ │ Redis 4.0+ 原生模块:RedisBloom │ │ │ └─────────────────────────────────────────────────────────────────┘

八、HyperLogLog:基数估算

8.1 典型场景

┌─────────────────────────────────────────────────────────────────┐ │ 场景:海量 UV 统计(误差可接受) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 问题:1 亿独立访客,用 Set 存储 → 内存 > 1GB │ │ │ │ HyperLogLog 方案: │ │ PFADD uv:2024-01-01 user1 user2 user3 ... user100000000 │ │ PFCOUNT uv:2024-01-01 │ │ → 100023456(真实值 100000000,误差 0.023%) │ │ │ │ 内存:固定 12KB,与数据量无关! │ │ │ │ 合并多天的 UV(去重总 UV): │ │ PFMERGE uv:total uv:2024-01-01 uv:2024-01-02 uv:2024-01-03 │ │ PFCOUNT uv:total │ │ │ │ 适用:网站 UV、广告曝光去重、搜索关键词去重 │ │ 不适用:精确计数(如订单金额统计) │ │ │ └─────────────────────────────────────────────────────────────────┘

九、Stream:持久化消息队列

9.1 典型场景

┌─────────────────────────────────────────────────────────────────┐ │ 场景:事件溯源 + 消费者组(替代 List 做可靠队列) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 生产者: │ │ XADD orders * userId 1001 amount 199.99 status created │ │ → 返回消息 ID:1705312800000-0 │ │ │ │ 消费者组(多个消费者并行处理,不重复消费): │ │ XGROUP CREATE orders payment-group $ MKSTREAM │ │ │ │ 消费者 1: │ │ XREADGROUP GROUP payment-group consumer-1 COUNT 1 STREAMS orders >│ │ → 读取新消息,处理支付 │ │ XACK orders payment-group 1705312800000-0 # 确认已处理 │ │ │ │ 消费者 2: │ │ XREADGROUP GROUP payment-group consumer-2 COUNT 1 STREAMS orders >│ │ → 读取下一条新消息 │ │ │ │ 未确认消息检查(处理超时重试): │ │ XPENDING orders payment-group │ │ → 查看哪些消息已分配但未 ACK │ │ │ │ 对比 Kafka: │ │ - Stream:轻量,无需外部依赖,适合中小规模 │ │ - Kafka:高吞吐、持久化、分区副本,适合大规模 │ │ │ └─────────────────────────────────────────────────────────────────┘

十、选型决策树

┌─────────────────────────────────────────────────────────────────┐ │ Redis 数据类型选型决策树 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 需要消息队列 + 消费者组 + ACK? │ │ │ │ │ YES → Stream │ │ │ │ │ NO → 需要按分数排序/范围查询? │ │ │ │ │ YES → ZSet(排行榜、延迟队列、滑动窗口) │ │ │ │ │ NO → 需要地理位置查询? │ │ │ │ │ YES → Geo(底层 ZSet) │ │ │ │ │ NO → 需要集合运算(交并差)? │ │ │ │ │ YES → Set(标签、关系、抽奖) │ │ │ │ │ NO → 需要字段级更新? │ │ │ │ │ YES → Hash(对象缓存、购物车) │ │ │ │ │ NO → 需要精确去重计数? │ │ │ │ │ YES → Set(小数据) │ │ │ HyperLogLog(大数据)│ │ │ │ │ NO → 需要位操作? │ │ │ │ │ YES → Bitmap │ │ │ │ │ NO → 需要队列语义?│ │ │ │ │ YES → List│ │ │ │ │ NO → String│ │ │ └─────────────────────────────────────────────────────────────────┘

十一、一句话总结

String是万能缓存,Hash做对象字段级操作,List做轻量队列,Set做关系与去重,ZSet是时间/分数驱动的核心引擎,Bitmap/HyperLogLog用极致空间换统计能力,Stream是原生持久化消息队列。架构师选型时,先问"是否需要排序",再问"是否需要精确",最后才考虑"是否足够简单"。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/1 16:43:17

Apache JMeter 5.4.1性能测试实战:从核心原理到分布式压测

1. 项目概述&#xff1a;从“会用”到“精通”的性能测试实战如果你正在为你的Web应用、API接口或者数据库服务寻找一个可靠的压力测试工具&#xff0c;那么Apache JMeter这个名字你肯定不陌生。它开源、免费、功能强大&#xff0c;几乎是性能测试工程师和开发者的标配工具。但…

作者头像 李华
网站建设 2026/7/1 16:42:26

ChatLog:三分钟解锁QQ群聊天记录的数据洞察力

ChatLog&#xff1a;三分钟解锁QQ群聊天记录的数据洞察力 【免费下载链接】chatLog QQ群聊天记录分析 项目地址: https://gitcode.com/gh_mirrors/ch/chatLog 你是否曾好奇&#xff0c;在那些热闹的QQ群里&#xff0c;谁才是真正的"话痨之王"&#xff1f;哪些…

作者头像 李华
网站建设 2026/7/1 16:41:28

两节串联锂电池充电管理芯片IC-5V USB升压充电方案对比

7.4V/8.4V 双节串联锂电池充电管理芯片方案实测整理升压 / 降压 / 升降压拓扑一次说清&#xff0c;附电路图与 BOM 清单做两节串联锂电&#xff08;7.4V/8.4V&#xff09;产品&#xff0c;保护板只是基础&#xff0c;充电电路才是真正让人挠头的地方。5V USB 怎么给 8.4V 电池包…

作者头像 李华
网站建设 2026/7/1 16:37:03

STM32F407VGT6扩展EEPROM存储方案与实现

1. 为什么需要为STM32F407VGT6扩展存储空间&#xff1f;STM32F407VGT6作为一款基于ARM Cortex-M4内核的微控制器&#xff0c;内置了1MB Flash和192KB SRAM。这个配置对于大多数嵌入式应用来说已经相当不错&#xff0c;但在以下场景中可能会遇到存储瓶颈&#xff1a;数据采集系统…

作者头像 李华
网站建设 2026/7/1 16:35:33

3个简单步骤:用ImDisk虚拟磁盘彻底改变你的Windows文件管理方式

3个简单步骤&#xff1a;用ImDisk虚拟磁盘彻底改变你的Windows文件管理方式 【免费下载链接】ImDisk ImDisk Virtual Disk Driver 项目地址: https://gitcode.com/gh_mirrors/im/ImDisk 还在为频繁插拔U盘、光盘而烦恼吗&#xff1f;是否经常需要处理ISO镜像文件却找不到…

作者头像 李华
网站建设 2026/7/1 16:34:47

无犯罪证明公证需要多长时间?无犯罪证明公证有效期多久?

准备出国签证、境外务工、海外移民或是国内入职特殊岗位的人群&#xff0c;都会碰到办理无犯罪证明公证的需求&#xff0c;大家先纠结两件核心问题&#xff1a;一是整套公证流程要等多久&#xff0c;二是办好的公证书能放多久不会失效。一、无犯罪证明公证需要多长时间&#xf…

作者头像 李华