Redis读书笔记二:单机数据库

179 阅读11分钟

一、redis数据库

1、键空间

redisDb结构中的字典dict,保存着数据库中所有的键值对,称之为键空间。键空间的键即为数据库的键,键空间的值即为数据库的值,对redis数据库的添加、删除、更新、查找等操作,实际上是对键空间这个字典的增删改查操作。

2、过期字典

redisDb结构中的字典expires,保存了数据库中所有键的过期时间,称之为过期字典。过期字典的键是指向键空间中的某个键对象,过期字典的值是一个long long类型的整数,保存了键所指向的数据库键的过期时间,其是一个精确到毫秒的UNIX时间戳。

所有为redis键设置过期时间、生存时间的操作,最终都会转化为PEXPIREAT命令,其可以为一个键设置毫秒级的过期时间。

3、过期键删除策略

a、定时删除

在设置键的过期时间的同时设置一个定时器,当过期时间到来时进行删除。

虽然对内存最友好,但是对CPU时间压力很大,需要创建大量的定时器,会影响到服务器的响应时间和吞吐量。

b、惰性删除

过期键放任不管,当每次从键空间读取键值时,都检查是否过期,如果过期则删除掉。

不需要额外花费CPU时间,但是对内存会造成很大的浪费,如果一个键一直不被访问,则其一定不会被删除。

c、定期删除

每隔一段时间,程序就对数据库进行一次检查,删除掉过期的键。

可以避免上述问题,但是需要控制删除操作执行的时长和频率来降低对CPU和内存的影响。

d、redis服务器的过期删除策略

采用了定期删除和惰性删除两个配合使用的策略。

e、主从服务器

当主服务器删除一个过期键后,他会向所有的从服务器发送一条DEL命令,显式地删除过期键;

当从服务器发现过期键后,不会进行删除,而是等待主服务器发来的DEL命令,这种统一、中心化的过期键删除策略可以保证主从服务器的数据一致性。

二、RDB持久化

RDB持久化可以在一定的触发条件下,将redis在内存中的数据库状态,即所有的键值对数据,保存在磁盘里面,避免数据的丢失。

1、RDB文件载入

只要redis服务器启动时检测到有RDB文件的存在,就会自动载入RDB文件;

但是如果开启了AOF持久化功能,由于AOF同步时效性更好,服务器会优先使用AOF文件来还原数据库的状态;

2、SAVE和BGSAVE

SAVE命令是由服务器进程直接执行保存操作,会阻塞整个服务器;

BGSAVE命令由子线程执行保存操作,不会阻塞服务器,但是在执行BGSAVE期间,所有的SAVE命令、BGSAVE命令都会被拒绝,BGREWRITEAOF命令会被延迟执行;

3、自动间隔保存

通过redis服务器的save选项,可以设置多个RDB的保存条件,只要有其中一个(比如300秒内发生了10次变更操作)被满足,即会触发BGSAVE命令;

dirty计数器记录了距离上一次成功执行SAVE命令或者BGSAVE命令之后,服务器对数据库状态进行了多少次修改(包括写入、删除、更新等);

lastsave属性是一个UNIX时间戳,记录了服务器上一次成功执行SAVE或者BGSAVE命令的时间;

redis服务器周期性操作函数serverCorn默认每隔100ms就会执行一次,该函数会检查服务器是否满足了在save选项中设置的保存条件,如果满足的话即会触发BGSAVE命令。

4、RDB文件结构

一个RDB文件由REDIS、db_version、database、EOF、check_sum,五个部分顺序排列组成;

REDIS部分是写死的这五个字符,目的是快速检查所载入的文件是否为RDB文件;

db_version长度为4个字节,值是一个字符串表示的整数,表示RDB文件的版本号;

database可以分为多个database,比如数据库0即为database0,每个数据库都可以保存为保存为SELECTDB、db_number、key_value_pairs三个部分;

其中SELECTDB常量长度为1字节,标示是这个database的开始,db_number则代表数据库号码,而key_value_pairs保存了数据库中所有键值对的数据;

EOF常量的长度为1字节,标示RDB文件正文内容的结束,当程序读入遇到这个值的时候,它能知道所有的键值对已经导入了;

check_sum是一个8字节长的无符号整数,保存着一个校验和,是通过对其他四个部分的内容进行计算得出的,以此来检验文件是否有出错和损坏。

三、AOF持久化

AOF持久化是通过保存redis服务器所执行的写命令来记录数据库的状态,文件中的所有命令都以redis命令请求协议的格式保存。

1、AOF的实现

可以分为追加(append)、文件写入、文件同步(sync)三个步骤

当AOF功能开启后,服务器会在执行完一个命令后,以协议的格式将执行的命令追加到服务器状态的aof_buf缓冲区末尾;

redis服务器周期性操作函数serverCorn在每次执行时,都会调用flushAppendOnlyFile函数,其考虑的是是否需要将aof_buf缓冲区中的内容写入和保存到AOF文件中,具体行为由appendfsync选项决定;

0200926220731026.png)

2、AOF文件的载入与数据还原

由于AOF文件包括了重建数据库状态的所有命令,所以只要服务器将其读入并且重新执行,即可还原服务器关闭之前的状态,具体步骤如下:

a、创建一个不带网络链接的伪客户端;(由于redis的命令只能在客户端的上下文中执行,于是使用了伪客户端的形式来恢复数据库状态)

b、从AOF文件中分析并读出一条写命令;

c、使用伪客户端执行被读出的写命令;

d、一直执行步骤b和c,直到AOF中的写命令都被处理完毕。

3、AOF重写

如果不对文件加以控制,那么AOF文件中的内容会越来越多,体积过大可能对redis服务器造成影响,且使用文件来进行数据库还原的所需的时间就会越多。

为了解决AOF文件膨胀的问题,redis实现了AOF重写功能,服务器创建一个新的AOF文件来替换现有的AOF文件,新的文件不会包含任何浪费空间的冗余命令,其体积通常会小的多;

这个功能,是通过读取服务器当前的数据库状态来实现的,首先从数据库中读取键现在的值,然后用一条命令去记录键值对,替代之前记录这个键值对的命令,比如set a a,set a b,最终只会保存set a b命令。

AOF也是通过后台调用aof_rewrite函数来进行重写,但是在重写过程中,服务器还是可以处理新的命令请求,这个请求会带来保存数据的不一致。

所以redis除了维护一个AOF缓冲区外,还维护了一个AOF重写缓冲区,当客户端命令到来的时候,其会将执行后的写命令追加到AOF缓冲区和AOF重写缓冲区中。

当对AOF缓冲区进行重写时,新的命令仍然可以追加到AOF重写缓冲区中,重写完成后,会将AOF重写缓冲区中的内容写入到新的AOF文件中,然后对新的AOF文件进行改名,原子地覆盖现有的AOF文件,完成新旧AOF文件的交替。

四、事件

redis服务器是一个事件驱动程序,服务器需要处理的事件可以分为文件事件和时间事件。

1、文件事件

是基于Reactor模式实现的网络通信程序,这里记录一下自己对Reactor模式的理解:

A、单线程IO模型,是最简单的形式,即一个while循环扫描是否有需要处理的IO事件,有的话进行阻塞式处理,其无法并发,处理效率低,且一个请求没处理完之前,剩余的只能被阻塞;

B、多线程IO模型,即每来一次连接,都新建一个线程处理,这种方式的缺点是资源消耗过多,并且线程的反复创建和销毁也需要开销,如果连接数过高,系统将会无法承载;

C、单线程Reactor模型,相当于维持一个事件的列表,每有一个新的连接过来,都会在这个列表上进行注册。其有一个reactor线程,不停的通过while循环扫描连接列表,当有IO事件发生时,其会调用对应的handler来处理。这样可以避免资源消耗过多,只有发生IO事件时才进行处理,而不是整个连接过程都在处理;

D、多线程Reactor模型,即将执行handler的操作维护在一个线程池中,这样即可通过非阻塞的方式来处理IO事件。

文件事件可以分为AE_READABLE(读)和AE_WRITABLE(写)两类事件。

2、时间事件

时间事件分为定时事件和周期性事件,其中定时事件只在指定的时间到达一次,周期性事件每隔一段时间到达一次

服务器在一般情况下只执行serverCorn函数这一个时间事件,其为周期性事件;

文件事件和时间事件是合作关系,服务器会轮流执行这两种事件,并且处理事件的过程也不会进行抢占;

由于在一个时间周期内,是先执行文件事件,再执行时间事件,所以很多情况下时间事件会比预期的时间要更晚一些。

五、客户端

1、客户端属性

客户端状态结构使用clients链表连接多个客户端状态,新添加的客户端状态会被放在链表的末尾;

客户端的flag属性使用不同的标示来表示客户端的角色,以及客户端当前所处的状态。

2、客户端状态

输入缓存区记录了客户端发送的命令请求,这个缓冲区大小不可以超过1GB;

缓冲区可以分为固定大小缓存区和可变大小缓冲区两种,其中固定大小为16KB,一般存储一些短回复("OK"等);可变大小缓冲区的最大大小不可以超过服务器设置的硬性标准;

如果输出缓冲区的大小超过了服务器设置的硬性限制,客户端会被立即关闭,此外,如果客户端在一定时间内,超过了服务器设置的软性标准,也会被关闭;

3、伪客户端

服务器会在以下两种情况下创建自己的伪客户端连接:

A、处理Lua脚本的伪客户端在服务器初始化时就会被创建,会一直存在直到服务器关闭;

B、载入AOF文件时使用的伪客户端会在载入工作开始时动态创建,载入工作完成后关闭。

六、服务端

1、命令请求过程

一个命令请求从发送到完成主要包括以下步骤:

a、客户端将命令请求发送给服务器;

b、服务器读取命令请求,并解析出命令参数;

c、命令执行器根据参数查找命令的实现函数,然后执行对应的函数并给出命令回复;

d、服务器将命令返回给客户端。

2、severCorn函数

默认每100ms执行一次,执行工作主要包括更新服务器状态信息,处理服务器接收的SIGTERM信号,管理客户端资源和数据库状态,检查并执行持久化操作等。

3、服务器启动过程

a、初始化服务器状态;

b、载入服务器配置;

c、初始化服务器数据结构;

d、还原数据库状态;

e、执行事件循环。