数据库

redisDb结构中的dict字典保存了数据库中的所有键值对,我们将这字典成为键空间。

redisDb结构的expires字典保存了数据库中所有键的过期时间,我们称这个字典为过期字典。

redis 多库 java redis建库_数据

 

过期键删除策略

  • 定时删除:在设置键的过期时间的同时,创建一个定时器。让定时器在键的过期时间来临时,立即执行对键的删除操作。
  • 优点:过期键尽快删除,释放占用内存
  • 缺点:CPU时间最不友好,在内存不紧张但CPU时间紧张的情况下,将cpu用在删除和当前任务无关的过期键上,无疑会对服务器的响应时间和吞吐量造成影响。此外,时间事件通过无序链表实现,查找时间复杂度为O(N),并不高效。
  • 惰性删除:放任键过期不管,但每次从键空间中获取键时,都检查取得的键是否过期。如果过期的话,就删除该键,如果没有过期,就返回该键。
  • 优点:CPU时间最友好,不会在删除无关过期键上花费任何cpu时间
  • 缺点:内存不友好,过期键不被访问到,也许永远就不会被删除
  • 定期删除:每隔一段时间,程序就对数据库进行一次检查,删除里面的过期键。至于要删除多少过期键,以及要检查多少个数据库,则有算法决定。
  • 定期删除策略是前两种策略的一种整合和折中。根据情况,合理的设置删除操作执行时长和执行频率。

AOF、RDB和复制功能对过期键的处理

RDB

  • 生成文件:在执行Save命令或BgSave命令创建一个新的Rdb文件时,会对数据库中键进行检查,已过期的键不会保存到Rdb文件中。
  • 载入文件:以主服务器模式运行,未过期的键会被载入到数据库中。过期键会被忽略。以从服务器模式运行,所有键不论是否过期,都会被载入到数据库(主从服务器进行数据同步时,从服务器数据库就会被清空。过期键对载入文件的从服务器也不会造成影响)

AOF

  • 文件写入:当过期键没有被删除,那么Aof文件不会因为这个键产生任何影响。当被删除后,会追加一条DEL命令,显式记录该键已被删除
  • 重写:重写过程中,程序会对数据库中的键进行检查,已过期的键不会被保存到重写后的AOF文件中。

复制

当服务器运行在复制模式下,从服务器的过期键删除动作由主服务器控制。

redis 多库 java redis建库_数据_02

从服务器会正常返回键值给客户端,好像键没有过期一样(这是个bug吧)

redis 多库 java redis建库_数据_03

主服务器被访问,删除该过期键,向客户端返回空回复,并向从服务器发送DEL命令。从服务器才删除

RDB持久化

RDB持久化功能生成的文件是一个经过压缩的二进制文件。

  • SAVE命令:阻塞Redis服务器进程,直到创建完毕。阻塞期间,服务器不能处理任何命令请求
  • BGSAVE命令:派生出一个子进程负责创建RDB文件,服务器进程继续处理命令请求。bgsave期间,客户端发送的save、bgsave命令会被服务器拒绝。bgrewriteaof命令会被延迟到bgsave执行完毕。

Rdb文件的载入是在服务器启动时自动执行的,所以没有用于载入Rdb文件的命令。如果服务器开启了AOF持久化功能,服务器会优先使用AOF文件来还原数据。

保存条件

用户可以通过save设置多个保存条件,只要其中任意一个条件被满足,服务器就会执行bgsave命令

如: save 900 1  服务器在900秒内,对数据库进行了至少1次修改

redis 多库 java redis建库_数据库_04

通过saveparams数组,dirty计数器(修改次数),lastsave属性(上一次执行时间),周期性操作函数serverCron默认每隔100毫秒就会执行一次,其中一项工作就是检查save选项所设置的保存条件是否满足。满足,执行bgsave命令。

AOF持久化

AOF持久化是通过保存Redis服务器所执行的写命令来记录数据库状态的。它的实现可以分为命令追加、文件写入、文件同步三个步骤。

命令追加

当AOF持久化功能处于打开状态时,服务器在执行完一个写命令之后,会以协议格式将被执行的写命令追加到服务器状态的aof_buf缓冲区的末尾。

AOF文件写入

通过flushAppendOnlyFile函数,考虑是否将aof_buf缓冲区中内容写入和保存到AOF文件里面。函数行为由appendfsync配置项决定。默认everysec

redis 多库 java redis建库_redis_05

  • always:效率最慢,但也是最安全的。即使故障停机,也只会糗事一个事件循环中所产生的命令数据
  • everysec:从效率讲,足够快,就算故障停机,也只丢失一秒钟的命令数据。
  • no:无须执行同步操作(大部分情况下),Aof文件写入速度是最快的。但单次同步时长通常是三种模式中最长的。故障停机时,将丢失上次同步AOF文件之后的所有写命令数据。

文件同步

为了提高文件的写入效率,在现代操作系统中,当用户调用write函数,将一些数据写入到文件的时候,操作系统通常会将写入数据暂时保存在一个内存缓冲区里面,等到缓冲区空间被填满,或者超过指定时限后,才真正将缓冲区中数据写入到磁盘里面。

AOF文件重写

AOF文件重写并不需要对现有AOF文件进行任何读取、分析或者写入操作,是通过读取服务器当前的数据库状态来实现的。

实现原理:从数据库中读取键现在的值,然后用一条命令去记录键值对,代替之前记录这个键值对的多条命令。

如果服务器对animals键执行了以下命令:

SADD animals "cat"                  // {"cat"}
SADD animals "dog" "panda" "tiger"  // {"cat","dog","panda","tiger"}
SREM animals "cat"                  // {"dog","panda","tiger"}
SADD animals "lion" "cat"           // {"dog","panda","tiger","lion","cat"}

为了记录animals键的状态,AOF文件必须保存上面四条命令。AOF重写,通过读取animals键的值,用一条

SADD animals  "dog" "panda" "tiger" "lion" "cat" 来代替上面的四条命令。

注意:实际中,为了避免在执行命令时造成客户端输入缓冲区异常,在处理列表、哈希表、集合、有序集合时,会先检查元素数量,超过REDIS_AOF_REWRITE_ITEMS_PER_CMD常量(默认64),使用多条命令来记录键的值。

AOF后台重写

AOF重写程序放到子进程执行

  • 子进程重写期间,服务器进程可以继续处理命令请求
  • 子进程带有服务器进程的数据副本,使用子进程而不是线程,可以在避免使用锁的情况下,保证数据的安全性

为了解决重写期间数据未写入新AOF文件的数据不一致问题,Redis服务器设置了AOF重写缓冲区。

redis 多库 java redis建库_数据库_06

当子进程完成AOF重写后,会向父进程发送一个信号,父进程接到信号后,执行以下工作:

  1. 将AOF重写缓冲区中的所有内容写入到新AOF文件中
  2. 对新的AOF文件进行改名,原子地覆盖现有AOF文件,完成新旧两个文件的替换