Redis在处理客户端请求时,通常使用单线程来进行读取、解析、执行和响应返回,因此被称为单线程应用。在4.0版本之前,这一描述是准确的。
从4.0版本开始,Redis开始使用后台线程来处理一些耗时的操作,如清理脏数据、释放超时连接、删除大key等,但网络读写和执行命令仍然是单线程处理的。
Redis 之所以使用单线程是因为 Redis 执行的是纯内存的操作,Redis 服务的瓶颈不在 CPU,而是在网络和内存。单线程避免了不必要的上下文切换和竞争条件,也无需考虑锁的问题,整个线程模型比较简单。
多线程模型虽然在某些方面表现优异,但是它却引入了程序执行顺序的不确定性,带来了并发读写的一系列问题,增加了系统复杂度、同时可能存在线程切换、甚至加锁解锁、死锁造成的性能损耗。Redis 通过事件模型以及 IO 多路复用等技术,处理性能非常高,因此没有必要使用多线程来执行命令。
Redis 6.0使用多条IO线程从IO队列中读取请求并进行解析,然后将解析后的命令交给主线程执行。虽然Redis 6.0支持多线程,但执行命令的线程仍然只有主线程一个,因此仍然被认为是单线程执行的。但Redis 6.0通过使用多线程IO突破了单线程IO的瓶颈,发挥了多核的优势。
当 Redis Server 收到一个请求的时候,先会进入 IO 队列,多条 IO 线程会从 IO 队列里面读取请求并进行解析,解析之后的命令就会交给 Redis 主线程进行执行;执行后的结果会重新进入 IO 队列,等待 IO 线程将响应返回给客户端。
上面已经说了,Redis是基于单个主线程 + IO 多线程的线程模型,
关于Reactor模型的基本介绍之前写过了:博客
Reactor模型有三种——单Reactor单线程、单Reactor多线程、多Reactor多线程,Redis是基于第二种单Reactor多线程,是基于事件驱动的、多路复用的、异步非阻塞I/O的线程模型。
事件驱动的I/O模型(Event-driven I/O Model)是一种高效的I/O处理模型,主要用于处理多个I/O事件。在事件驱动的I/O模型中,程序会监听多个I/O事件,并在事件发生时触发相应的处理程序进行处理,而不是等待I/O操作完成。 这种模型避免了阻塞式I/O操作可能带来的性能问题,同时也提高了系统的并发性能。
而AIO虽然也是异步非阻塞的(基于发布订阅模式),但和事件驱动还是不一样的:博客
总的来说,AIO Server需要有一个线程来处理读写事件,在等待OS内核处理读写事件并异步通知此线程的时候可以继续做其他的事情。和Reactor相比其实现的机制还是很不一样的。
select、poll、epoll操作系统底层原理:博客
在缓存系统中,常见的过期策略有以下几种:
定时过期策略(TTL):为每个缓存条目设置一个过期时间(Time To Live),当缓存超过这个时间时,缓存条目就会被自动删除。这种策略适用于缓存对象的过期时间比较固定的场景。
惰性过期策略(LRU):当缓存空间不足时,删除最近最少使用(Least Recently Used)的缓存条目。这种策略适用于缓存对象的访问模式是热点访问的场景。
定期过期策略:周期性地清理缓存中的过期条目,以保证缓存空间的有效利用。这种策略适用于缓存对象的过期时间比较长的场景。
基于容量的过期策略(LFU):根据缓存条目的使用频率(Least Frequently Used)来删除缓存条目,以保证缓存空间的有效利用。这种策略适用于缓存空间有限的场景。
基于热度的过期策略(Hot Key):根据缓存条目的热度(Hot Key)来删除缓存条目,以保证缓存空间的有效利用。这种策略适用于缓存对象的访问模式是热点访问的场景。
在 Redis 中并没有采用定时过期策略,而是选择了惰性过期策略(第二种)和定期过期策略(第三种)的组合。
Redis不会主动删除过期的key,而是在客户端访问这个key时,Redis会检查这个key是否过期,如果过期了就会删除这个key。
惰性删除的实现原理是基于Redis的请求响应模型。当客户端请求一个key时,Redis会首先检查这个key是否过期,如果过期了就将其删除,并返回一个不存在的响应。如果这个key没有过期,就返回对应的value。
需要注意的是,惰性删除不是实时的,过期key可能会长时间存在于内存中,直到有客户端请求时才被删除。这可能会导致内存浪费,需要根据实际情况选择合适的过期策略。
Redis的过期字典是一种特殊的字典结构,它用于存储带有过期时间的key和对应的value。过期字典的实现基于哈希表和跳跃表,可以以时间复杂度O(1)的速度进行新增、删除和查找操作。
具体来说,当我们向Redis存储一个带有过期时间的key时,Redis会将其添加到过期字典中。过期字典的键是带有过期时间的key,值是一个指向存储这个key的数据结构的指针。过期字典中的每个键都对应一个过期时间,用来记录这个key何时过期。
Redis在每个 事件循环 中都会检查 一部分 过期字典中的key,并将过期的key删除。为了避免一次检查过多的key,Redis会将所有的过期key分散到不同的时间点上,并使用定时器来监控这些时间点。当Redis在某个时间点上检查到一个过期key时,就会将其删除。
过期字典的实现基于哈希表和跳跃表。哈希表用于快速查找key,而跳跃表则用于按照过期时间排序key。当Redis需要检查过期key时,它会遍历跳跃表,找到已经过期的key,并将其从哈希表和跳跃表中删除。
Redis的过期字典每次检查的过期key数量是由服务器配置的一个参数决定的,这个参数叫做hz。hz是Redis服务器每秒运行的事件循环数量,它决定了每秒钟服务器会检查多少个过期key。默认的hz值是10,也就是每秒钟会检查10个过期key。
Redis的事件循环是一个基于事件驱动的异步非阻塞的网络通信模型,它通过监听和处理事件来实现网络通信和其他任务的处理。在Redis中,事件是指网络通信中发生的各种事件,例如客户端连接、读写事件、定时器事件等。
为了处理这些事件,Redis使用了一个事件处理器,它会监听事件,并根据事件的类型和状态来执行相应的操作。事件处理器包含多个事件状态,每个状态都对应一类事件。例如,REDIS_READ_EVENT状态表示读事件,REDIS_WRITE_EVENT状态表示写事件,REDIS_TIME_EVENT状态表示定时器事件等。
当一个事件到达时,Redis会将其添加到事件队列中,并在下一个事件循环中处理它。在事件循环中,Redis会遍历事件队列,并根据事件的类型和状态来执行相应的操作。例如,对于一个客户端连接事件,Redis会建立一个新的客户端连接,并将其添加到客户端列表中;对于一个读事件,Redis会从客户端连接中读取数据并进行处理;对于一个定时器事件,Redis会执行相应的定时任务等等。
需要注意的是,Redis的事件循环是单线程的,因此在任何时候只会有一个事件循环正在执行。这意味着在事件循环中处理事件时,应尽量避免执行耗时较长的操作,以免影响其他事件的处理效率。
Redis的一个事件循环会处理多个事件,具体处理的事件数量取决于事件队列中的事件数量以及服务器配置的参数。在事件循环中,Redis会遍历事件队列,并根据事件的类型和状态来执行相应的操作。
事件队列是Redis存储事件的数据结构,它是一个先进先出(FIFO)的队列。当一个事件到达时,Redis会将其添加到事件队列中,并在下一个事件循环中处理它。如果事件队列中有多个事件,Redis会按照它们的顺序依次处理。
Redis的事件循环是以时间为依据划分的。具体来说,Redis会定时执行事件循环,每个事件循环的时间长度由服务器配置的参数决定。在每个事件循环中,Redis会处理所有已经到达的网络请求和定时任务,并等待下一个事件循环的到来。
Redis的事件循环的时间长度由服务器配置的参数hz决定。hz表示Redis服务器每秒运行的周期性任务数量,它决定了每秒钟服务器会执行多少个事件循环。默认的hz值是10,也就是每秒钟执行10个事件循环。
采用懒惰删除机制可以简化Redis的实现。如果Redis需要立即删除过期key,那么它需要在每个事件循环中执行删除操作,这会增加代码的复杂度和维护成本。采用懒惰删除机制可以将删除操作集中在一起,简化代码实现和维护。
采用懒惰删除机制可以简化Redis的实现。如果Redis需要立即删除过期key,那么它需要在每个事件循环中执行删除操作,这会增加代码的复杂度和维护成本。采用懒惰删除机制可以将删除操作集中在一起,简化代码实现和维护。
Redis的缓存内存淘汰机制是指在内存不足时,Redis会根据一定的规则和算法自动删除一些缓存数据,以释放一部分内存空间,以保证Redis服务的正常运行。Redis的缓存内存淘汰机制主要有以下几种(用户可选择):
LRU算法:LRU全称是Least Recently Used,即最近最少使用。LRU算法的原理是根据数据的访问时间来淘汰缓存数据,即淘汰最近最少被访问的数据。当内存不足时,Redis会自动删除最近最少使用的缓存数据,以释放一部分内存空间。
LFU算法:LFU全称是Least Frequently Used,即最不经常使用。LFU算法的原理是根据数据的访问频率来淘汰缓存数据,即淘汰访问频率最低的数据。当内存不足时,Redis会自动删除访问频率最低的缓存数据,以释放一部分内存空间。
TTL算法:TTL全称是Time To Live,即生存时间。TTL算法的原理是根据缓存数据的过期时间来淘汰数据。当缓存数据的过期时间到期时,Redis会自动删除这些数据,以释放一部分内存空间。
Random算法:Random算法的原理是随机选择一些缓存数据进行删除,以释放一部分内存空间。当内存不足时,Redis会随机选择一些缓存数据进行删除,以释放一部分内存空间。
需要注意的是,Redis的缓存内存淘汰机制并不是完全准确的,它只是在一定程度上保证了Redis服务的可用性和稳定性。在实际使用Redis时,应根据实际情况选择合适的内存淘汰机制,以保证缓存数据的有效性和完整性。