Redsi的持久化+淘汰策略

Redis持久化机制

RDB

RDB是Redis用来进行持久化的一种方式,是把当前内存中的数据集快照写入磁盘,也就是快照(数据库中所有键值对数据)。恢复时是将快照文件直接读到内存里。

两种触发方式

手动触发
  • save:执行了 save 命令,就会在主线程生成 RDB 文件,由于和执行操作命令在同一个线程,所以如果写入 RDB 文件的时间太长,会阻塞主线程;在此期间,不能执行其他命令,直到持久化完成。
  • bgsave:该命令不会阻塞redis,在后台进行的。该触发方式会fork一个子进程,由子进程负责持久化,因此阻塞只会发生在fork子进程的时候。
自动触发
# 时间策略
	save 900 1 #表示900 秒内如果至少有 1 个 key 的值变化,则保存
	save 300 10 #表示300 秒内如果至少有 10 个 key 的值变化,则保存
	save 60 10000 #表示60 秒内如果至少有 10000 个 key 的值变化,则保存
	
	# 设置快照的文件名,默认是 dump.rdb
	dbfilename dump.rdb
	
	# 设置快照文件的存放路径,这个配置项一定是个目录,而不能是文件名。默认是和当前配置文件保存在同一目录。也就是说通过在配置文件中配置的 save 方式,当实际操作满足该配置形式时就会进行 RDB 持久化,将当前的内存快照保存在 dir 配置的目录中,文件名由配置的 dbfilename 决定。
	dir /home/work/app/redis/data/
	
	# 默认值为yes。当启用了RDB且最后一次后台保存数据失败,Redis是否停止接收数据。这会让用户意识到数据没有正确持久化到磁盘上,否则没有人会注意到灾难(disaster)发生了。如果Redis重启了,那么又可以重新开始接收数据了
	
	stop-writes-on-bgsave-error yes
	# 默认值是yes。对于存储到磁盘中的快照,可以设置是否进行压缩存储。如果是的话,redis会采用LZF算法进行压缩。如果你不想消耗CPU来进行压缩的话,可以设置为关闭此功能,但是存储在磁盘上的快照会比较大。
	rdbcompression yes
	
	# 默认值是yes。在存储快照后,我们还可以让redis使用CRC64算法来进行数据校验,但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。
	rdbchecksum yes

配置参数是save,其实是bgsave命令,是通过fork子进程来生成RDB文件

除了根据redis配置文件配置规则自动触发,还有下面三种触发方式:

1、从节点全量复制时,主节点发送一个rdb文件给从节点完成复制操作时,主节点会触发bgsave。

2、执行debug reload时自动触发RDB持久化。

3、执行shutdown时,若没有开启aof 也会触发RDB持久化。

持久化流程

在这里插入图片描述

  1. Redis父进程首先判断:当前是否在执行save,或bgsave/bgrewriteaof(aof文件重写命令)的子进程,如果在执行则bgsave命令直接返回。
  2. 父进程执行fork(调用OS函数复制主进程)操作创建子进程,这个过程中父进程是阻塞的,Redis不能执行来自客户端的任何命令。
  3. 父进程fork后,bgsave命令返回”Background saving started”信息并不再阻塞父进程,并可以响应其他命令。
  4. 子进程创建RDB文件,根据父进程内存快照生成临时快照文件,完成后对原有文件进行原子替换。(RDB始终完整)
  5. 子进程发送信号给父进程表示完成,父进程更新统计信息。
  6. 父进程fork子进程后,继续工作。

这里注意的是 fork 操作会阻塞,导致Redis读写性能下降。我们可以控制单个Redis实例的最大内存,来尽可能降低Redis在fork时的事件消耗。以及上面提到的自动触发的频率减少fork次数,或者使用手动触发,根据自己的机制来完成持久化。

执行快照的时候,数据能被修改吗?

Redis采用**写时复制技术(Copy-On-Write, COW)**执行 bgsava 命令的时候,会通过 fork() 创建子进程,此时子进程和父进程是共享同一片内存数据的,因为创建子进程的时候,会复制父进程的页表,但是页表指向的物理内存还是一个。
在这里插入图片描述

只有在发生修改内存数据的情况时,物理内存才会被复制一份。在这里插入图片描述

这样的目的是为了减少创建子进程时的性能损耗,从而加快创建子进程的速度,毕竟创建子进程的过程中,是会阻塞主线程的。

所以,创建 bgsave 子进程后,由于共享父进程的所有内存数据,于是就可以直接读取主线程里的内存数据,并将数据写入到 RDB 文件。

当主线程对这些共享的内存数据也都是只读操作,那么,主线程和 bgsave 子进程相互不影响。

但是,如果主线程要修改共享数据里的某一块数据(比如键值对 A)时,就会发生写时复制,于是这块数据的物理内存就会被复制一份(键值对 A',然后主线程在这个数据副本(键值对 A')进行修改操作。与此同时,bgsave 子进程可以继续把原来的数据(键值对 A)写入到 RDB 文件

父进程修改的某个数据只是该数据的复制品。这里再深入一点,Redis内存中的全量数据由一个个的"数据段面"组成,每个数据段页面的大小为4K,客户端要修改的数据在哪个页面中,就会复制一份这个页面到内存中,这个复制的过程称为"页面分离"。

就是这样,Redis 使用 bgsave 对当前内存中的所有数据做快照,这个操作是由 bgsave 子进程在后台完成的,执行时不会阻塞主线程,这就使得主线程同时可以修改数据。

bgsave 快照过程中,如果主线程修改了共享数据,发生了写时复制后,RDB 快照保存的是原本的内存数据,而主线程刚修改的数据,是被办法在这一时间写入 RDB 文件的,只能交由下一次的 bgsave 快照。

所以 Redis 在使用 bgsave 快照过程中,如果主线程修改了内存数据,不管是否是共享的内存数据,RDB 快照都无法写入主线程刚修改的数据,因为此时主线程的内存数据和子线程的内存数据已经分离了,子线程写入到 RDB 文件的内存数据只能是原本的内存数据。

如果系统恰好在 RDB 快照文件创建完毕后崩溃了,那么 Redis 将会丢失主线程在快照期间修改的数据。

另外,写时复制的时候会出现这么个极端的情况。在 Redis 执行 RDB 持久化期间,刚 fork 时,主进程和子进程共享同一物理内存,但是途中主进程处理了写操作,修改了共享内存,于是当前被修改的数据的物理内存就会被复制一份。再次极端情况下,**如果所有的共享内存都被修改,则此时的内存占用是原先的 2 倍。**所以,针对写操作多的场景,我们要留意下快照过程中内存的变化,防止内存被占满了

RDB优缺点

  • 优点

    • RDB 是紧凑的二进制文件,比较适合备份,全量复制等场景
    • RDB 恢复数据远快于 AOF
  • 缺点

    • RDB 无法实现实时或者秒级持久化;
    • 新老版本无法兼容RDB格式。
    • 且执行快照是一个比较重的操作,如果频率太频繁,可能会对Redis性能产生影响。如果频率太低,服务器故障就会丢失的数据更多。

AOF

使用AOF持久化时,Redis会将每一个收到的写命令都通过write函数追加到文件中,类似于MySQL的binlog。当Redis重启时会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。当然由于OS会在内核中缓存write做的修改,所以可能不是立即写到磁盘上。这样AOF方式的持久化也还是有可能会丢失部分修改。

开启AOF

# 是否开启aof
appendonly yes

# 文件名称
appendfilename "appendonly.aof"

# 同步方式
appendfsync everysec
#always:每次命令写入aof_buffer后都会调用fsync同步到aof文件,fsync完成后线程才返回。线程每次都会等fsync完成后才返回,性能较差,数据安全性最高
#everysec:命令先写入aof_buffer,然后调用系统write操作,write操作完成后线程就返回,由后台专门的线程每秒执行一次fsync操作。推荐使用;最多丢失2s内的数据。
#no:命令先写入到aof_buffer,然后调用系统write操作,write操作后线程返回,fsync操作由操作系统负责,时间不确定。fsync由系统负责,数据安全性无法确保,并且会加大每次fsync同步到硬盘的数据量。不推荐

# aof重写期间是否同步
no-appendfsync-on-rewrite no

# 重写触发配置 
#表示当前aof文件大小超过上一次aof文件大小的百分之多少的时候就会进行重写。如果之前没有重写过,就以启动时的aof文件大小为准
auto-aof-rewrite-percentage 100
#限制允许重写最小aof文件的大小。也就是文件大小下雨64MB的时候,不需要进行优化。
auto-aof-rewrite-min-size 64mb

# 加载aof时如果有错如何处理
aof-load-truncated yes
#如果该配置启用,在加载时发现aof尾部不正确是,会向客户端写入一个log,但是会继续执行,如果设置为 `no` ,发现错误就会停止,必须修复后才能重新加载。

# 文件重写策略
aof-rewrite-incremental-fsync yes

Redis 是先执行写操作命令后,才将该命令记录到 AOF 日志里的。

  • 好处
    • 避免额外的检查开销
      • 如果先将写操作命令记录到 AOF 日志里,再执行该命令的话,如果当前的命令语法有问题,那么如果不进行命令语法检查,该错误的命令记录到 AOF 日志里后,Redis 在使用日志恢复数据时,就可能会出错。
      • 而如果先执行写操作命令再记录日志的话,只有在该命令执行成功后,才将命令记录到 AOF 日志里,这样就不用额外的检查开销,保证记录在 AOF 日志里的命令都是可执行并且正确的。
    • 不会阻塞当前写操作命令的执行 当写操作命令执行成功后,才会将命令记录到 AOF 日志。
  • 缺点
    • 执行写操作命令和记录日志是两个过程,那当 Redis 在还没来得及将命令写入到硬盘时,服务器发生宕机了,这个数据就会有丢失的风险
    • 由于写操作命令执行成功后才记录到 AOF 日志,所以不会阻塞当前写操作命令的执行,但是可能会给「下一个」命令带来阻塞风险 将命令写入到日志的这个操作也是在主进程完成的(执行命令也是在主进程),也就是说这两个操作是同步的。硬盘IO压力大就会造成写入阻塞

AOF原理

在这里插入图片描述

三种回写策略
  1. Redis 执行完写操作命令后,会将命令追加到 server.aof_buf 缓冲区;
  2. 然后通过 write() 系统调用,将 aof_buf 缓冲区的数据写入到 AOF 文件,此时数据并没有写入到硬盘,而是拷贝到了内核缓冲区 page cache,等待内核将数据写入硬盘;
  3. 具体内核缓冲区的数据什么时候写入到硬盘,由内核决定。

redis.conf 配置文件中的 appendfsync 配置项可以有以下 3 种参数可填。此参数就是控制第三步,具体什么时候写入硬盘:

  • Always:每次命令写入aof_buffer后都会调用fsync同步到aof文件,fsync完成后线程才返回。线程每次都会等fsync完成后才返回,性能较差,数据安全性最高
  • Everysec:命令先写入aof_buffer,然后调用系统write操作,write操作完成后线程就返回,由后台专门的线程每秒执行一次fsync操作。推荐使用;最多丢失2s内的数据。
  • No:命令先写入到aof_buffer,然后调用系统write操作,write操作后线程返回,fsync操作由操作系统负责,时间不确定。fsync由系统负责,数据安全性无法确保,并且会加大每次fsync同步到硬盘的数据量。不推荐

这 3 种写回策略都无法能完美解决「主进程阻塞」和「减少数据丢失」的问题,因为两个问题是对立的,偏向于一边的话,就会要牺牲另外一边,原因如下:

  • Always 策略的话,可以最大程度保证数据不丢失,但是由于它每执行一条写操作命令就同步将 AOF 内容写回硬盘,所以是不可避免会影响主进程的性能;
  • No 策略的话,是交由操作系统来决定何时将 AOF 日志内容写回硬盘,相比于 Always 策略性能较好,但是操作系统写回硬盘的时机是不可预知的,如果 AOF 日志内容没有写回硬盘,一旦服务器宕机,就会丢失不定数量的数据。
  • Everysec 策略的话,是折中的一种方式,避免了 Always 策略的性能开销,也比 No 策略更能避免数据丢失,当然如果上一秒的写操作命令日志没有写回到硬盘,发生了宕机,这一秒内的数据自然也会丢失。
写回策略写回时机优点缺点
Always同步写回可靠性高,最大程度保证数据不丢失每个写命令都要写回硬盘,性能开销大
Everysec每秒写回性能适中宕机时会最多丢失2秒以内的数据
No有系统控制写回性能好宕机时丢失的数据可能会更多。

三种回写策略只是控制fsync() 函数的调用时机。当应用程序向文件写入数据时,内核通常先将数据复制到内核缓冲区中,然后排入队列,然后由内核决定何时写入硬盘。在这里插入图片描述

如果想要应用程序向文件写入数据后,能立马将数据同步到硬盘,就可以调用 fsync() 函数,这样内核就会将内核缓冲区的数据直接写入到硬盘,等到硬盘写操作完成后,该函数才会返回。

AOF重写机制

AOF 日志是一个文件,随着执行的写操作命令越来越多,文件的大小会越来越大。如果当 AOF 日志文件过大就会带来性能问题,比如重启 Redis 后,需要读 AOF 文件的内容以恢复数据,如果文件过大,整个恢复的过程就会很慢。Redis 为了避免 AOF 文件越写越大,提供了 AOF 重写机制,当 AOF 文件的大小超过所设定的阈值后,Redis 就会启用 AOF 重写机制,来压缩 AOF 文件。

AOF 重写机制是在重写时,读取当前数据库中的所有键值对,然后将每一个键值对用一条命令记录到「新的 AOF 文件」,等到全部记录完后,就将新的 AOF 文件替换掉现有的 AOF 文件。

举个例子,在没有使用重写机制前,假设前后执行了「set name zhangsan」和「set name lisi」这两个命令的话,就会将这两个命令记录到 AOF 文件。

但是在使用重写机制后,就会读取 name 最新的 value(键值对) ,然后用一条 「set name lisi」命令记录到新的 AOF 文件,之前的第一个命令就没有必要记录了,因为它属于「历史」命令,没有作用了。这样一来,一个键值对在重写日志中只用一条命令就行了。

重写工作完成后,就会将新的 AOF 文件覆盖现有的 AOF 文件,这就相当于压缩了 AOF 文件,使得 AOF 文件体积变小了。

然后,在通过 AOF 日志恢复数据时,只用执行这条命令,就可以直接完成这个键值对的写入了。

所以,重写机制的妙处在于,尽管某个键值对被多条写命令反复修改,最终也只需要根据这个「键值对」当前的最新状态,然后用一条命令去记录键值对,代替之前记录这个键值对的多条命令,这样就减少了 AOF 文件中的命令数量。最后在重写工作完成后,将新的 AOF 文件覆盖现有的 AOF 文件。

这里说一下为什么重写 AOF 的时候,不直接复用现有的 AOF 文件,而是先写到新的 AOF 文件再覆盖过去。

因为如果 AOF 重写过程中失败了,现有的 AOF 文件就会造成污染,可能无法用于恢复使用。

所以 AOF 重写过程,先重写到新的 AOF 文件,重写失败的话,就直接删除这个文件就好,不会对现有的 AOF 文件造成影响。

AOF 后台重写

写入 AOF 日志的操作虽然是在主进程完成的,因为它写入的内容不多,所以一般不太影响命令的操作。

但是在触发 AOF 重写时,比如当 AOF 文件大于 64M 时,就会对 AOF 文件进行重写,这时是需要读取所有缓存的键值对数据,并为每个键值对生成一条命令,然后将其写入到新的 AOF 文件,重写完后,就把现在的 AOF 文件替换掉。

这个过程其实是很耗时的,所以重写的操作不能放在主进程里。

所以,Redis 的重写 AOF 过程是由后台子进程 *bgrewriteaof* 来完成的,这么做可以达到两个好处:

  • 子进程进行 AOF 重写期间,主进程可以继续处理命令请求,从而避免阻塞主进程;
  • 子进程带有主进程的数据副本,**这里使用子进程而不是线程,因为如果是使用线程,多线程之间会共享内存,那么在修改共享内存数据的时候,需要通过加锁来保证数据的安全,而这样就会降低性能。**而使用子进程,创建子进程时,父子进程是共享内存数据的,不过这个共享的内存只能以只读的方式,而当父子进程任意一方修改了该共享内存,就会发生「写时复制」,于是父子进程就有了独立的数据副本,就不用加锁来保证数据安全。

子进程是怎么拥有主进程一样的数据副本的呢?

主进程在通过 fork 系统调用生成 bgrewriteaof 子进程时,操作系统会把主进程的「页表」复制一份给子进程,这个页表记录着虚拟地址和物理地址映射关系,而不会复制物理内存,也就是说,两者的虚拟空间不同,但其对应的物理空间是同一个。
在这里插入图片描述

这样一来,子进程就共享了父进程的物理内存数据了,这样能够节约物理内存资源,页表对应的页表项的属性会标记该物理内存的权限为只读

不过,当父进程或者子进程在向这个内存发起写操作时,CPU 就会触发缺页中断,这个缺页中断是由于违反权限导致的,然后操作系统会在「缺页异常处理函数」里进行物理内存的复制,并重新设置其内存映射关系,将父子进程的内存读写权限设置为可读写,最后才会对内存进行写操作。
在这里插入图片描述

写时复制顾名思义,在发生写操作的时候,操作系统才会去复制物理内存,这样是为了防止 fork 创建子进程时,由于物理内存数据的复制时间过长而导致父进程长时间阻塞的问题。

当然,操作系统复制父进程页表的时候,父进程也是阻塞中的,不过页表的大小相比实际的物理内存小很多,所以通常复制页表的过程是比较快的。

不过,如果父进程的内存数据非常大,那自然页表也会很大,这时父进程在通过 fork 创建子进程的时候,阻塞的时间也越久。

所以,有两个阶段会导致阻塞父进程:

  • 创建子进程的途中,由于要复制父进程的页表等数据结构,阻塞的时间跟页表的大小有关,页表越大,阻塞的时间也越长;
  • 创建完子进程后,如果子进程或者父进程修改了共享数据,就会发生写时复制,这期间会拷贝物理内存,如果内存越大,自然阻塞的时间也越长;

触发重写机制后,主进程就会创建重写 AOF 的子进程,此时父子进程共享物理内存,重写子进程只会对这个内存进行只读,重写 AOF 子进程会读取数据库里的所有数据,并逐一把内存数据的键值对转换成一条命令,再将命令记录到重写日志(新的 AOF 文件)。

但是子进程重写过程中,主进程依然可以正常处理命令。

如果此时主进程修改了已经存在 key-value,就会发生写时复制,注意这里只会复制主进程修改的物理内存数据,没修改物理内存还是与子进程共享的

所以如果这个阶段修改的是一个 bigkey,也就是数据量比较大的 key-value 的时候,这时复制的物理内存数据的过程就会比较耗时,有阻塞主进程的风险。

还有个问题,重写 AOF 日志过程中,如果主进程修改了已经存在 key-value,此时这个 key-value 数据在子进程的内存数据就跟主进程的内存数据不一致了,这时要怎么办呢?

为了解决这种数据不一致问题,Redis 设置了一个 AOF 重写缓冲区,这个缓冲区在创建 bgrewriteaof 子进程之后开始使用。

在重写 AOF 期间,当 Redis 执行完一个写命令之后,它会同时将这个写命令写入到 「AOF 缓冲区」和 「AOF 重写缓冲区」
在这里插入图片描述

也就是说,在 bgrewriteaof 子进程执行 AOF 重写期间,主进程需要执行以下三个工作:

  • 执行客户端发来的命令;
  • 将执行后的写命令追加到 「AOF 缓冲区」;
  • 将执行后的写命令追加到 「AOF 重写缓冲区」;

当子进程完成 AOF 重写工作(扫描数据库中所有数据,逐一把内存数据的键值对转换成一条命令,再将命令记录到重写日志)后,会向主进程发送一条信号,信号是进程间通讯的一种方式,且是异步的。

主进程收到该信号后,会调用一个信号处理函数,该函数主要做以下工作:

  • 将 AOF 重写缓冲区中的所有内容追加到新的 AOF 的文件中,使得新旧两个 AOF 文件所保存的数据库状态一致;
  • 新的 AOF 的文件进行改名,覆盖现有的 AOF 文件。

信号函数执行完后,主进程就可以继续像往常一样处理命令了。

在整个 AOF 后台重写过程中,除了发生写时复制会对主进程造成阻塞,还有信号处理函数执行时也会对主进程造成阻塞,在其他时候,AOF 后台重写都不会阻塞主进程。

AOF的优缺点

  • 优点

    • 可以更好地保护数据不丢失
    • 适合做灾难性的误删除紧急恢复。
  • 缺点

    • 对于同一份文件,AOF 文件要比 RDB 快照大;
    • AOF 开启后,会对写的 QPS 有所影响,相对于 RDB 来说 写 QPS 要下降;
    • 数据库恢复比较慢(因为 Redis 执行命令由单线程负责的,而 AOF 日志恢复数据的方式是顺序执行日志里的每一条命令), 不合适做冷备。

AOF+RDB混合持久化机制

基本上都不会是单独使用每一种方式解决持久化的。因为都存在问题。4.0版本的混合持久化默认关闭的,通过aof-use-rdb-preamble配置参数控制,yes则表示开启,no表示禁用,5.0之后默认开启。

混合持久化是通过bgrewriteaof完成的,不同的是当开启混合持久化时,fork出的子进程先将共享的内存副本全量的以RDB方式写入aof文件,然后在将重写缓冲区的增量命令以AOF方式写入到文件,写入完成后通知主进程更新统计信息,并将新的含有RDB格式和AOF格式的AOF文件替换旧的的AOF文件。简单的说:新的AOF文件前半段是RDB格式的全量数据后半段是AOF格式的增量数据,

优点: 混合持久化结合了RDB持久化 和 AOF 持久化的优点, 由于绝大部分都是RDB格式,加载速度快,同时结合AOF,增量的数据以AOF方式保存了,数据更少的丢失。

缺点: 兼容性差,一旦开启了混合持久化,在4.0之前版本都不识别该aof文件,同时由于前部分是RDB格式,阅读性较差

RDB和AOF的对比

  • redis提供了rdb持久化方案,为什么还要aof?优化数据丢失问题,rdb会丢失最后一次快照后的数据,aof丢失不会超过2秒的数据

  • 如果aof和rdb同时存在,听谁的?aof

  • rdb和aof优势劣势:rdb 适合大规模的数据恢复,对数据完整性和一致性不高 ,在一定间隔时间做一次备份,如果redis意外down机的话,就会丢失最后一次快照后的所有操作aof 根据配置项而定。官方建议 两种持久化机制同时开启,如果两个同时开启 优先使用aof持久化机制

  • Redis以主服务模式运行的时候,RDB不会保存过期键值对的数据,Redis以从服务器模式运行,RDB会保存过期键值。,当主服务器向从服务器同步时,在清空过期键值对。

  • AOF写入文件时,会对过期的key追加一条del命令,当重写AOF时,会忽略过期key和del命令

Redis的过期策略

Redis 可以看作是一个内存数据库,通过 Maxmemory 指令配置 Redis 的数据集使用指定量的内存。设置 Maxmemory 为 0,则表示无限制。当内存使用达到 Maxmemory 极限时,需要使用某种淘汰算法来决定清理掉哪些数据,以保证新数据的存入。

# redis.conf
#配置Redis的内存大小
maxmemory 100mb
  • 定时过期:每个设置过期时间的key 都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。
  • 惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
    • 4.0之前懒惰删除默认是在主线程删除并释放key内存的,即这整个操作是同步的,这会造成主线程的 同步阻塞;
    • 4.0+版本又做了优化,释放key内存可以放到了异步线程中去做了 (lazy-free),目的是可以减少主线程释放内存的耗时,提升主线程处理性能 。
  • 定期过期:每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。
    • 从过期字典中挑选出20个KEY;删除20个key中已经过期的;如果删除的key占比超过25%,则继续挑选20个key。该操作其实是一种贪心算法。
    • 基于以上逻辑为了解决循环过度导致线程卡死的现象,在算法上加上了超时时间的机制,默认时间是25ms。 扫描频率:默认是每秒10次过期扫描。

Redis会在RedisDB中的expires字典中会保存所有设置了过期时间的key的过期时间数据。key是指向键空间(Redis集群中保存的所有键)中的某个键的指针,value保存的该键的毫秒精度的UNIX时间戳表示的过期时间。Redis使用了惰性过期+定期过期的两种过期策略。

Redis的淘汰策略

redis4.0之前有6种淘汰策略,4.0之后又增加了两种,一共8种淘汰策略。

  • 针对设置了过期时间的key做处理:

    • volatile-ttl:从设置过期时间的数据集中挑选将要过期的数据淘汰,ttl值越大越优先被淘汰。
    • volatile-random:从已设置过期时间的数据集中任意选择数据淘汰。当内存达到限制无法写入非过期时间的数据集时,可以通过该淘汰策略在主键空间中随机移除某个key。
    • volatile-lru:从设置过期时间的数据集中挑选最少使用的数据进行淘汰,根据lru算法
    • volatile-lfu:从设置过期时间的数据集中挑选最少使用的数据进行淘汰。根据lfu算法
  • 针对所有key的处理

    • allkeys-random:从数据集中选择任意数据淘汰。
    • allkeys-lru:从数据集中挑选最近最少使用的数据淘汰,根据lru算法,该策略要淘汰的key面向的是全体key集合。
    • allkeys-lfu:从数据集中挑选最近最少使用的数据淘汰,根据lfu算法,该策略要淘汰的key面向的是全体key集合。
  • noeviction:禁止驱逐数据,也就是当内存不足以容纳新入数据时,新写入操作就会报错,请求可以继续进行,线上任务也不能持续进行,采用no-enviction策略可以保证数据不被丢失,这也是系统默认的一种淘汰策略。

  • LRU和LFU

    • LRU: 淘汰最近一段时间被访问次数很少的数据,以最近一次访问时间做参考**(最近最少使用)**
    • LFU:淘汰最近一段时间内被访问次数最少得数据,以次数作为参考**(最不经常使用)**
# 配置淘汰策略
maxmemory-policy volatile-lru
  • 30
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值