Redis数据结构:别小看这5把“瑞士军刀”,用好了性能飙升!

简介: Redis提供5种基础数据结构及多种高级结构,如String、Hash、List、Set、ZSet,底层通过SDS、跳表等实现高效操作。灵活运用可解决缓存、计数、消息队列、排行榜等问题,结合Bitmap、HyperLogLog、GEO更可应对签到、UV统计、地理位置等场景,是高性能应用的核心利器。

引言:

上一篇文章介绍了Redis的基本概念,了解了Redis 为什么快以及为什么使用单线程操作数据。Redis 受欢迎的另一个原因就是提供了丰富的数据结构,就像一套精密的‘瑞士军刀’,便于我们去解决各种各样的问题。灵活使用redis的数据结构,能让我们高效、优雅地解决业务问题。 今天,就带你深入Redis的“武器库”,看看这5把核心“军刀”如何助你披荆斩棘!

Redis 提供了 5 种基本数据类型:String(字符串)、List(列表)、Set(集合)、Hash(散列)、Zset(有序集合)。

其底层实现主要依赖这 8 种数据结构:简单动态字符串(SDS)、LinkedList(双向链表)、Dict(哈希表/字典)、SkipList(跳表)、Intset(整数集合)、ZipList(压缩列表)、QuickList(快速列表)。

我们要分清这两者的区别:

  • 数据类型是编程方面的概念:是直接提供给开发者使用的
  • 数据结构是底层实现的机制:是计算机层面存储数据的实现方式

    基础数据结构

    1. String (字符串):不只是“Hello World”

  • 本质: 最基础的类型,可存文本、数字(整数/浮点数)、甚至二进制数据(如图片片段)。最多可以容纳的数据长度是 512M
  • 底层: 简单动态字符串 (SDS),比C原生字符串更安全高效,支持动态扩容和预分配。
  • Redis原生命令:
    SET key value [EX seconds]  # 设置键值,可设置过期时间
    GET key                    # 获取值
    INCR key                   # 原子递增
    DECR key                   # 原子递减
    APPEND key value           # 追加内容
    
  • 适用场景: 简单缓存、计数器、分布式锁、小对象存储。

实战案例

  • 原生命令

      # 实现文章阅读量统计
      SET article:1001:views 0
      INCR article:1001:views
    
      # 实现分布式锁
      SETNX lock:order:1001 "1" EX 30
    
  • Java代码片段 :
    // 计数器
    Long views = jedis.incr("article:1001:views");
    // 分布式锁 (简化版)
    String result = jedis.set("lock:order:1001", "client1", "NX", "PX", 30000);
    if ("OK".equals(result)) {
         
        // 获取锁成功,执行业务
    }
    

2. Hash (哈希表):存储“对象”的利器

  • 本质: 键值对集合,特别适合存储对象(如用户信息、商品属性)。
  • 底层: 压缩列表 (ziplist - 小数据时) 或 哈希表 (hashtable - 大数据时)。
  • 优点:
    • 结构化存储: 一个Key对应多个field-value,天然表示对象。
    • 高效操作: HGET/HSET 操作单个字段,无需序列化/反序列化整个对象!HGETALL 获取全部。
    • 节省网络: 更新用户姓名?只需HSET user:1001 name "新名字",不用传整个用户对象。
  • 常用命令

    HSET key field value  # 设置字段值
    HGET key field        # 获取字段值
    HGETALL key           # 获取所有字段值
    HINCRBY key field increment  # 字段值递增
    
  • 适用场景: 对象缓存(用户、商品、配置)、需要频繁修改部分字段的场景。

实战案例

  • 原生命令

      # 存储用户信息
      HSET user:1001 name "张三" age 28 email "zhangsan@example.com"
    
      # 更新用户年龄
      HINCRBY user:1001 age 1
    
  • Java代码片段:

    // 存储用户信息
    Map<String, String> userMap = new HashMap<>();
    userMap.put("name", "张三");
    userMap.put("age", "28");
    userMap.put("email", "zhangsan@example.com");
    jedis.hset("user:1001", userMap);
    // 只获取年龄
    String age = jedis.hget("user:1001", "age");
    // 只更新年龄
    jedis.hset("user:1001", "age", "1");
    

    3. List (列表):灵活的双端队列

  • 本质: 有序、可重复元素的集合,按插入顺序排序。双向链表实现。

  • 底层: 压缩列表 (ziplist) 或 双向链表 (linkedlist)。
  • 特点:
    • 队列: LPUSH (左入) + RPOP (右出) = 先进先出 (FIFO) (消息队列、任务队列)
    • 栈: LPUSH (左入) + LPOP (左出) = 后进先出 (LIFO)
    • 最新列表: LPUSH + LTRIM 0 99 = 保持最新的100条数据(最新消息、最新订单)
    • 阻塞操作: BRPOP/BLPOP (没有元素时阻塞等待,实现简单消息队列)
  • 常用命令
    LPUSH key value      # 左侧插入
    RPUSH key value      # 右侧插入
    LPOP key             # 左侧弹出
    RPOP key             # 右侧弹出
    BLPOP key timeout    # 阻塞式左侧弹出
    
  • 适用场景: 消息队列、最新列表(朋友圈、微博)、任务队列、记录操作日志。

实战案例

  • 原生命令

      # 实现消息队列
      LPUSH message:queue "task1"
      RPOP message:queue
    
      # 实现最新文章列表
      LPUSH news:latest "article:1001"
      LTRIM news:latest 0 9  # 只保留最新10条
    
  • Java代码片段:
    // 1. 消息队列 
    jedis.lpush("message:queue", "task1", "task2", "task3"); 
    String task = jedis.rpop("message:queue");
    // 消费者 (阻塞式)
    List<String> task = jedis.brpop(30, "message:queue"); // 阻塞30秒等待
    if (task != null) {
         
        String taskData = task.get(1); // 实际数据在返回列表的第二个元素
        // 处理任务...
    }
    // 2. 最新文章列表 
    jedis.lpush("news:latest", "article:1001", "article:1002", "article:1003"); 
    jedis.ltrim("news:latest", 0, 9); // 只保留最新10条
    

4. Set (集合):排重与社交的基石

  • 本质: 无序、唯一元素的集合。自动去重!
  • 底层: 整数集合 (intset - 纯整数小集合) 或 哈希表 (hashtable)。
  • 超能力:

    • 去重: SADD 自动过滤重复元素。
    • 集合运算: SINTER (交集-共同好友) SUNION (并集) SDIFF (差集-可能认识的人)。
    • 随机元素: SRANDMEMBER (抽奖) SPOP (随机移除并返回 - 秒杀资格)。
    • 成员存在性: SISMEMBER (快速判断用户是否在某个组/拥有标签)。
  • 常用命令:

    SADD key member      # 添加元素
    SREM key member      # 移除元素
    SISMEMBER key member # 判断元素是否存在
    SINTER key1 key2     # 求交集
    
  • 适用场景: 标签系统、社交关系(好友、关注)、唯一性检查(抽奖用户)、黑白名单、随机推荐。

实战案例

  • 原生命令

      # 文章标签系统
      SADD article:1001:tags "Redis" "数据库" "缓存"
    
      # 共同好友
      SINTER user:1001:friends user:1002:friends
    
  • Java代码片段 (共同好友):
    // 1. 标签系统 
    jedis.sadd("article:1001:tags", "Redis", "数据库", "缓存"); 
    // 2. 检查标签 
    boolean isMember = jedis.sismember("article:1001:tags", "Redis"); System.out.println("是否包含Redis标签:" + isMember); 
    // 输出:true 
    // 3. 共同好友 
    jedis.sadd("user:1001:friends", "user1002", "user1003", "user1004"); jedis.sadd("user:1002:friends", "user1001", "user1003", "user1005"); Set<String> commonFriends = jedis.sinter("user:1001:friends", "user:1002:friends"); 
    System.out.println("共同好友:" + commonFriends); 
    // 输出:[user1003]
    

5. Sorted Set (ZSet - 有序集合):排行榜的王者

  • 本质: Set的升级版!元素唯一,但每个元素关联一个score (分数)。元素按score排序(从小到大)。分数可相同,相同分数按字典序排。
  • 底层: 压缩列表 + 跳表 (skiplist)。跳表是实现高效范围查询的关键。
  • 特点:
    • 元素唯一
    • 按分数排序
    • 支持范围查询
  • 常用命令
    ZADD key score member  # 添加元素
    ZRANGE key start stop [WITHSCORES]  # 获取范围
    ZREVRANK key member    # 获取逆序排名
    ZSCORE key member      # 获取分数
    
  • 适用场景:排行榜、延迟队列、带权重的元素存储

实战案例

  • 原生命令

      # 游戏排行榜
      ZADD game:leaderboard 5000 "player1" 4500 "player2"
    
      # 获取TOP3
      ZREVRANGE game:leaderboard 0 2 WITHSCORES
    
  • Java代码片段:
    // 1. 排行榜 
    jedis.zadd("game:leaderboard", 5000, "player1"); jedis.zadd("game:leaderboard", 4500, "player2"); jedis.zadd("game:leaderboard", 4800, "player3"); 
    // 2. 获取TOP3 
    Set<String> topPlayers = jedis.zrevrange("game:leaderboard", 0, 2); System.out.println("TOP3玩家:" + topPlayers); 
    // 输出:[player1, player3, player2] 
    // 3. 获取玩家排名 
    Long rank = jedis.zrevrank("game:leaderboard", "player2"); System.out.println("player2排名:" + (rank + 1)); 
    // 输出:3 
    // 4. 范围查询 
    Set<String> players = jedis.zrangeByScore("game:leaderboard", 4700, 5100); 
    System.out.println("4700-5100分玩家:" + players); 
    // 输出:[player3, player1]
    

高级数据结构应用

1. 位图(Bitmap)——极省空间的统计工具

特点

  • 基于String实现
  • 极省空间
  • 支持位运算

应用场景:用户签到、活跃用户统计、特征标记

实战案例

# 用户签到
SETBIT sign:202405:user1 15 1 # 5月15日签到
# 统计当月签到天数
BITCOUNT sign:202405:user1

2. HyperLogLog——海量数据去重统计

特点

  • 固定12KB内存
  • 误差率0.81%
  • 支持合并

应用场景:UV统计、大规模去重计数

实战案例

# 统计UV
PFADD uv:20240501 "user1" "user2" "user3"
PFCOUNT uv:20240501

3. 地理空间索引(GEO)——位置服务好帮手

特点

  • 基于Sorted Set实现
  • 支持距离计算
  • 支持范围查询

应用场景:附近的人、位置搜索、地理围栏

实战案例

# 添加地理位置
GEOADD locations 116.404 39.915 "天安门" 121.474 31.230 "上海外滩"
# 查询5公里内的地点
GEORADIUS locations 116.404 39.915 5 km

数据结构选型指南

需求场景 推荐数据结构 原因
简单键值存储 String 简单高效
对象存储 Hash 字段独立操作
消息队列 List 顺序访问
去重集合 Set 自动去重
排行榜 Sorted Set 自动排序
二值统计 Bitmap 极省空间
海量去重统计 HyperLogLog 固定内存消耗
地理位置服务 GEO 内置距离计算

性能优化建议

  1. 避免大Key:单个String不超过10KB,Hash字段不超过1000个
  2. 合理使用管道:减少网络往返时间
  3. 设置合适过期时间:避免内存浪费
  4. 使用SCAN替代KEYS:避免阻塞
  5. 考虑数据分片:超大集合可考虑分片存储

结语:Redis数据结构的力量

Redis通过精心设计的数据结构,在性能、功能和易用性之间取得了完美平衡。掌握这些数据结构,你就能:

  • 构建高性能缓存系统
  • 实现复杂的业务逻辑
  • 处理海量数据统计
  • 开发实时应用

记住:没有最好的数据结构,只有最适合的数据结构。根据你的具体业务场景,选择最合适的Redis数据结构,才能发挥Redis的最大威力。

相关文章
|
7天前
|
弹性计算 关系型数据库 微服务
基于 Docker 与 Kubernetes(K3s)的微服务:阿里云生产环境扩容实践
在微服务架构中,如何实现“稳定扩容”与“成本可控”是企业面临的核心挑战。本文结合 Python FastAPI 微服务实战,详解如何基于阿里云基础设施,利用 Docker 封装服务、K3s 实现容器编排,构建生产级微服务架构。内容涵盖容器构建、集群部署、自动扩缩容、可观测性等关键环节,适配阿里云资源特性与服务生态,助力企业打造低成本、高可靠、易扩展的微服务解决方案。
1167 3
|
6天前
|
机器学习/深度学习 人工智能 前端开发
通义DeepResearch全面开源!同步分享可落地的高阶Agent构建方法论
通义研究团队开源发布通义 DeepResearch —— 首个在性能上可与 OpenAI DeepResearch 相媲美、并在多项权威基准测试中取得领先表现的全开源 Web Agent。
854 12
|
16天前
|
人工智能 运维 安全
|
5天前
|
机器学习/深度学习 物联网
Wan2.2再次开源数字人:Animate-14B!一键实现电影角色替换和动作驱动
今天,通义万相的视频生成模型又又又开源了!Wan2.2系列模型家族新增数字人成员Wan2.2-Animate-14B。
445 10
|
7天前
|
弹性计算 Kubernetes jenkins
如何在 ECS/EKS 集群中有效使用 Jenkins
本文探讨了如何将 Jenkins 与 AWS ECS 和 EKS 集群集成,以构建高效、灵活且具备自动扩缩容能力的 CI/CD 流水线,提升软件交付效率并优化资源成本。
329 0
|
14天前
|
人工智能 异构计算
敬请锁定《C位面对面》,洞察通用计算如何在AI时代持续赋能企业创新,助力业务发展!
敬请锁定《C位面对面》,洞察通用计算如何在AI时代持续赋能企业创新,助力业务发展!