读书笔记-《Redis设计与实现》(二)单机数据库实现(上)

相比前面我们学习的数据结构与对象(读书笔记-《Redis设计与实现》(一)数据结构与对象(上)读书笔记-《Redis设计与实现》(一)数据结构与对象(下)),这部分的内容可以说就是轻松 + 愉快了,只要能 Get 到这几个机制的要点就行。


01

数据库

Redis 将所有数据库都保存在 redisServer 结构中,客户端结构为 redisClient,它们的关键属性如下:

cpp 复制代码
struct redisServer {
    // 一个数组,保存着服务器中的所有数据库
    redisDb *db;
    
    // 服务器的数据库数量,默认是16
    int dbnum; 
    
    // 记录了保存条件的数组,它包括秒数和修改次数属性
    struct saveparam *saveparams;
    
    // 修改计数器
    long long dirty;
    
    // 上一次保存的时间
    time_t lastsave;
    
    // AOF 缓冲区
    sds aof_buf;
    
    // ...
}

typedef struct redisClient {
    // 客户端正在使用的数据库
    redisDb *db;
   
    // ...
} redisClient;

typedef struct redisDb {
    // 数据库键空间,保存着数据库中的所有键值对。
    // key 只能是字符串对象,value 则是五种类型中的一种
    dict *dict;
    
    // 过期字典,保存着键的过期时间。
    // key 指向上面 dict 的 key,value 为过期时间的毫秒时间戳
    dict *expires 
    
    // ...
}

这里需要重点看看 Redis 过期键的删除策略,一般来说,有三种思路:

  • 定时删除:每设置一个带过期时间的键就创建一个定时器,定时器到点后执行删除。特点是 CPU 开销大,内存开销小。

  • 惰性删除:不做处理,仅在对该键执行操作时进行判断,如果过期则删除。特点是 CPU 开销小,内存开销大,甚至可能导致内存泄露。

  • 定期删除:每隔一段时间,对数据库进行检查并删除过期的键盘。特点是兼顾 CPU 和 内存。

Redis 的过期键删除策略使用的是惰性删除 + 定期删除

惰性删除由 db.c/expireIfNeeded 函数实现,所有对键的读写命令在执行前,都会先调用该函数。

定期删除由 redis.c/activeExpireCycle 函数实现,每当 Redis 的服务器周期性操作函数 serverCron 执行时,就会调用该函数。


02

RDB 持久化

RDB(Redis Database)持久化就是将内存中的数据存储到磁盘,生成一个 RDB 文件,可手动执行或服务器根据配置定期执行。

手动执行 的命令为 SAVE (阻塞服务器进程)和 BGSAVE(派生出一个子进程,此时 Redis 会尽量减少别的内存开销,例如之前提到的 rehash )。

自动执行 则是根据时间修改次数来判断的,例如在300秒内修改了10次数据库,就执行一次 BGSAVE。相关属性在上面 redisServer 的代码中可以看到,当然,这些数字也可以通过配置修改。

RDB 文件是一个经过压缩的二进制文件,其结构如下:

  • REDIS:也就是 REDIS 这五个字符。

  • db_version:记录版本号。

  • database:包含零个或任意多个数据库。每个数据库的结构为 SELECTDB、db_numbe、key_value_pairs,分别是数据库号码标识符、数据库号码和键值对数据。

  • EOF:结束标识符。

  • check_sum:文件校验码,由前面的内容计算得出,用于检查文件是否有出错或损坏的情况。


03

AOF 持久化

除了 RDB,Redis 还提供了一种持久化机制叫 AOF(Append Only File),它是通过保存服务器所执行的命令来实现持久化的,步骤为:

  • 命令追加:AOF 持久化功能开启时,服务器在执行完一个写命令后,会将其追加到缓冲区末尾(见上面代码中 redisServer 的 aof_buf 属性)。

  • 文件写入与同步:Redis 服务器进程本身可看作一个事件循环,每次循环结束前会调用 flushAppendOnlyFile 函数来判断是否将缓冲区的内容持久化。其由配置项 appendfsync 来决定,包括 always、everysec、no,分别代表每次循环均同步、1秒同步1次(默认)、交由操作系统同步。很明显,另外两个选项都过于极端,前者浪费性能,后者故障时丢失过多数据。

说到这不难想到,如果先新增一个键再删除掉,那么这两个命令明显是可以"消除"掉的。

Redis 提供了 AOF 重写机制,也就是读取数据库中键当前的值,用一条命令去替代针对这个键的其他命令。

不过由于 AOF 重写会有大量写入操作,因此其被放入了子进程。当重写工作完成后,子进程会向父进程发送信号,父进程收到后会调用处理函数,将 AOF 重写缓冲区的内容写入新的 AOF 文件并替换掉旧的 AOF 文件。

总的来说,AOF 和 RDB 各有特点。前者有做优化,默认情况下最多丢1秒的数据,适合大多数场景,后者可以保存某个时间点的数据库状态,适合备份的场景。


原文链接:读书笔记-《Redis设计与实现》(二)单机数据库实现(上)
原创不易,点个关注不迷路哟,谢谢~

文章推荐:

相关推荐
dg101110 分钟前
go-zero学习笔记(三)
笔记·学习·golang
知识点集锦38 分钟前
【无标题】
网络·学习·microsoft·华为·云计算
bjackal39 分钟前
K8S学习笔记-------1.安装部署K8S集群环境
笔记·学习·kubernetes
迟來北风40 分钟前
idea找不到或无法加载主类怎么解决
java·spring boot·intellij-idea
qq_3384323742 分钟前
IntelliJ IDEA远程开发代理远程服务器端口(免费内网穿透)
java·ide·intellij-idea·远程开发
翎野君1 小时前
在业务高峰期更新 PostgreSQL 表结构(DDL)导致性能问题
数据库·postgresql
想做富婆1 小时前
oracle:索引(B树索引,位图索引,分区索引,主键索引,唯一索引,联合索引/组合索引,函数索引)
数据库·oracle·索引
LightspeedResearch1 小时前
冲刺高分!挑战7天一篇GBD DAY1-7
学习
南宫生2 小时前
力扣动态规划-19【算法学习day.113】
java·学习·算法·leetcode·动态规划
优人ovo2 小时前
基于JMX实现消息队列监控
java·kafka