当前位置: 首页 > article >正文

Redis 各数据类型使用场景详解

1. 字符串(String)

场景 1:计数器(如文章阅读量)

  • 问题

    • 高并发下对同一数值进行增减操作时,需保证原子性,避免竞态条件导致数据不一致。

    • 频繁读写可能成为性能瓶颈。

  • 解决方案

    • INCR/DECR 命令:Redis 的原子操作确保增减操作的线程安全,无需额外锁机制。

    • 内存存储:数据直接存储在内存中,读写速度极快(微秒级响应)。

场景 2:分布式锁

  • 问题

    • 多节点服务竞争同一资源时,需确保同一时间只有一个客户端持有锁。

    • 锁需支持自动释放(如超时),避免死锁。

  • 解决方案

    • SETNX + EXPIR

      SET lock:order_123 "client1" NX EX 30  # 设置锁,30秒后自动过期
    • 通过原子性命令确保锁的唯一性,过期时间防止客户端崩溃导致锁无法释放。


2. 列表(List)

场景 1:消息队列(生产者-消费者模型)

  • 问题

    • 多生产者/消费者场景下,需保证消息的顺序性和可靠性。

    • 消费者轮询队列会浪费资源(如空轮询)。

  • 解决方案

    • LPUSH + BRPOP

      • 生产者使用 LPUSH 将消息推入队列头部。

      • 消费者使用 BRPOP 阻塞式地从队列尾部获取消息,避免空轮询。

    • 示例

      LPUSH task_queue "task_data"  # 生产者推送任务
      BRPOP task_queue 30          # 消费者阻塞30秒等待任务

场景 2:最新动态列表(如用户最近10条动态)

  • 问题

    • 动态需按时间倒序展示,且需限制列表长度(如只保留最新100条)。

    • 频繁插入可能导致内存占用过高。

  • 解决方案

    • LPUSH + LTRIM

      LPUSH user:1:feeds "new_feed"  # 插入新动态
      LTRIM user:1:feeds 0 99        # 保留最新的100条
    • 通过 LTRIM 自动裁剪旧数据,控制内存使用。


3. 哈希(Hash)

场景 1:存储用户信息(对象缓存)

  • 问题

    • 对象字段较多时,若用多个 String 存储,会产生大量键,增加内存和管理成本。

    • 部分更新需频繁序列化/反序列化整个对象。

  • 解决方案

    • HMSET/HGETALL

      HMSET user:1001 name "Alice" age 30 email "alice@example.com"
      HGETALL user:1001  # 获取所有字段
    • 哈希表将对象字段存储为单键下的多个字段,减少键数量,支持按字段读写。

场景 2:商品库存管理

  • 问题

    • 高并发下单时,需保证库存扣减的原子性。

    • 扣减后需实时更新库存值。

  • 解决方案

    • HINCRBY 命令

      HINCRBY product:1001 stock -1  # 原子性扣减库存
    • 哈希表结合 HINCRBY 实现原子性库存操作,避免超卖。


4. 集合(Set)

场景 1:用户标签系统

  • 问题

    • 用户标签需唯一且无序,便于快速查询共同标签或去重。

    • 多标签集合操作(如交集、并集)需高效执行。

  • 解决方案

    • SADD + SINTER

      SADD user:1:tags "tech" "music"  # 添加标签
      SINTER user:1:tags user:2:tags   # 获取共同标签
    • 集合的 SINTER 直接计算交集,复杂度为 O(N*M),但内存中操作极快。

场景 2:唯一访问用户统计(去重)

  • 问题

    • 同一用户多次访问同一资源时需去重统计。

    • 直接记录用户ID可能导致重复存储。

  • 解决方案

    • SADD + SCARD

      SADD article:1001:views "user:2023"  # 用户访问时添加
      SCARD article:1001:views             # 获取唯一用户数
    • 集合自动去重,SCARD 直接返回唯一用户总数。


5. 有序集合(Sorted Set)

场景 1:游戏积分排行榜

  • 问题

    • 玩家积分需实时排序,支持按名次快速查询。

    • 积分更新频繁,需保证排序的高效性。

  • 解决方案

    • ZADD + ZREVRANGE

      ZADD leaderboard 1000 "player1"  # 添加/更新玩家积分
      ZREVRANGE leaderboard 0 9 WITHSCORES  # 获取前十名
    • 有序集合内部通过跳表(Skip List)实现,插入和查询时间复杂度为 O(log N)。

场景 2:延迟队列(按时间戳调度任务)

  • 问题

    • 任务需在指定时间执行,需按时间顺序处理。

    • 轮询未到期的任务会浪费资源。

  • 解决方案

    • ZADD + ZRANGEBYSCORE

      ZADD delay_queue 1630000000 "task_data"  # 添加任务,分数为执行时间戳
      ZRANGEBYSCORE delay_queue 0 <current_timestamp>  # 获取所有到期任务
    • 消费者定期查询分数小于当前时间戳的任务并处理。


6. 扩展数据类型

场景 1:用户签到统计(Bitmap)

  • 问题

    • 每日签到状态只需记录是否签到(0/1),使用字符串浪费内存。

    • 统计连续签到天数需高效计算。

  • 解决方案

    • SETBIT + BITCOUNT

      SETBIT user:1001:sign:2023:10 5 1  # 10月第6天签到(偏移量从0开始)
      BITCOUNT user:1001:sign:2023:10     # 统计10月签到次数
    • 位图每个位表示一天,每月31天仅需4字节,内存占用极低。

场景 2:独立访客统计(HyperLogLog)

  • 问题

    • 海量用户访问记录需统计唯一用户数,精确统计内存占用过高。

    • 允许一定误差(如0.81%)。

  • 解决方案

    • PFADD + PFCOUNT

      PFADD uv:2023:10:01 "user1" "user2"  # 记录UV
      PFCOUNT uv:2023:10:01                # 估算UV数
    • HyperLogLog 固定使用12KB内存,可统计上亿唯一值。

场景 3:附近商家查询(Geospatial)

  • 问题

    • 需根据经纬度快速检索附近一定范围内的商家。

    • 直接计算距离性能差(需遍历所有点)。

  • 解决方案

    • GEOADD + GEORADIUS

      GEOADD shops 116.405285 39.904989 "shop1"  # 添加商家坐标
      GEORADIUS shops 116.40 39.90 10 km         # 查询10公里内的商家
    • 底层使用有序集合存储坐标(经度+纬度编码为分数),支持快速范围查询。

场景 4:订单事件日志(Stream)

  • 问题

    • 订单状态变更需记录完整事件流,供后续审计或重放。

    • 多消费者需独立消费同一消息流(如支付服务和库存服务)。

  • 解决方案

    • XADD + 消费者组(Consumer Group)

      XADD order:1001:events * status "paid" amount 200  # 添加事件
      XGROUP CREATE order:1001:events group1 0           # 创建消费者组
      XREADGROUP GROUP group1 consumer1 STREAMS order:1001:events >  # 消费消息
    • Stream 支持多消费者组、消息确认和回溯,类似 Kafka 的日志机制。


总结

数据结构核心问题Redis的解决方案
String并发计数、分布式锁原子操作(INCR)、SETNX + 过期时间
List消息顺序性、动态列表长度控制阻塞弹出(BRPOP)、LPUSH + LTRIM
Hash对象字段管理、部分更新字段级读写(HSET/HGET
Set去重、集合运算唯一性存储、SINTER/SUNION
Sorted Set按权重排序、范围查询跳表排序、ZRANGEBYSCORE
Bitmap二值状态统计(如签到)位操作(SETBIT/BITCOUNT
HyperLogLog海量数据去重统计(允许误差)固定内存估算基数(PFADD/PFCOUNT
Geospatial地理位置检索有序集合编码坐标(GEORADIUS
Stream消息多消费者组、事件溯源消费者组、消息ID序列(XREADGROUP

关键原则

  1. 选择最匹配业务需求的数据结构,避免用 String 存储复杂对象。

  2. 优先利用 Redis 原子操作,减少客户端复杂逻辑。

  3. 关注内存效率(如小对象用 Hash,二值数据用 Bitmap)。


http://www.kler.cn/a/576855.html

相关文章:

  • Scaling Laws for Neural Language Models
  • DIY Tomcat:手写一个简易Servlet容器
  • HTTPS安全通信协议原理
  • 什么是 Java 的 Timer?
  • opentitan riscv
  • 关于父组件向子组件传值的形式(类型一)
  • 【商城实战(13)】购物车价格与数量的奥秘
  • 【leetcode hot 100 142】环形链表Ⅱ
  • webflux响应式编程
  • Windows、macOS和Linux系统的统计文件夹下的文件数量的方法
  • 笔记:代码随想录算法训练营day38: LeetCode322. 零钱兑换、279.完全平方数、139.单词拆分;多重背包
  • 数学建模:MATLAB强化学习
  • MacBook上API调⽤⼯具推荐
  • 线性代数笔记28--奇异值分解(SVD)
  • 【从零开始学习计算机科学】数字逻辑(五) Verilog HDL语言
  • Lab17_ Blind SQL injection with out-of-band data exfiltration
  • MTK Android12 桌面上显示文件管理器图标
  • 深入剖析 ConcurrentHashMap:高并发场景下的高效哈希表
  • 查看k8s集群的资源使用情况
  • Azure云生态系统详解:核心服务、混合架构与云原生概念