Redis数据组织揭秘:全局哈希表

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: Redis数据组织揭秘:全局哈希表

前言

首先,Redis作为一个优秀开源的内存数据结构存储系统,可以用作数据库、缓存和消息中介。它支持多种数据结构,包括字符串、哈希表、列表、集合、有序集合等。当我们谈论Redis中的“哈希表”时,我们通常是指Redis用作数据结构之一的哈希数据类型,而不是Redis内部用于存储所有键值对的全局哈希表实现。

一、什么是Redis的全局哈希表

Redis的全局哈希表是一个内部数据结构,用于存储Redis服务器中的所有键值对。全局哈希表通常是一个由哈希桶组成的数组。每个哈希桶可以保存一个或多个键值对,这些键值对通过哈希函数映射到特定的哈希桶中。当发生哈希冲突(即多个键哈希到同一个桶)时,Redis会使用链表或其他数据结构来解决冲突。


全局哈希表的优势在于它提供了一种高效的方式来存储和检索键值对。通过将键哈希到哈希桶中,Redis可以在平均常数时间内执行查找、插入和删除操作,从而实现快速的数据访问。


一个哈希表就是一个数组,数组每个元素叫哈希桶,每个哈希桶保存键值对数据。然而哈希桶中的元素不是 value 本身,而是指向 value 的指针,即 value 存储的内存地址

如图,这个哈希表保存了所有键值对,哈希桶中的 entry 元素保存key 和value 指针,哈希表能在 O(1) 时间复杂度快速查找键值对,所以我们只需要计算 key 的哈希值就能找到对应的哈希桶位置,进而找到对应的 entry 元素。不同类型的 value 都能被找到,不论是 String、List、Set、Hash。


这种查找方式只需要进行一次哈希计算,不论数据规模多少,然而,在 Redis 中写入大量数据后,操作有时候会变慢,因为出现了哈希表的冲突以及 rehash 带来的操作阻塞。

二、全局哈希表的核心实现

由于哈希表的特性,可能会出现多个键哈希到哈希表中同一个位置的情况,这称为哈希冲突。为了解决这个问题,Redis采用了链式哈希。但在某些情况下,可能还需要对整个哈希表进行rehash操作,以维持其性能。接下来,我将详细解释这些概念。

2.1. 哈希冲突

哈希冲突是指两个或更多的键通过哈希函数计算后,得到了相同的哈希值,从而它们被映射到了哈希表中的同一个位置。在理想情况下,哈希函数会将键均匀地分布在哈希表中,但实际上这是很难完全实现的,特别是在数据量非常大时。

2.2. 链式哈希

为了解决哈希冲突,Redis采用了链式哈希的方法。具体来说,哈希表中的每个位置(通常称为哈希桶或槽)实际上是一个链表或其他类型的动态数据结构(如红黑树)。当发生哈希冲突时,新的键值对会被添加到该位置的链表中。这样,哈希表中的每个位置都可以存储多个键值对,它们通过链表连接在一起。

9c969afce9384e8db66c3dde07d51854.png

如图所示,entry1、entry2 和 entry3 都保存在哈希桶 3 中,导致哈希冲突。entry1 增加个next 指针指向 entry2,entry2 增加next 指针指向 entry3,不论哈希桶 3 元素有多少个,都可以通过指针连接起来,形成一个链表,叫做哈希冲突链。

链式哈希会产生一个问题,随着哈希表数据越来越多,哈希冲突越来越多,单个哈希桶链表上数据越来越多,查找时间复杂度退化到 O(n),查找耗时增加,效率降低。

2.3. rehash

rehash是指重新构建哈希表的过程。当哈希表中的键值对数量与哈希表的大小(即哈希桶的数量)之间的比率变得不合适时,Redis会执行rehash操作。例如,当哈希表变得过于拥挤(负载因子过高)或过于稀疏(负载因子过低)时,就可能需要进行rehash。

rehash操作通常涉及以下步骤:

  • 创建一个新的哈希表,其大小可能大于或小于当前的哈希表,具体取决于负载因子的调整需求。
  • 将旧哈希表中的所有键值对重新哈希到新哈希表中。
  • 一旦所有键值对都被迁移到新的哈希表中,旧的哈希表就会被释放。

在第二步涉及大量数据拷贝,如果一次性把哈希表 1 迁移完,耗时很长,会造成线程阻塞,无法处理其他请求,Redis 是怎么处理这个问题呢?它采用渐进式 rehash

2.4. 渐进式 rehash

渐进式rehash是Redis中一种特殊的rehash策略,用于在不影响服务器性能的情况下逐步完成rehash操作。由于Redis是单线程的,并且需要处理来自客户端的实时请求,因此一次性完成大规模的rehash操作可能会导致服务器在一段时间内无法响应客户端的请求。

为了避免这种情况,Redis采用了渐进式rehash的方法。这种方法将rehash操作分成多个小步骤,每个步骤只迁移一小部分键值对。这样,Redis可以在处理客户端请求的同时,逐步完成整个rehash操作。渐进式rehash通过维护两个哈希表(一个旧的和一个新的)并在处理客户端请求时逐步迁移键值对来实现。

渐进式 rehash 把一次性大量拷贝的开销,分摊到了多次处理请求的过程中,避免了耗时操作,保证了数据的快速访问。

总结来说,哈希冲突是哈希表不可避免的问题,Redis通过链式哈希来有效地解决它。而rehash和渐进式rehash则是Redis为了维持哈希表性能而采用的重要策略。

三、全局哈希表的优势

全局哈希表的优势主要体现在以下几个方面:

  • 高效查找:全局哈希表通过哈希函数将键映射到存储位置,使得查找操作的时间复杂度降低到接近常数级别。这意味着无论数据量有多大,查找特定键的值所需的时间都基本保持不变,从而实现了非常快速的查找性能。
  • 快速插入和删除:全局哈希表不仅支持高效的查找操作,还提供了快速的插入和删除功能。插入和删除操作的时间复杂度也接近常数级别,因为它们不涉及数据移动或重新排序等耗时操作。
  • 数据共享和同步:全局哈希表可以在分布式环境中实现数据的共享和同步。多个客户端或节点可以同时读写全局哈希表,从而保持数据的一致性和实时性。这对于需要协同处理或共享数据的应用场景非常有用。
  • 高可扩展性和容错性:通过将数据分布在多个节点上,全局哈希表可以很容易地进行扩展,以应对不断增长的数据量。同时,当某个节点发生故障时,其他节点可以继续提供服务,确保系统的可用性和稳定性。

需要注意的是,全局哈希表也存在一些局限性,例如无法按照特定顺序遍历元素、键的唯一性要求等。因此,在选择使用全局哈希表时,需要根据具体的应用场景和需求进行权衡和考虑。

四、集合全局哈希表来看Redis数据查询流程

在集群环境下,数据的分布和查询流程与单实例环境有所不同。Redis集群使用分片(sharding)来将数据分布在多个节点上,每个节点负责处理一部分哈希槽(hash slot)中的数据。全局哈希表的概念在这里仍然适用,但是它是分布在集群的所有节点上的。以下是Redis集群环境下的数据查询流程:


1. 客户端连接:

客户端首先连接到Redis集群中的一个节点,这个节点可以是集群中的任意节点,因为Redis集群中的每个节点都保存了集群的元数据,包括哈希槽与节点的映射关系。


2. 发送查询命令:

客户端向连接的节点发送查询命令,并指定要查询的键。


3. 计算哈希槽:

Redis集群不使用单个哈希函数直接定位到具体的节点,而是使用哈希槽的概念。Redis集群有16384个哈希槽,客户端会根据键的哈希值计算出这个键属于哪个哈希槽。哈希槽的计算公式通常是HASH_SLOT = CRC16(key) mod 16384,其中CRC16是16位循环冗余校验码。


4. 节点定位:

客户端或代理节点(如果使用了像Twemproxy这样的代理)会查询集群的元数据,确定负责处理该哈希槽的节点。如果客户端已经连接到了正确的节点,它会直接向该节点发送查询命令;否则,它会重定向到正确的节点。


5. 在节点的哈希桶查找数据:

一旦很久key的hash计算后确定了哈希桶的位置,Redis会在这个桶中查找数据。由于哈希冲突的存在,一个桶中可能存储了多个键值对。因此,Redis可能需要遍历桶中的链表或其他数据结构来找到确切的键值对。


如果桶中的数据结构是链表,Redis会遍历链表,逐个比较链表中的键与客户端提供的键是否匹配。

如果使用的是其他数据结构(如红黑树),则按照相应数据结构的查找算法进行查找。

6. 提取并返回数据:

如果找到了匹配的键值对,节点会提取出对应的值,并将其返回给客户端。如果查询的键不存在,节点会返回一个空结果或错误信息。


7. 处理特殊情况:

在查询过程中,节点可能会遇到一些特殊情况,如数据迁移、节点故障等。Redis集群有一套机制来处理这些情况,例如使用重定向命令ASK或MOVED来指引客户端到正确的节点。


8. 结束查询:

查询完成后,节点会关闭与客户端的连接(如果是一次性查询的话),或者等待处理下一个客户端请求。

五、数据库和全局哈希表的关系在Redis中,“数据库”是一个逻辑上的概念,用于对键值对进行分组和隔离。Redis服务器默认会创建16个数据库(编号从0到15),这些数据库在内部是通过数组来存储和管理的。每个Redis客户端都可以选择一个目标数据库进行操作,默认情况下,客户端的目标数据库为0号数据库,但可以通过执行SELECT命令来切换目标数据库。


每个Redis数据库都有其自己的键值对集合,这些键值对在全局范围内是隔离的。这意味着,在不同的数据库中,可以存在相同的键,它们不会相互干扰或冲突。


关于全局哈希表,它是Redis内部用于实现快速键值对访问的数据结构。Redis使用一个全局哈希表来保存所有的键值对,无论这些键值对属于哪个数据库。全局哈希表通过哈希算法将键映射到相应的哈希桶中,以实现快速的查找、插入和删除操作。


然而,需要注意的是,尽管所有数据库共享同一个全局哈希表,但它们在内部是通过不同的键值对集合来隔离的。当切换到不同的数据库时,Redis会在内部切换到相应的键值对集合,以确保操作的正确性和隔离性。


总结来说,Redis中的数据库是一个逻辑上的概念,用于对键值对进行分组和隔离。而全局哈希表是Redis内部用于实现快速键值对访问的数据结构。尽管所有数据库共享同一个全局哈希表,但它们在内部是通过不同的键值对集合来隔离的。

六、Redis的内部哈希表和集群的哈希槽的区分

全局哈希表是Redis内部用于存储所有键值对的数据结构,它是一个由哈希桶组成的数组,每个哈希桶可以保存一个或多个键值对。键通过哈希函数被映射到哈希桶中,当发生哈希冲突时(即多个键映射到同一个哈希桶),Redis会使用链表或其他数据结构来解决冲突。


而哈希槽(hash slot)是Redis集群中的一个概念,不是单实例Redis的全局哈希表实现的一部分。在Redis集群中,数据被分布在多个Redis节点上,为了提高数据的分布均匀性和可扩展性,Redis引入了哈希槽的概念。


哈希槽是一个逻辑上的分区,整个键空间被划分为16384个哈希槽,每个键都会被映射到这些哈希槽中的一个。Redis集群中的每个节点负责处理一部分哈希槽,这样可以将数据均匀地分布在多个节点上。


需要注意的是,哈希槽和Redis内部的全局哈希表是两个不同的概念,它们分别用于不同的目的。全局哈希表是Redis内部用于存储键值对的数据结构,而哈希槽是Redis集群中用于数据分布和节点间通信的逻辑分区。


总结来说,Redis的全局哈希表是一个内部数据结构,用于存储键值对,并通过哈希函数将键映射到哈希桶中。而哈希槽是Redis集群中的一个概念,用于在多个节点之间分配数据和实现数据的分布式存储。

总结

Redis的全局哈希表和查询流程是其高性能和灵活性的关键所在。通过精心设计的数据结构和算法,Redis实现了在内存中快速存储和检索数据的能力。未来,随着硬件技术的发展和新型存储介质的涌现,我们期待Redis能够进一步优化其数据组织方式,为我们带来更加出色的性能体验。


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
1月前
|
存储 缓存 NoSQL
数据的存储--Redis缓存存储(一)
数据的存储--Redis缓存存储(一)
|
11天前
|
NoSQL Redis
Redis的数据淘汰策略有哪些 ?
Redis 提供了 8 种数据淘汰策略,分为淘汰易失数据和淘汰全库数据两大类。易失数据淘汰策略包括:volatile-lru、volatile-lfu、volatile-ttl 和 volatile-random;全库数据淘汰策略包括:allkeys-lru、allkeys-lfu 和 allkeys-random。此外,还有 no-eviction 策略,禁止驱逐数据,当内存不足时新写入操作会报错。
45 16
|
1月前
|
监控 NoSQL Java
场景题:百万数据插入Redis有哪些实现方案?
场景题:百万数据插入Redis有哪些实现方案?
40 1
场景题:百万数据插入Redis有哪些实现方案?
|
1月前
|
存储 缓存 NoSQL
数据的存储--Redis缓存存储(二)
数据的存储--Redis缓存存储(二)
数据的存储--Redis缓存存储(二)
|
11天前
|
缓存 NoSQL 关系型数据库
Redis和Mysql如何保证数据⼀致?
在项目中,为了解决Redis与Mysql的数据一致性问题,我们采用了多种策略:对于低一致性要求的数据,不做特别处理;时效性数据通过设置缓存过期时间来减少不一致风险;高一致性但时效性要求不高的数据,利用MQ异步同步确保最终一致性;而对一致性和时效性都有高要求的数据,则采用分布式事务(如Seata TCC模式)来保障。
47 14
|
11天前
|
存储 NoSQL 算法
Redis分片集群中数据是怎么存储和读取的 ?
Redis集群采用哈希槽分区算法,共有16384个哈希槽,每个槽分配到不同的Redis节点上。数据操作时,通过CRC16算法对key计算并取模,确定其所属的槽和对应的节点,从而实现高效的数据存取。
40 13
|
11天前
|
存储 NoSQL Redis
Redis的数据过期策略有哪些 ?
Redis 采用两种过期键删除策略:惰性删除和定期删除。惰性删除在读取键时检查是否过期并删除,对 CPU 友好但可能积压大量过期键。定期删除则定时抽样检查并删除过期键,对内存更友好。默认每秒扫描 10 次,每次检查 20 个键,若超过 25% 过期则继续检查,单次最大执行时间 25ms。两者结合使用以平衡性能和资源占用。
35 11
|
11天前
|
监控 NoSQL 测试技术
【赵渝强老师】Redis的AOF数据持久化
Redis 是内存数据库,提供数据持久化功能,支持 RDB 和 AOF 两种方式。AOF 以日志形式记录每个写操作,支持定期重写以压缩文件。默认情况下,AOF 功能关闭,需在 `redis.conf` 中启用。通过 `info` 命令可监控 AOF 状态。AOF 重写功能可有效控制文件大小,避免性能下降。
|
11天前
|
存储 监控 NoSQL
【赵渝强老师】Redis的RDB数据持久化
Redis 是内存数据库,提供数据持久化功能以防止服务器进程退出导致数据丢失。Redis 支持 RDB 和 AOF 两种持久化方式,其中 RDB 是默认的持久化方式。RDB 通过在指定时间间隔内将内存中的数据快照写入磁盘,确保数据的安全性和恢复能力。RDB 持久化机制包括创建子进程、将数据写入临时文件并替换旧文件等步骤。优点包括适合大规模数据恢复和低数据完整性要求的场景,但也有数据完整性和一致性较低及备份时占用内存的缺点。
|
1月前
|
消息中间件 缓存 NoSQL
大数据-49 Redis 缓存问题中 穿透、雪崩、击穿、数据不一致、HotKey、BigKey
大数据-49 Redis 缓存问题中 穿透、雪崩、击穿、数据不一致、HotKey、BigKey
53 2
下一篇
无影云桌面