Redis学习记录——数据库、持久化、线程模型

142 阅读6分钟

数据库

  • redisServer的db数组,每一个db数组都是一个redisDb结构,代表一个数据库
  • 默认16个数据库
  • 切换数据库命令 select 2
  • redisDb 成为键空间

redisDb

本质就是两个字典,一个保存kv,一个保存k-过期时间

读写键空间的维护操作

  • 读取一个键后,会更新键空间明智次数或者不命中次数,可以在INFO stats命令的keyspace_hits、keyspace_misses 属性查看
  • 更新键的lru
  • 如果发现这个键已过期,会先删除这个过期键,
  • 如果有使用watch监视该键,会将该键标记为脏,
  • 每次修改一个键后,都会对脏键计数器加1,会触发持久化或者复制操作。

设置过期时间

  • 本质都是pexpireat命令来实现,pexpireat key time(在该时间过期)
  • 保存在db 的expires字典里,key 是键的指针,value long 过期的时间
  • pttl key 返回 key 毫秒级的剩余生存时间
  • 过期的判断:
    • 是否存在于expires字典
    • 当前时间戳是否大于键的过期时间

过期策略:

  • 定时删除:设置键过期时间时,创建一个定时器,到点删除,对内存优好,对cpu非常不友好
  • 惰性删除:获取键时,判断是否过期,可能会导致很多键过期了不删除,对内存不友好,对cpu非常有好
  • 定期删除:每隔一段时间,选取一定的键,判断是否过期,过期就删除
  • 一般使用惰性删除+定期删除

定期删除的过程:

  • 一定量的数据库中抽取一定数量的随机减进行检查,删除过期键
  • 全局变量current_db 记录当前检查的进度,如果是10,那下次从11号数据库开始
  • 全检查完后,current_db 重置为0

aof、rdb对过期键的处理

首先,aof、rdb 都不会因为过期键而造成错误的影响

rdb:

  • 执行save或者bgsave时,会检查,如果已经过期的键,不会写在rdb文件中。
  • 载入rdb时,也会对键进行判断,如果过去也不会载入数据库中
  • 如果是从服务器,过期的键也会载入,保证和主服务器的数据一致,当主服务器删除过期键时,会主动发一条del命令,删除从服务器的键。

aof

  • 当过期键被惰性删除或者定期删除后,会在aof文件里追加del命令,删除该键
  • aof重写时,也会进行检查过期键,过期的键不会写入,因为重写aof的原理,后面会说

复制的时候

  • 主服务器发现一个键过期后,会显式的向所有从服务器发送一条del命令
  • 从服务器发现后,也不删,等主服务器,保证一致性。

rdb 持久化

  • 两个命令 save,bgsave
  • save 是主进程进行持久化,会阻塞服务的处理
  • bgsave 是fork一个子进程进行持久化,不会阻塞服务的处理
  • 因为aof更新频率更高,如果服务器开启了aof,会优先使用aof文件来恢复数据库状态。
  • 执行bgsave时:
    • save命令会被拒绝
    • 再次的bgsave命令也会被拒绝
    • bgrewriteaof 命令会被延迟到bgsave 执行完后执行。因为这俩命令都是子进程执行,如果同时fork俩子进程进行大量的io操作,会严重影响主进程性能。

bgsave 执行的条件

  • 允许配置一定的条件选项,让服务每隔一段时间自动执行bgsave;比如
     默认配置
   save 900 1 服务在900s内,对数据库至少进行了一次修改
   save 300 10  服务在300s内,对数据库至少进行了10次修改
   save 60 10000  服务在60s内,对数据库至少进行了20000次修改
  • 使用 saveparams 结构保存,一个秒数,一个修改数,在redisServer里,
  • redisServer里还有一个dirty计数器,用来纪律上次执行完save或者bgsave后,对数据库进行了多少次修改
  • redisServer里还有 一个lastsave属性,距离上一次save或者bgsave 的时间

rdb 文件格式

  • 开始表示:REDIS,5字节,读到了这个,表示这是一个rdb文件
  • db_version:4字节,记录了rdb文件的版本号,比如说0006代表rdb文件第六版
  • databases :包含0 或任意多个数据库的数据
  • EOF 常量:1字节,代表rdb文件结束
  • check_num:8字节无符号整数,对上面4个部分内容计算得到的一个值,用来校验rdb文件是否损坏。

aof持久化

分为以下三步:

  • 命令追加:服务器执行一个写命令后,会将命令追加到redisServer的aof_bug缓冲区末尾
  • 文件写入
  • 文件同步

文件的写入和同步

redis服务器就是事件循环loop,在每一次循环中sercerCron中,会根据配置来判断是否将aof_buf缓冲区的内容写入aof中,

  • always:总是写入aof文件,并且同步
  • everysec:写入aof文件,距离上次同步超过1s,就同步。默认配置
  • no:写入aof文件,是否同步根据操作系统来决定

aof文件的载入

  • 启动一个不带网络连接的伪客户端,
  • 读取aof文件,伪客户端执行命令,循环直到完成。

aof文件重写原理

  • 其实aof文件重写不需要读取aof文件

  • 遍历数据库中的键:

    • 过期的就不写了
    • 用一条写命令重写,比如sadd animals k1 k2 k3
    • 如果这个列表或者字典什么的太大,会用多条写命令代替,避免造成客户端输入缓冲区溢出
    • 默认是64个元素
  • 后台执行,避免重写时不一致,使用aof重写缓冲区

aof重写过程:

  • 1.创建一个子进程,重写aof文件,并将此时的写操作记录到aof重写缓冲区中,
  • 2.子进程完成重写后,会向父进程发送一个信号,此时服务阻塞,将aof重写缓冲区的内存写入的aof文件中,原子性的替换文件,完成重写。

redis 线程模型

  • 基于事件驱动的reactor模型,应该也是基于epoll的

多个线程(IO Thread)各自维护一个独立的事件循环。整体模型是由 Main 线程负责接收新连接,并分发给 IO Thread 去独立处理读写事件、解析请求命令,但是具体命令的执行还是使用main 线程来执行,最后使用IO 线程回写响应给客户端。

IO线程轮训socket列表读事件,然后解析为redis命令,并把解析好的命令放到全局待执行队列,然后主线程从全局待执行队列读取命令然后具体执行命令,最后把响应结果分配到不同IO线程,由IO线程来具体执行把响应结果写回客户端。

image.png