Redis 数据结构详解:从底层实现到应用场景

引言

Redis 是一个高效、灵活的键值存储系统,广泛应用于缓存、消息队列、实时分析等场景。Redis 提供了丰富的数据结构,每一种数据结构都适用于特定的业务需求。理解这些底层数据结构,不仅能帮助你更好地使用 Redis,也能加深你对 Redis 内部实现的理解。

本文将详细介绍 Redis 的核心数据结构,包括:

  • String
  • Hash
  • List
  • Set
  • Sorted Set (Zset)
  • Bitmap
  • HyperLogLog
  • Geo
  • Stream

此外,我们还将深入解析 Zset 的底层实现,特别是 跳表 和 Redis 7.0 引入的 ListPack,并探讨 Redis 为什么选择跳表而非 B+ 树。

一、Redis 的数据结构概览

Redis 支持的多种数据结构使它能够应对各种复杂的场景。下面是对 Redis 常见数据结构的简要介绍:

String(字符串)

  • 存储值:简单的字符串(例如,数字、文本、二进制数据)

  • 读写能力:支持快速的读取、写入、修改操作。

  • 应用场景

    • 缓存:简单的缓存键值对

    • 计数器:实现页访问计数、秒杀商品库存等功能

Hash(哈希)

  • 存储值:键值对集合(类似于字典或映射)

  • 读写能力:适合存储对象,支持按字段读取和修改。

  • 应用场景

    • 用户信息:存储用户的基本资料(用户名、邮箱、年龄等)

    • 配置管理:存储配置信息并能快速修改

List(列表)

  • 存储值:有序字符串集合

  • 读写能力 :支持两端插入、删除(LPUSHRPUSH),以及索引访问。

  • 应用场景

    • 消息队列:使用 LPUSHRPOP 实现生产者消费者模式

    • 实时数据流:处理顺序数据流,如日志收集

Set(集合)

  • 存储值:无序字符串集合,支持唯一性

  • 读写能力:支持快速插入、删除、查找操作,支持求交集、并集和差集等集合运算。

  • 应用场景

    • 唯一元素集合:如用户的标签、访问过的商品 ID

    • 社交关系:如用户的好友关系、关注列表

Sorted Set(有序集合,Zset)

  • 存储值:每个元素都包含一个分数(score)和一个值,元素会根据分数排序

  • 读写能力:支持按分数区间或排名范围获取元素,支持快速插入和删除

  • 应用场景

    • 排行榜:存储用户积分、游戏得分等

    • 时间序列:如股票价格、传感器数据等

Bitmap(位图)

  • 存储值:用于对 bit 位进行操作的数组

  • 读写能力:可以对单个位进行高效操作,适用于计数、标记等。

  • 应用场景

    • 用户签到:记录每个用户的每日签到情况

    • 活跃用户统计:使用 SETBIT 记录某个时间段内是否活跃

HyperLogLog

  • 存储值:用于统计唯一元素的基数估算,适合大数据量场景

  • 读写能力:基于概率算法,支持高效估算唯一值数量

  • 应用场景

    • 唯一用户统计:统计活跃用户数,避免直接存储大量数据

Geo(地理空间)

  • 存储值:基于经纬度坐标的数据

  • 读写能力:支持按位置范围查询、计算两点之间的距离等

  • 应用场景

    • 实时位置追踪:如共享单车、打车服务中的用户位置

    • 最近餐馆、商店查询等

Stream(流)

  • 存储值:消息流,支持高效的生产和消费操作

  • 读写能力:类似于日志,支持高并发的生产和消费

  • 应用场景

    • 实时日志:系统日志收集、监控数据流

    • 消息队列:提供持久化、顺序消费的消息队列

二、Zset(有序集合)的底层实现

Redis Zset 数据结构概述

Redis 中的有序集合(Zset)是一个按照分数排序的字符串集合,支持以下操作:

  • 插入、删除元素

  • 查询指定范围的元素(按分数排序)

  • 获取某个分数区间内的元素

Zset 的底层数据结构

最初,Redis 使用 压缩列表(Ziplist)跳表(Skiplist) 来实现 Zset 的存储。

(1)压缩列表(Ziplist)
  • 优点:内存节省,适用于元素数量小、范围不大的场景

  • 缺点:插入删除操作的性能较低

(2)跳表(Skiplist)
  • Redis 选择 跳表 作为 Zset 的默认数据结构。

  • 跳表通过多层次的索引结构来加速查找,具有对数时间复杂度 O(log N)


Redis 7.0:ListPack 实现 Zset

  • 从 Redis 7.0 开始,Zset 数据结构由 ListPack 取代了之前的压缩列表和跳表实现。

  • ListPack 是一种新的高效的内存编码方式,支持更低的内存消耗,并且支持更高效的查询和插入。

三、跳表(Skiplist)详解

跳表的基本概念

跳表是一种通过多级索引来加速查找的概率性数据结构。它可以在有序的链表上建立多级索引,每一级的元素个数是上一级的部分元素。

跳表的结构
  • 底层:一个普通的有序链表

  • 上层:多个索引层,元素的数量逐层减少

跳表的层高如何设置?

跳表的层高是动态确定的,通过 概率性算法 来决定:

  • 每个元素有一定概率升到上层

  • 理论上,跳表的层高通常是对数级别,平均层高约为 log N

为什么 Redis 选择跳表而非 B+ 树?

B+ 树 vs 跳表
  • B+ 树:结构更复杂,性能受限于树的平衡和节点管理

  • 跳表 :实现简单,且由于概率机制,跳表具有良好的 内存局部性 ,适合 内存存储 这种快速查找需求

Redis 选择 跳表 的原因:

  • 内存效率:跳表在内存中的实现比 B+ 树简单,且性能更好

  • 实现简洁:跳表的实现不需要复杂的树的平衡机制,增加了 Redis 的易用性和可维护性

  • 高并发:跳表适用于高并发场景,能高效处理大量数据的插入和查询

总结

  • Redis 提供了多种高效的数据结构,能够满足不同业务需求。
  • Zset 的底层实现,从最初的压缩列表到跳表,再到 Redis 7.0 的 ListPack,逐步提高了性能与内存效率。
  • Redis 选择 跳表 而非 B+ 树,因为跳表在内存中的实现更简单、高效,适合快速查找。

Redis 通过这些高效的数据结构,保证了它在高并发、高性能场景中的优势。

相关推荐
曾几何时`2 小时前
滑动窗口(十五)2962. 统计最大元素出现至少 K 次的子数组(越长越合法型)
数据结构·算法
Bdygsl2 小时前
数据结构 —— 队列
数据结构
于樱花森上飞舞2 小时前
【多线程】CAS和哈希表
java·数据结构·java-ee
编程之路,妙趣横生2 小时前
数据结构(十二) 位图 & 布隆过滤器
数据结构
a努力。2 小时前
哈罗骑行Java面试被问:Redis的持久化策略对比
java·redis·面试
Yu_iChan2 小时前
苍穹外卖Day6 缓存菜品与缓存套餐功能
redis·缓存
无言(* ̄(エ) ̄)2 小时前
C语言--运算符/函数/结构体/指针
c语言·开发语言·数据结构·数据库·算法·mongodb
im_AMBER2 小时前
Leetcode 91 子序列首尾元素的最大乘积
数据结构·笔记·学习·算法·leetcode
CoderCodingNo2 小时前
【CSP】CSP-XL 2025辽宁复赛真题-第三题, 小L打比赛(match)
数据结构·算法