【Redis】单机数据库实现

数据库

Redis服务器将所有数据库都保存在redisSever结构的db数组中,db数组的每一项都是一个redisDb结构,代表了一个数据库。一般默认初始化16个数据库。可以通过SELECT指令切换数据库。

Redis是一个键值对数据库服务器,redisDb结构的dict字典保存了数据库中的所有键值对,将这个字典成为键空间。键空间的键是数据库的键,每个键都是字符串对象。值可以是任意一种Redis对象。并且还有一个expires字典保存键过期信息。

读写键空间是的维护操作

  • miss与hit
    在读取一个键之后(包括读操作和写操作),服务器会根据键是否存在,更新服务器中命中率(hit)和不命中(miss)的次数。

  • LRU更新
    在读取一个键之后,服务器会更新LRU,用于计算闲置时间。

  • 删除过期键
    在读取时候发现某个键已经过期,会先删除这个过期键,再执行其他操作。

  • 标记dirty
    如果由客户端使用WATCH指令监视了某个键,那么服务器在对被监视的键进行修改的之后,会把这个键标记为dirty,从而让事务处理器注意。

  • dirty计数器增加
    服务器每次修改一个键之后,都会对dirty计数器增1.这个计数器会触发服务器的持久化和复制操作。

  • 发送数据库通知
    如果开启了数据库通知,在对键进行修改之后,服务器会按照配置发送数据库通知。

键的生存过期时间

通过EXPIRE或者PEXPIRE可以对某个键只是生存时间,在过了生存时间之后,服务器会删除生存时间为0的键。EXPIRE<key><ttl>设置生存时间为ttl秒,EXPIREAT<key><timestamp>某个键的过期时间设置为timestamp指定的秒。

redisDb结构中expires字典保存了数据库中所有键的过期时间,称为过期字典。过期字典的键是一个指针,指向键空间的某个键对象,值是一个long long类型的整数,保存了过期时间(键空间的键和过期字典的键都指向同一个键对象,节省空间)。

PERSIST可以移除一个键的过期时间,TTL可以以秒为单位返回键的剩余空间。

过期键的处理

  • 过期键的判定

首先检查键是否存在与过期字典,如果存在返回过期时间;检查当前UNIX时间是否大于过期时间。

  • 过期键删除策略
  1. 定时删除:创建一个定时器,在过期时间来临时候删除。内存友好,CPU不友好,并不使用因为时间事件是无序队列, O ( N ) O(N) O(N)复杂度。
  2. 惰性删除:放任过期时间不管,在再次访问改键时候进行判定。CPU友好,内存不友好。如果不去访问,相当于是一种内存泄漏。
  3. 定期删除:每隔一段时间对数据库进行检查,删除过期键。折衷的方法。
  • Redis中的惰性与定期删除

惰性:所有的命令执行前都会调用expireIfNeed,检查过期情况。因此每个命令的实现函数都需要能执行键存在和不存在两种情况。

定期:由activeExpireCycle执行,redis服务器的周期性程序severCron被执行时,该方法被调用。在规定的时间内,分多次遍历服务器的各个数据库。每次运行时,从一定数量的数据库中选择一定数量的随机键检查并删除过期键,并保存每次的进度,下次再被调用时候继续运行。

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

  • RDB对过期键的处理

在生成RDB文件时候,会检查过期情况,因此RDB文件不会包含过期键。载入过程中,如果是主服务器,会对过期进行检查,过期数据不会被载入,从服务器则全部加载。但是也不会有影响,因为从服务器会被主服务器同步。

  • AOF对过期键的处理

AOF生成过程中,如果键已经过期但是没有被显式删除则不会对AOF有影响,在被惰性或者定期删除之后,程序会在AOF文件之后跟一个DEL命令。AOF重写中程序会检查过期情况,因此不会被存入AOF。

  • 复制操作对过期键的处理
    主服务器在删除一个键之后,会显式的向从服务器发送del指令;从服务在读取到过期键之后,不会进行任何操作而是正常返回,只是为了保持一致。

持久化

Redis时内存数据库,因此速度快,但是我们需要把数据存回硬盘,因此需要持久化。

RDB持久化

RDB持久化功能所生成的RDB文件时一个经过压缩的二进制文件,通过该文件可以还原生成的RDB文件。

RDB文件的创建与载入

两个指令可以生成RDB文件SAVEBGSAVE。SAVE会阻塞Redis服务器进程,直到EDB文件创建完毕。BGSAVE会派生出一个子进程,然后子进程负责创建RDB文件,父进程继续处理命令。

Redis的载入是服务器启动时候自动执行的。需要注意的是,由于AOF的更新频率必RDB高,因此如果服务器开启了AOF持久化,服务器会优先采用AOF来恢复。

BGSAVE执行期间,客户端的SAVE和BGSAVE指令会被拒绝,BGREWRITEAOF会被延迟到BGSAVE执行完毕;如果BGREWRITEAOF正在执行,BGSAVE会被拒绝。

自动保存

一般BGSAVE会在一定条件下自动执行,如900s以内数据库执行了至少一次修改;300s内数据库执行了10次修改;60s内执行了10000次修改。这里依靠了两个全局变量,一个是dirty计数器,统计了上次保存之后,服务器进行了多少次修改,一个是lastsave保存了上次的成功save的事件戳。Redis的周期性函数severCon每100ms执行一次检查,如果满足条件就BGSAVE。

RDB文件结构

redis五个字符,db_version四个字节保存了版本号,EOF是结尾标志,check_sum是检验和。
在这里插入图片描述

SELECTDB是一个标识,db_number表示数据库号码。key_value_pairs是键值对。对于不同类型的键值对会不同的保存方式。
在这里插入图片描述

AOF持久化

AOF(Append Only File)与RDB直接保存数据库中的键值对来记录数据库的状态不同,AOF持久化是通过保存服务器所执行的写命令来记录数据库状态的。被写入AOF文件的所有命令都是以Redis命令请求协议格式保存的。

AOF持久化的实现

在服务器执行完一个写命令以后,会在服务器状态的 aof_buf 缓冲区末尾追加一个写命令。并定期的同步到AOF文件中。

Redis的服务器进程就是一个事件循环,有两种事件,一个是文件事件负责接收客户端的命令请求,以及发送回复到客户端。另外时间事件负责执行类似severCron函数这样需要定时运行的函数。

因此服务器每次结束一个事件循环之前,都会调用flushAppendOnlyFile的函数,考虑是否将 aof_buf 缓冲区的内容写入AOF文件中。其中分为三个时间参数:always每次都将缓冲区的全部数据写入AOF文件中并同步;everysec如果上次同步时间距离现在超过1s,则进行一次同步,这个过程是一个线程专门执行的;no只是写入AOF但是不同步到硬盘。

AOF载入过程中,回有一个伪客户端执行AOF的命令,直到完成全部恢复工作。

AOF重写

为了压缩AOF文件的大小,redis提供了AOF文件重写功能,可以创建一个新的AOF文件来替代现有的AOF文件,也就是BGREWEITEAOF

事实上AOF的重写不需要对现有的AOF文件进行任何的读写,而是用个服务器状态实现。在重写过程中会忽略过期的键。重写名列最多包含64个键值对,否则还是要多个语句。

为了提高性能,redis将AOF的重写程序放在了子进程中执行,这样服务器进程可以正常相应客户端的请求,并且子进程有服务器进程的数据副本,可以保证数据安全。同时为了解决不一致问题,redis服务器设置了一个AOF重写缓冲区,在子进程创建之后,redis的写命令会同时给AOF缓冲区和AOF重写缓冲区。在完成AOF的重写后,子进程给父进程发送一个信号,父进程阻塞并且将重写缓冲区的内容写入AOF文件中,并原子的覆盖原AOF文件,完成整个任务。

事件

redis服务器是一个事件驱动模型,包括文件事件和时间事件。redis服务器通过套接字与客户端进行连接,而文件事件就是服务器对套接字操作的抽象。

文件事件

文件事件处理器被用于处理文件事件。虽然redis是单线程的,但是处理器采用IO多路复用,监听多个嵌套字,提高效率。事件处理器分为四个组成部分,套接字,IO多路复用,文件事件分派器,事件处理器。

在这里插入图片描述

客户端

服务器

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值