问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

Redis技术原理详解:从内存管理到集群架构

创作时间:
作者:
@小白创作中心

Redis技术原理详解:从内存管理到集群架构

引用
CSDN
1.
https://blog.csdn.net/Cosmoshhhyyy/article/details/137418197

Redis作为全球广泛使用的开源数据库,其高效的数据处理能力和灵活的架构设计使其在各种应用场景中大放异彩。本文将从Redis的诞生背景出发,深入探讨其内存管理机制、持久化策略、高可用解决方案以及集群架构等核心技术原理,帮助读者全面理解Redis的工作方式。

Redis的诞生:

Redis的诞生与MySQL有着密切的关系。在Redis出现之前,用户的每次请求都是直接访问MySQL。随着使用量的增加,人们发现大部分请求都是读操作,而且很多都是重复的数据。由于磁盘I/O速度较慢,这导致了系统性能瓶颈。因此,人们借鉴CPU缓存机制的思路,希望在MySQL上建立一个缓存层。基于这种需求,一个基于内存的数据库应运而生,这就是Redis。

Redis是基于内存的,因此其查询速度远快于MySQL。当一个请求到达时,系统会首先查询Redis中是否存在所需数据。如果存在,直接返回结果;如果不存在,则继续查询数据库,并将查出的数据放入Redis中,以便下次查询时可以直接从缓存中获取。通过这种方式,可以将常用的热点数据存储在Redis中,从而大大减轻MySQL的压力并提高整体性能。

Redis内存管理机制:

然而,随着数据量的增加,内存管理问题逐渐显现。由于内存资源昂贵且有限,如果不加以管理,内存很快就会被占满。为了解决这个问题,Redis提供了数据过期设置功能,允许程序员自主设置数据的过期时间。当数据过期后,Redis会将其删除。具体来说,Redis采用了两种删除策略:

  • 定期删除:Redis会每隔一段时间随机删除一些过期数据。之所以不一次性删除所有过期数据,主要是因为数据量可能很大,全部删除需要耗费大量时间。因此,只随机删除一部分数据即可保证系统的正常运行。

  • 惰性删除:在定期删除中,有些数据可能每次都能逃脱随机删除算法的筛选而保留下来。因此,Redis额外引入了一个策略:每当请求查询到Redis中已经过期的数据时,就直接进行删除。

但是,即使有定期删除和惰性删除机制,仍然可能有部分数据能够长期存活。为了解决这一问题,Redis引入了内存淘汰策略,类似于操作系统中的内存页置换算法。具体策略包括:

  1. No Eviction(无淘汰):当内存达到限制时,新写入操作会导致写入操作失败并返回错误。

  2. All Keys Random(随机淘汰):Redis会从所有的键中随机选择一个进行淘汰。

  3. Volatile Least Recently Used(Volatile-LRU):Redis会从设置了过期时间的键中,选择最近最少使用的键进行淘汰。

  4. Volatile Least Frequently Used(Volatile-LFU):Redis会从设置了过期时间的键中,选择最不经常使用的键进行淘汰。

  5. Volatile Random(Volatile-Random):Redis会从设置了过期时间的键中随机选择一个进行淘汰。

  6. Least Recently Used(LRU):Redis会从所有的键中,选择最近最少使用的键进行淘汰。

  7. Least Frequently Used(LFU):Redis会从所有的键中,选择最不经常使用的键进行淘汰。

  8. Random(随机):Redis会从所有的键中随机选择一个进行淘汰。

通过定期删除、惰性删除和淘汰策略的结合使用,Redis有效地解决了内存管理问题。

缓存穿透、击穿与雪崩:

在实际应用中,缓存系统可能会遇到一些特殊场景,如缓存穿透、缓存击穿和缓存雪崩,这些场景都可能导致系统性能下降甚至崩溃。下面将分别介绍这些场景及其解决方案。

缓存穿透:

缓存穿透是指当一个数据在MySQL中不存在时,系统会先查询Redis,发现没有缓存,然后又去查询MySQL,结果MySQL中也没有,导致每次请求都会直接访问数据库,造成数据库压力。为了解决这个问题,可以采用布隆过滤器

布隆过滤器(Bloom Filter)是一种用于快速检查一个元素是否可能存在于一个集合中的数据结构,它通过使用位数组和哈希函数来实现。虽然它可以告诉你一个元素“可能存在”或“肯定不存在”,但它无法告诉你一个元素“肯定存在”。

其原理是通过多个哈希函数将一个元素映射到位数组的多个位置上,将0置为1。在判断一个元素是否存在时,就会用多个相同哈希函数映射,然后判断映射的位置上是否都为1,若都为1说明可能存在,因为可能有其他的一些元素映射会将这些位置正好都置为了1,所以可能会发生很小概率的误判,当然如果不都为1,那么一定是不存在的。

通过布隆过滤器,可以快速判断一个请求的数据是否存在于MySQL中,从而过滤掉不必要的数据库查询,提高系统性能。

缓存击穿:

缓存击穿是指当某个热点数据过期时,大量请求同时访问该数据,导致所有请求都绕过缓存直接访问数据库,造成数据库压力剧增。为了解决这个问题,可以采用以下策略:

  1. 设置热点数据的过期时间均匀分布,避免大量数据在同一时间过期。
  2. 对于一些关键的热点数据,可以设置永不过期。

缓存雪崩:

缓存雪崩是缓存击穿的加强版,指的是大量热点数据在同一时刻过期,导致所有请求都直接访问数据库,造成数据库崩溃。解决方案包括:

  1. 设置热点数据过期时间均匀一点,别都一起过期了。
  2. 设置某些热点数据永不过期。

通过这些策略,可以有效避免缓存穿透、击穿和雪崩问题,提高系统的稳定性和可靠性。

Redis持久化:

Redis需要持久化的主要原因是,如果Redis崩溃,重启时需要恢复原有的数据。Redis提供了两种主要的持久化方式:RDB和AOF。

RDB:

RDB(Redis Database Backup)是通过fork一个子进程将数据遍历一遍,然后以二进制格式(压缩空间)写入文件中,相当于一个备份(快照)。但是,遍历一遍数据会消耗大量时间和性能,而且很多请求都是读操作,原数据可能并未改动,因此没有必要频繁进行备份。为此,Redis提供了可选的配置参数:

格式为:

save <seconds> <changes>

其中<seconds>表示多少秒内至少发生了多少次修改操作(默认配置是save 900 1,表示如果在900秒内至少发生了1次修改,则执行快照操作)。可以根据需求调整这个参数,例如增加保存的频率以提高数据的持久性,或者减少保存的频率以减少磁盘IO开销。

AOF:

AOF(Append Only File)是另一种持久化方式,它将所有对数据的操作记录在AOF文件中。为了保证性能,Redis在内存中建立了一个AOF缓冲区(aof_buf)临时存放操作记录,等到空闲时再写入AOF文件中。

随着时间的推移,AOF文件可能会变得非常庞大。为了解决这个问题,Redis引入了AOF重写功能,会将冗余的指令去掉,同时去掉一些不影响结果的操作,比如数据修改a->b->c,其实只记录最后修改成c的操作就够了。

在重写的过程中,可能会有新的请求进入,导致最终结果不一致的情况。为了解决这个问题,Redis引入了另一个缓存区(aof_rewrite_buf),用于存储重写过程中的新AOF内容。重写完毕后,新AOF文件会检测aof_rewrite_buf并进行补充,最后重命名新AOF文件替换掉原有的文件。当然,重写过程也是通过fork一个子进程完成的,不会影响Redis的正常工作。

高可用的Redis:

为了提高系统的可用性,Redis引入了主从模式和哨兵监控机制。

主-从模式:

主从模式通过一个主节点(Master)负责写数据,多个从节点(Slave)负责读数据,实现读写分离。当主节点挂掉时,从节点可以接管工作,多个Redis一起工作显然比单个Redis更可靠。

主从复制:

主节点会将自己的RDB文件发送给从节点,同时传输期间的新增删除和修改命令,以保证主从数据的一致性。当从节点短暂掉线后重新连接,为了数据一致,主节点会将全部数据发给掉线节点,但这是不必要的,只需要同步缺失的部分数据即可。为此,Redis引入了复制偏移量的概念。

复制偏移量表示了主节点和从节点在复制过程中的同步位置,即已复制的字节数量。主节点会持续地将写入操作记录到复制缓冲区,并将这些写入操作的信息发送给从节点。从节点在接收到主节点发送的复制命令后,会将这些写入操作逐个应用到自己的数据副本中,同时更新自己的复制偏移量。这样,根据偏移量,就可以知道从节点缺失了哪些数据,只需要传输缺失的数据即可。

哨兵监控:

虽然有了主从模式,但每次Redis挂了还需要程序员手动将从节点升级为主节点,不够智能。因此,Redis引入了哨兵(Sentinel)机制。

一个哨兵节点是不够的,万一哨兵挂了怎么办?所以通常会有多个哨兵节点。哨兵节点会定期向Redis节点发送心跳检测,如果在一定时间内未收到响应,哨兵会认为该节点已经下线。但是,一个哨兵节点判断某个主节点下线可能是误判,需要超过一定数量的哨兵节点都认为这个节点下线了,才能确认其真的下线。这就是多数投票机制

当确认主节点真的下线后,哨兵会进行故障转移:

  1. 选举新的主节点:当主节点不可用时,哨兵会从当前的从节点中选举出一个新的主节点。哨兵会考虑到每个从节点的优先级、复制偏移量、复制延迟等因素,选择出一个适合的从节点作为新的主节点。

  2. 更新配置:一旦新的主节点被选举出来,哨兵会更新所有其他节点的配置,以使它们知道新的主节点的位置。

  3. 客户端重定向:在完成故障转移后,哨兵会通知客户端发生了主节点切换,并提供新的主节点的地址。客户端收到通知后,会重新连接到新的主节点上,从而完成故障转移过程。

Redis集群:

随着数据量的不断增加,即使有高可用机制,单个Redis实例也难以应对大规模数据存储的需求。因此,Redis集群(实际上更像是一种分布式架构)应运而生,将数据分布到不同的Redis实例上,每个实例存储不同的数据。

Redis集群将数据划分为16384个槽(Slot),每个槽对应一个唯一的哈希值。每个节点负责管理其中一部分槽,以存储和处理对应的数据。槽的划分和迁移由集群自动管理。程序员可以根据Redis实例的能力大小,分配槽位。

为了提高效率,Redis使用一个大小为16384的数组来记录每个槽位由哪个Redis实例管理,以及对应的IP和端口号。这样,每次数据请求时,可以直接根据数组查找对应的Redis实例。

每个槽位都需要有Redis实例去管理,否则为下线状态。同时,每个单独的集群节点也由主从构成,以防止某个Redis实例突然下线导致槽位无人管理。

通过这种分布式架构,Redis集群能够有效地处理大规模数据存储和高并发访问的需求。

结语:

通过上述技术手段,Redis不仅实现了高效的内存管理,还提供了完善的持久化策略、高可用解决方案以及分布式存储能力,使其成为了一个功能强大且可靠的数据库系统。无论是小型应用还是大型分布式系统,Redis都能提供卓越的性能和稳定性。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号