Redis 底层实现深度解析:从 ListPack 到哈希表扩容

前言

作为一个高性能的键值存储系统,Redis 凭借其丰富的数据结构、快速的数据操作和灵活的应用场景,成为了现代分布式系统中不可或缺的组件。为了支持各种类型的操作,Redis 在内存中使用了多种不同的底层数据结构。

本文将深入解析 Redis 中的几个重要实现,包括:

  • ListPack:Redis 中有序集合的内存优化
  • 哈希表扩容:如何应对键值对数量增长带来的挑战
  • SDS:为什么 Redis 使用这种数据结构来存储字符串
  • Zset 在项目中的应用:Redis 有序集合的实际用途与实践

一、Redis 的 ListPack 实现

什么是 ListPack?

ListPack 是 Redis 中用于优化内存存储的一种数据结构,主要应用于 Redis 7.0 引入的优化。Redis 使用 ListPack 来存储 有序集合(Zset)小型的列表(List) ,它是一种内存紧凑型的数据存储方式,目的是减少内存开销并提高存取速度。

主要特点:
  • 紧凑的内存布局:ListPack 将数据项和指针紧凑存储,避免了传统数据结构中由于元素个数增多导致的内存浪费。

  • 适用于小数据集:对于存储的小型数据,ListPack 提供了非常高效的存储和操作。

如何实现 ListPack?

Redis 通过将多个元素存储在一个连续的内存区域来实现 ListPack。每个元素包含它的值和某些元数据,如元素的长度。

  • 压缩存储:ListPack 对存储的数据进行了压缩处理,避免了为每个元素单独分配内存块。

  • 数据类型支持:支持字符串、整数等基本数据类型,并能够高效地进行元素查找和修改。

二、Redis 哈希表扩容

哈希表扩容的机制

Redis 使用哈希表(Hash Table)作为其字典类型数据的底层存储。哈希表的扩容是 Redis 存储优化中的一个关键点。哈希表的大小并不是一成不变的,它会随着存储的键值对数量增加而进行 扩容

哈希表的扩容步骤:
  • 当 Redis 中存储的元素个数超过当前哈希表的负载因子时(默认负载因子是 0.75),哈希表将自动进行扩容。

  • 扩容的过程包括创建一个新的哈希表,并将原哈希表中的元素重新哈希到新的哈希表中。

  • 扩容是 渐进式的 ,Redis 使用 渐进式 rehashing 技术来减少扩容时的性能开销。

哈希表扩容时的读操作

在 Redis 执行扩容时,读请求不会被阻塞 。扩容是通过 渐进式 rehash 实现的,它将所有键值对分布到新的哈希表中,而不是一次性复制。

  • 渐进式 rehash:Redis 会逐步将旧哈希表中的元素迁移到新哈希表中,同时仍然能够响应读请求。

  • 性能平衡:虽然在扩容过程中需要重新哈希,但通过渐进式的操作,Redis 保证了扩容对性能的影响是可控的。

三、Redis 中的字符串存储:SDS

SDS 的介绍

Redis 使用 SDS(Simple Dynamic String) 来存储字符串数据,而不是 C 语言中的传统字符串。C 语言中的字符串是以字符数组的形式存储的,并以 null 结尾。但是,C 字符串有几个缺点:

  • 没有长度信息:字符串的长度必须通过遍历字符串来计算。

  • 空间浪费:如果字符串有修改操作,可能会浪费额外的内存。

为了解决这些问题,Redis 使用了 SDS,它的设计避免了 C 字符串的缺陷,并且提供了更高效的字符串处理能力。

SDS 的设计:
  • 存储结构:SDS 包含三个字段:

    • len:字符串的长度

    • alloc:分配的空间大小

    • buf:实际存储字符串内容的缓冲区

  • 优点

    • O(1) 获取字符串长度 :通过 len 字段可以立即获取字符串的长度。

    • 动态扩展:在字符串长度增加时,SDS 可以动态扩展,而不会浪费空间。

四、Redis Zset 在项目中的应用

Zset 的基本概念

Redis 的有序集合(Zset)是一种非常强大的数据结构,它存储的是一组 唯一的元素 ,并且每个元素都有一个 分数(score) 。Redis 会根据元素的分数对其进行 自动排序,这使得 Zset 成为实现排行榜、排名、时间序列等应用场景的理想选择。

Redis Zset 的应用场景

(1)排行榜

Zset 非常适用于实现实时的排行榜功能。例如,在一个游戏应用中,用户的积分可以作为分数存储在 Zset 中:

复制代码
ZADD leaderboard 150 user1
ZADD leaderboard 200 user2
ZADD leaderboard 100 user3
(2)时间序列数据

由于 Zset 按分数排序,您可以利用时间戳作为分数,存储时间序列数据,如用户的登录时间、股票价格等:

复制代码
ZADD timeseries 1627296000 user1_login
ZADD timeseries 1627299600 user1_login
(3)实时推荐

通过 Zset 的排序和区间查询特性,您可以为用户提供基于兴趣和历史行为的实时推荐。

复制代码
ZREVRANGE user_interests 0 10

总结

Redis 作为高效的内存数据存储,提供了多种底层数据结构来满足不同的应用场景。通过深入理解 Redis 中的 ListPack哈希表扩容机制SDS 字符串存储 以及 Zset 的应用场景,我们能够更加高效地利用 Redis 来解决实际问题。

  • ListPack 提供了内存优化和高效存储

  • 哈希表扩容 使用渐进式 rehash 保证高效扩容

  • SDS 解决了传统 C 字符串的缺陷,提高了字符串操作效率

  • Zset 在排行榜、时间序列、实时推荐等场景中的应用具有广泛的实用价值

Redis 不仅仅是一个简单的缓存工具,它的设计和底层实现让它成为了一个功能强大的数据结构服务器。

相关推荐
斯普信云原生组2 小时前
Redis 阈值超限及影响分析
redis·spring·bootstrap
程序员JerrySUN3 小时前
OP-TEE + YOLOv8:从“加密权重”到“内存中解密并推理”的完整实战记录
android·java·开发语言·redis·yolo·架构
郝学胜-神的一滴3 小时前
Linux进程与线程控制原语对比:双刃出鞘,各显锋芒
linux·服务器·开发语言·数据结构·c++·程序人生
javachen__4 小时前
341-十道经典程序设计题目
数据结构·c++·算法
此生只爱蛋4 小时前
【Redis】数据类型补充
数据库·redis·缓存
毅炼5 小时前
hot100打卡——day08
java·数据结构·算法·leetcode·深度优先
denggun123457 小时前
悬垂指针 和 野指针
数据结构
哈里谢顿7 小时前
MySQL 和 Redis搭配使用指南
redis·mysql
程序帝国7 小时前
SpringBoot整合RediSearch(完整,详细,连接池版本)
java·spring boot·redis·后端·redisearch