《Redis设计与实现 —— 黄健宏》读书笔记

2016/08/20 Redis 共 2900 字,约 9 分钟
Bob.Zhu

引言

单机数据库的实现

数据库

数据库的实现

数据库服务端和客户端实现的数据结构:

typedef struct redisServer {
    int dbnum;          // 服务器的数据库数量,默认值是16
    redisDb[] *db;      // 一个数组,保存着服务器中的所有数据库,数组大小由dbnum决定
    ......
    list *clients;      // 连接到此服务器的客户端链表
} redisServer;
typedef struct redisClient {
    redisDb *db;    // 记录客户端当前正在使用的数据库
    dict *dict;     // 键空间,字典类型,保存着数据库中的所有键值对
    dict *expires;  // 过期时间,字典类型,键是指向对象的指针,值是long类型的时间戳
    ......
} redisClient;

数据库客户端和服务端的关系如图所示: 数据库

上节图示中连接的是1号数据库,切换数据库指令:SELECT <dbnum>,比如 SELECT 2,就能够切换到2号数据库。

数据库键空间

  • 键空间的键也就是数据库的键,每个键都是一个字符串对象
  • 键空间的值也就是数据库的值,每个值都可以是前面所述五种 对象 的其中一种

数据库键空间范例

Redis的过期键删除策略

对于过期键的删除一般有如下三种方式:

  • 定时删除 在设置键的过期时间的同时,创建一个定时器(timer),定时结束立即删除
  • 惰性删除 每次响应请求从键空间获取键的时候检查是否过期,如果过期进行删除
  • 定期删除 每隔一段时间,对数据库进行检查,批量删除过期的键,是上述两种方法的整合和折中

Redis是配合使用惰性删除和定期删除两种策略,以很好得在合理使用CPU时间和避免浪费内存空间之间取得平衡。

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

当服务器运行在复制模式(主从模式)下时,从服务器的过期键删除动作由主服务器控制。 也就是说主服务器检测到一个键过期时候会进行删除,同时发送给所有从服务器一条删除指令,进行从服务器过期键的删除。 我们知道,主服务收到请求的时候,会先检查键是否过期,如果不过期返回value,如果过期,返回nil。 但从服务器收到请求的时候不会进行检查,只要没有收到主服务器发送过来的删除指令进行删除,即便过期也会返回value。

数据库键空间范例

数据库通知

  • 键空间通知(key-space notification):某个键执行了什么命令
  • 键事件通知(key-event notification):某个命令被什么键执行了

RDB持久化

RDB文件的创建与载入

Redis所有数据都保存在内存中,那么一旦服务器进程退出,Redis数据就消失。 为了避免数据意外丢失,Redis提供了RDB持久化功能。 RDB持久化生成的RDB文件是一个经过压缩的二进制文件,通过RDB文件可以还原到 生成文件时候的数据库状态。

RDB持久化

持久化命令有两个:

  • SAVE:阻塞式持久化,持久化过程中不能使用Redis其他指令
  • BGSAVE:非阻塞式持久化,会新开一个子线程来进行持久化,不影响Redis其他指令

只要有RDB文件,启动Redis的时候就会自动载入,不需要手动调用指令。

自动间隔性保存

默认配置:

save 900 1
save 300 10
save 60 10000

只要满足900秒修改了一次,300秒修改了10次 或 60秒修改了1000次,那就自动执行一次 BGSAVE 操作。

AOF持久化

RDB持久化的是数据库状态,也就是将数据库中所有有效数据保存到RDB文件; 而AOF持久化的是操作命令,也就是将对数据新增、修改的指令保存到AOF文件。

  • 如果开启了AOF持久化功能,那么服务器会优先使用AOF文件还原数据库状态
  • 只有AOF持久化出于关闭状态时,服务器才会使用RDB文件来还原数据库状态

AOF持久化

AOF持久化的实现

  • 命令追加(append)
    appendfsync有三个配置选项:always、everysec、no
  • 文件写入
  • 文件同步(sync)

AOF文件的载入与数据还原

读取AOF文件,将所有命令重新执行一遍

AOF重写

如果一条list数据有新增,有修改删除,那么很多条指令之后可能只包含了很少的有效数据, 这时候可以从数据库读取当前有效的数据,用一条新增指令替换掉之前的很多条指令。

事件

Redis是一个事件驱动服务器,服务器需要处理以下两类事件:

  • 文件事件(file event)
  • 时间时间(time event)

文件事件

Redis服务器通过套接字与客户端连接,而文件事件就是套接字对文件事件的抽象。 服务器与客户端的通信会产生相应的文件事件,而服务器则通过监听并处理这些事件来完成一系列网络通信操作。

文件事件

时间事件

时间事件分为如下两类:

  • 定时事件
  • 周期性事件

客户端

客户端属性

  • 通用属性
  • 特定功能相关属性

服务器

命令请求的执行过程

客户端请求 服务端回复

serverCron函数

Redis服务器中的serverCron函数每隔100毫秒执行一次,这个函数负责管理服务器的资源, 并保持服务器自身的良好运转。

  • 更新服务器时间缓存
  • 更新LRU时钟
  • 更新服务器每秒执行命令次数
  • 更新服务器内存峰值记录
  • 处理SIGTERM信号
  • 管理客户端资源
  • 管理数据库资源
  • 执行被延迟的BGREWRITEAOF
  • 检查持久化操作的运行状态
  • 将AOF缓冲区的内容写入到AOF文件
  • 关闭异步客户端
  • 更新cronloops计数器的值

多机数据库的实现

复制

主从结构

旧版功能的实现

  • 同步(sync)
    使用 SLAVEOF 指令,将从服务器的状态更新至主服务器当前的数据库状态
  • 命令传播(command propagate)
    主服务器数据被修改时,主服务器将指令发送给从服务器,将主从服务器状态再次同步为一致状态

旧版功能缺陷

  • 初次复制:使用同步指令 sync 生成RDB文件进行同步,一般来说这个没问题
  • 断线后重复制:断线后重连复制的是全部RDB文件,这里是不需要的,新版主要解决这个问题

新版复制功能的实现

  • 完整重同步(full resynchronization):用于处理初次复制的情况,相当于 SALVEOF 指令
  • 部分重同步(partial resynchronization):用于处理断线后重复值的情况

部分重同步的实现

  • 主服务器的复制偏移量(replication offset)和从服务器的复制偏移量
  • 主服务器的复制挤压缓冲区(replication backlog):存储断线时间段内的指令,默认1M内存空间
  • 服务器的运行ID

PSYNC命令的实现

主从结构

Sentinel

Sentinel(哨岗、哨兵)是Redis高可用性(high availability)解决方案:由一个或多个Sentinel实例(instance) 组成的Sentinel系统(system)可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器 进入下线状态时,自动将下线主服务器属下的某个服务器升级为新的主服务器,然后由新的主服务器代替已下线的从服务器继续 处理命令请求。当已下线的主服务器重新上线之后,降级为从服务器继续运行。

集群

Redis集群是Redis提供的分布式数据库方案,集群通过分片(sharing)来进行数据共享,并提供复制和故障转移功能。

节点

独立功能的实现

发布与订阅

事务

Lua脚本

排序

二进制位数组

慢查询日志

监视器

整理

  • 集合键

参考资料

文档信息

Search

    Table of Contents