Redis-单机数据库的实现

本文详细解读了《Redis设计与实现》中关于单机数据库的实现,涉及键的过期时间设置、过期键删除策略(惰性删除、定时删除和定期删除)、AOF和RDB持久化机制,以及客户端和服务器的交互过程。
摘要由CSDN通过智能技术生成

《Redis设计与实现》读书笔记-CSDN博客       

Redis-数据结构与对象-CSDN博客

Redis-单机数据库的实现-CSDN博客

Redis-多机数据库的实现-CSDN博客

        这是《Redis设计与实现》的第二部分-- 单机数据库的实现,对 Redis 实现单机寺久化、AOF持久化、事件等进行了介绍

第9章 数据库

       Redis服务器默认会创建16个数据库,Redis数据库结构如下:

typedef struct redisDb {

  // 数据库键空间,保存着数据库中的所有键值对
  dict *dict;

  // 过期字典,保存着键的过期时间
  dict *expires;
}

9.4 设置键的生存时间或者过期时间

1. 设置过期时间

        虽然有多种不同单位和不同形式的设置命令,但实际上EXPIRE、PEXPIRE、EXPIREAT三个命令都是使用PEXPIREAT命令来实现的

EXPIRE <key> <ttl>

将键key的生存时间设置为ttl秒

PEXPIRE<key> <ttl>

将键key 的生存时间设置为ttl毫秒

EXPIREAT<key> <timestamp>

将键key的过期时间设置为timestamp所指定的秒数时间戳

PEXPIREAT<key> <timestamp>

将键key的过期时间设置为timestamp所指定的毫秒数时间戳。

2. 保存过期时间

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

  1. 过期字典的键是一个指针,这个指针指向键空间中的某个键对象(也即是某个数据库键)。

  2. 过期字典的值是一个long long类型的整数,这个整数保存了键所指向的数据库键的过期时间———一个毫秒精度的UNIX时间戳。

3. 计算并返回剩余生存时间

通过计算键的过期时间和当前时间之间的差来实现的

TTL命令以秒为单位返回键的剩余生存时间,PTTL命令则以毫秒为单位返回键的剩余生存时间:

4. 过期键的判定

通过过期字典,程序可以用以下步骤检查一个给定键是否过期:

1)检查给定键是否存在于过期字典:如果存在,那么取得键的过期时间。

2)检查当前UNIX时间戳是否大于键的过期时间:如果是的话,那么键已经过期;否则的话,键未过期。

9.5 过期键删除策略

三种不同的删除策略,第一种和第三种为主动删除策略,而第二种则为被动删除策略。

定时删除:在设置键的过期时间的同时,创建一个定时器( timer ),让定时器在键的过期时间来临时,立即执行对键的删除操作。

惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查取得的键是否过期,如果过期的话,就删除该键;如果没有过期,就返回该键。

定期删除:每隔一段时间,程序就对数据库进行一次检查,删除里面的过期键。至于要删除多少过期键,以及要检查多少个数据库,则由算法决定。

9.5.1 定时删除

        定时删除策略对内存是最友好的:通过使用定时器,定时删除策略可以保证过期键会尽可能快地被删除,并释放过期键所占用的内存。

        另一方面,定时删除策略的缺点是,它对CPU时间是最不友好的,将CPU时间用在删除和当前任务无关的过期键上,会对服务器的响应时间和吞吐量造成影响。

9.5.2 惰性删除

        惰性删除策略对CPU时间来说是最友好的:程序只会在取出键时才对键进行过期检查,这可以保证删除过期键的操作只会在非做不可的情况下进行,并且删除的目标仅限于当前处理的键,这个策略不会在删除其他无关的过期键上花费任何CPU时间。

        惰性删除策略的缺点是,它对内存是最不友好的:如果一个键已经过期,而这个键又仍然保留在数据库中,那么只要这个过期键不被删除,它所占用的内存就不会释放。

9.5.3 定期删除

定期删除策略是前两种策略的一种整合和折中

9.6 Redis 的过期键删除策略

Redis使用的是惰性删除和定期删除两种策略,实现CPU和内存使用的平衡。

9.6.1 惰性删除策略的实现

过期键的惰性删除策略是由expireIfNeeded函数对输入键进行检查:

如果输入键已经过期,那么expireIfNeeded函数将输人键从数据库中删除。

如果输入键未过期,那么expireIfNeeded 函数不做动作。

9.6.2 定期删除策略的实现

        在规定的时间内,分多次遍历服务器中的各个数据库,从数据库的expires字典中随机检查一部分键(默认随机选取20个键)的过期时间,并删除其中的过期键。因为删除操作对CPU使用有影响,因此若在规定时间内没有遍历完所有的数据库,则会记录当前删除程序的进度,并在下一次删除的时候,接着上一次的进度进行处理。比如说,如果当前activeExpirecycle函数在遍历10号数据库时返回了,那么下次activeExpirecycle函数执行时,将从11号数据库开始查找并删除过期键。

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

《Redis设计与实现》读书笔记-CSDN博客

9.7.1 RDB

        在执行SAVE命令或者BGSAVE命令创建一个新的RDB文件时,程序会对数据库中的键进行检查,已过期的键不会被保存到新创建的RDB文件中。

        在启动Redis服务器时,如果服务器开启了RDB功能,那么服务器将对RDB文件进行载入:

         如果服务器以主服务器模式运行,那么在载人RDB文件时,程序会对文件中保存的键进行检查,未过期的键会被载入到数据库中,而过期键则会被忽略,所以过期键对载人RDB文件的主服务器不会造成影响。

          如果服务器以从服务器模式运行,那么在载入RDB文件时,文件中保存的所有键,不论是否过期,都会被载入到数据库中。不过,因为主从服务器在进行数据同步的时候,从服务器的数据库就会被清空,所以一般来讲,过期键对载入RDB文件的从服务器也不会造成影响。

9.7.2 AOF

        当过期键被惰性删除或者定期删除之后,程序会向AOF文件追加( append)一条DEL命令,来显式地记录该键已被删除。

        在执行AOF重写的过程中,程序会对数据库中的键进行检查,已过期的键不会被保存到重写后的AOF文件中。

9.7.3 复制

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

        主服务器在删除一个过期键之后,会显式地向所有从服务器发送一个DEL命令,告知从服务器删除这个过期键。

        从服务器在执行客户端发送的读命令.,即次性到过Mn是继续像处理未过期的键一样来处理过期键。从服务器只有在接到主服务器发来的DEL命令之后,才会删除过期键。

9.8 数据库通知

键空间通知( key-space notification ):“某个键执行了什么命令”的通知

键事件通知( key-event notification):它们关注的是“某个命令被什么键执行了”。

第10章 RDB持久化

10.1 RDB保存与载入

        RDB文件生成有两个命令,sava和bgsave。这两个命令的区别在于save执行时,redis服务会进行阻塞。而bgsava是单独创建一个子进程来进行保存,所以不会阻塞redis服务。此外,bgsave和save命令不能同时执行。

        当redis服务重启时,会去检测是否有RDB文件存在,若存在则会自动载入RDB文件。当AOF文件和RDB文件同时存在时,因为AOF文件的更新频率通常比RDB文件的更新频率高,所以如果服务器开启了AOF持久化功能,那么服务器会优先使用AOF文件来还原数据库状态。只有在AOF持久化功能处于关闭状态时,服务器才会使用RDB文件来还原数据库状态。服务器在载入RDB文件期间,会一直处于阻塞状态,直到载入工作完成为止。

10.2 自动间隔性保存

        Redis服务为了实现自动间隔保存这个功能,其原理是配置自动保存的触发条件,若300s中对redis操作了3次就触发一次自动保存。根据这个思路做了以下几件事:

  1. 配置自动保存规则

  2. 记录上次保存时间lastsave及修改(写入,删除,更新)次数dirty

  3. 默认每隔100毫秒检查是否满足保存规则。如果满足的话,就执行bgsave。

第11章 AOF持久化

        AOF(Append Only File)也是redis提供的一种持久化方式。与RDB保存数据库数据不同的,AOF保存的是redis的命令。当redis重启时,可以通过载入并执行AOF文件中的命令即可还原Redis保存的数据。

        AOF持久化分为三步:当执行了一个写命令时,将写命令保存到redis服务的aof_buf缓冲区。然后数据从aof_buf缓冲区写入系统的内存中,然后在将系统内存中的数据写入到磁盘的AOF文件中,从而真正实现持久化。

        对于aof_buf写入系统内存,再写入AOF文件这个路径,AOF提供了三种策略。以此三种策略,每次执行写命令,都会写入aof_buf,然后再写入系统内存,区别在于从内存写入文件的时机不同。

行为

优点

缺点

always

每次执行写命令都会写入AOF文件

安全

最慢

everysec

若上次由内存写入文件的时间超过了1S,则会再次写入文件

足够快,

停机,会丢失1S的命令

no

系统内存满了之后,由系统自动写入文件。

写入内容的速度最快

写入文件的间隔最长,若出现问题丢失的数据也最多

        当需要使用AOF文件进行数据还原时,服务器只需要读入并重新执行一遍AOF文件里面保存的写命令即可。但是随着服务器运行时间的流逝,AOF文件保存的命令会越来越多,文件的体积也会越来越大,为了解决AOF文件体积膨胀的问题,Redis提供了AOF文件重写功能。文件重写功能通过读取服务器当前的数据库状态来实现的。首先从数据库中读取键现在的值,然后用一条命令去记录键值对,代替之前记录这个键值对的多条命令,这就是AOF重写功能的实现原理。

        当需要进行AOF文件重写时,Redis会重新重现一个子进程来执行重写。父进程继续处理命令请求。就会导致新的命令可能对现有的数据库状态进行修改,使得服务器当前的数据库状态和重写后的AOF文件所保存的数据库状态不一致。为了解决数据不一致的问题,Redis设置了一个AOF重写缓冲区,当执行完一个写命令之后,会同时将这个写命令发送给AOF缓冲区和AOF重写缓冲区。

        AOF缓冲区的内容会定期被写入和同步到AOF文件,对现有AOF文件的处理工作会如常进行。从创建子进程开始,服务器执行的所有写命令都会被记录到AOF重写缓冲区里面.当子进程完成AOF 重写工作之后,它会向父进程发送一个信号,父进程在接到该信号之后,会调用一个信号处理函数,并执行以下工作:

1)将AOF重写缓冲区中的所有内容写入到新AOF文件中,这时新AOF 文件所保存的数据库状态将和服务器当前的数据库状态一致。

2)对新的AOF文件进行改名,原子地( atomic)覆盖现有的AOF文件,完成新旧两个AOF文件的替换。

第13章 客户端

客户端以链表的形式保存。

        客户端状态fd--套接字描述符,客户端分为伪客户端和普通客户端。伪客户端会在Redis启动执行AOF文件时或者执行Lua脚本时使用,其fd的属性为-1.对于普通客户端,fd为大于-1的整数,普通客户端使用套接字来与服务器进行通信。

        此外可以为每个客户端设置名字,默认为null。flags属性则可以记录客户端的角色和状态,不再细讲。客户端也会维护输入缓冲区来保存客户端发出的命令,最大不能超过1G,否则客户端就会被关闭。此外还有两个输出缓冲区,一个大小固定(16kb)来保存长度较小的回复,一个大小可变来保存比较长的回复

        Redis服务器在初始化启动的时候会创建两个伪客户端。一个是负责执行Lua脚本的lua_client,它在Redis服务器的整个生命周期中会一直存在。另一个是负责执行AOF文件的伪客户端,载入AOF文件后悔关闭,

第14章 服务器

14.1  命令请求的执行过程

        当用户在客户端输入一个命令请求时,客户端会将该命令进行协议格式的转换,然后发给服务器,服务器在接收到命令后会存在该客户端对应的输入缓冲区中,然后去缓冲区中的命令进行分析,并提取命令中包含的参数。随后使用命令执行器执行命令。

        在执行命令时,首先会在命令字典表中查看该命令是否存在,若存在,则会继续执行一些预备操作,如身份检查,参数检查等。最后在执行命令。服务器在执行完之后会继续执行后续工作,如检查是否加入慢查询日志,检查是否加入AOF文件等。

在执行完命令之后会将结果发送给客户端,客户端接收并打印回复。

14,2 serverCron 函数

         serverCron函数默认每隔100毫秒执行一次,它的工作主要包括更新服务器状态信息,包括更新服务器时间缓存,更新对象的空转时间,判断客户端是否正常连接以及客户端的缓冲区是否超过阈值来管理客户端资源和数据库状态,检查并执行持久化操作等等。

14.3 服务器启动过程

        服务器从启动到能够处理客户端的命令请求需要执行以下步骤:1)初始化服务器状态。包括设置服务器的运行id,设置默认的配置文件路径,默认的端口号等;2)载入用户给配置的服务器参数;,如端口号,如果用户没有配置,则使用第一步的默认参数。3)初始化服务器数据结构;如db数据,即初始化所有的数据库,初始化client(客户端)链表4)还原数据库状态;5)执行事件循环,这样服务器就可以接收客户端的命令了。

  • 21
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值