Mr Dk.'s BlogMr Dk.'s Blog
  • 🦆 About Me
  • ⛏️ Technology Stack
  • 🔗 Links
  • 🗒️ About Blog
  • Algorithm
  • C++
  • Compiler
  • Cryptography
  • DevOps
  • Docker
  • Git
  • Java
  • Linux
  • MS Office
  • MySQL
  • Network
  • Operating System
  • Performance
  • PostgreSQL
  • Productivity
  • Solidity
  • Vue.js
  • Web
  • Wireless
  • 🐧 How Linux Works (notes)
  • 🐧 Linux Kernel Comments (notes)
  • 🐧 Linux Kernel Development (notes)
  • 🐤 μc/OS-II Source Code (notes)
  • ☕ Understanding the JVM (notes)
  • ⛸️ Redis Implementation (notes)
  • 🗜️ Understanding Nginx (notes)
  • ⚙️ Netty in Action (notes)
  • ☁️ Spring Microservices (notes)
  • ⚒️ The Annotated STL Sources (notes)
  • ☕ Java Development Kit 8
GitHub
  • 🦆 About Me
  • ⛏️ Technology Stack
  • 🔗 Links
  • 🗒️ About Blog
  • Algorithm
  • C++
  • Compiler
  • Cryptography
  • DevOps
  • Docker
  • Git
  • Java
  • Linux
  • MS Office
  • MySQL
  • Network
  • Operating System
  • Performance
  • PostgreSQL
  • Productivity
  • Solidity
  • Vue.js
  • Web
  • Wireless
  • 🐧 How Linux Works (notes)
  • 🐧 Linux Kernel Comments (notes)
  • 🐧 Linux Kernel Development (notes)
  • 🐤 μc/OS-II Source Code (notes)
  • ☕ Understanding the JVM (notes)
  • ⛸️ Redis Implementation (notes)
  • 🗜️ Understanding Nginx (notes)
  • ⚙️ Netty in Action (notes)
  • ☁️ Spring Microservices (notes)
  • ⚒️ The Annotated STL Sources (notes)
  • ☕ Java Development Kit 8
GitHub
  • ⛸️ Redis Implementation
    • Part 1 - 数据结构与对象

      • Chapter 2 - 简单动态字符串
      • Chapter 3 - 链表
      • Chapter 4 - 字典
      • Chapter 5 - 跳跃表
      • Chapter 6 - 整数集合
      • Chapter 7 - 压缩列表
      • Chapter 8 - 对象
    • Part 2 - 单机数据库的实现

      • Chapter 9 - 数据库
      • Chapter 10 - RDB 持久化
      • Chapter 11 - AOF 持久化
      • Chapter 12 - 事件
      • Chapter 13 - 客户端
      • Chapter 14 - 服务器
    • Part 3 - 多机数据库的实现

      • Chapter 15 - 复制
      • Chapter 16 - Sentinel
      • Chapter 17 - 集群
    • Part 4 - 独立功能的实现

      • Chapter 18 - 发布与订阅
      • Chapter 19 - 事务
      • Chapter 21 - 排序
      • Chapter 22 - 二进制位数组
      • Chapter 23 - 慢查询日志

Chapter 9 - 数据库

Created by : Mr Dk.

2020 / 06 / 02 14:15

Nanjing, Jiangsu, China


Databases

Redis 服务器将所有的数据库都保存在如下结构体中。初始化服务时,会根据其中的 dbnum 属性来决定应创建多少个数据库。这个属性可以在服务器配置中指定,默认 16。

struct redisServer {
    // ...

    redisDb *db;

    // ...

    int dbnum;

    // ...
}

在服务器内部,维护客户端状态的结构体中的一个指针记录了当前客户端正在使用的数据库:

typedef struct redisClient {
    // ...
    redisDb *db;
    // ...
} redisClient;

Key Space

Redis 是一个 key-value 数据库服务器。每个数据库中都有一个 key space,是一个字典,保存了数据库中所有的键值对:

typedef struct redisDb {
    // ...

    dict *dict;

    // ...
} redisDb;

之后的增删改查全部通过对 key space 进行增删改查完成。在进行读写操作的同时,Redis 还需要进行一些额外的维护操作:

  1. 服务器根据读写访问的 key 是否存在,更新 hit/miss 的次数
  2. 更新 key 的 LRU 时间
  3. 如果 key 已过期,则先删除这个 key
  4. 如果客户端监视该 key,那么会将该 key 标记为脏
  5. 服务器修改 key 后都会对 dirty 计数器 +1 (用于触发持久化操作或复制操作)
  6. 如果开启了数据库通知,那么对 key 修改后,服务器按配置推送通知

Key Expiration

客户端可以以秒或毫秒精度为数据库中的 key 设置 TTL (Time To Live)。在到期后,服务器自动删除过期的 key。

在 redisDb 中有一个 expires 字典专门用于保存数据库中所有 key 的过期时间。过期时间以 long long 类型的整数保存,是一个毫秒精度的 UNIX 时间戳。

typedef struct redisDb {
    // ...
    dict *expires;
    // ...
} redisDb;

过期 key 的判定:

  1. key 存在于过期字典
  2. 当前 UNIX 时间戳是否大于过期时间

Removing Strategy Candidate

几种可能的删除机制:

定时删除

在设置 key 过期时间的同时,设置一个 timer,在 timer 到期时删除 key。

  • CPU 不友好 - 时间浪费在与任务无关的过期 key 上,影响吞吐量
  • 内存友好 - 过期的 key 会被尽快删除
  • 创建定时器的内部实现是无序链表 - 查找时间复杂度为 O(N)

惰性删除

只有在取出 key 时才对 key 进行过期检查。

  • 节省 CPU 到了最大程度
  • 对于内存十分不友好,因为有些 key 如果不被访问就永远不会释放

定期删除

是上述两种策略的折衷。每隔一段时间执行一次删除过期 key 的操作,并限制删除操作执行的时间与频率。最重要的点是控制好时间与频率,如果时间太长、频率高,那么就退化为定时删除;反之,则退化为惰性删除。

Removing Strategy Implementation

Redis 服务器实际使用了惰性删除和定期删除两种策略。

惰性删除实现

在所有读写数据库的命令之前,都会对操作的 key 进行检查:

  • 如果 key 已经过期,那么将该 key 从数据库中删除
  • 如果 key 未过期,那么检查函数直接通过

定期删除实现

在 规定时间 内,周期性地 分多次 遍历服务器中的各个数据库,从每个数据库的过期字典中 随机检查 一部分过期时间,并删除其中的过期 key。

需要用一个值记录当前的检查进度 - 假设 16 个数据库,第一次检查了前四个,第二次就从第五个开始检查,一轮检查完毕后,开启新的一轮。


持久化过期策略

对于 RDB 持久化功能,已过期的 key 将不会被保存到文件中;载入 RDB 文件时,过期的 key 将不会被载入到数据库中 (主数据库)。从数据库将载入所有 key。

对于 AOF 持久化,当过期的 key 被删除后,程序会向 AOF 文件追加 DEL 命令来显式标记删除。

当服务器运行在复制模式时,从服务器 的过期 key 由 主服务器 控制 (为了保证两者的一致性)。

  • 主服务器删除过期 key 后,将显式通知从服务器删除
  • 从服务器不会主动删除过期 key

数据库通知

两个类型的通知:

  • Key-space notification - 键空间通知 - 某个 key 执行了什么命令
  • Key-event notification - 键事件通知 - 某个命令被什么 key 执行了
Edit this page on GitHub
Next
Chapter 10 - RDB 持久化