Redis设计与实现 第 9 章 数据库

第 9 章 数据库

9.1 服务器中的数据库

redis.h/redisServer 结构的 db 数组中,每个元素都是 redis.h/redisDb 结构,代表一个数据库

Redis设计与实现 第 9 章 数据库

初始化服务器时会根据服务器状态的 dbnum 属性来决定创建多少个数据库

Redis设计与实现 第 9 章 数据库

dbnum 由服务器配置的 database 选项决定,默认为 16,所以会默认创建 16 个数据库

Redis设计与实现 第 9 章 数据库

9.2 切换数据库

Redis 客户端的目标数据库为 0 号数据库,可以通过 SELECT 命令来切换目标数据库

127.0.0.1:6379> set 32 "hello you"
OK
127.0.0.1:6379> get 32
"hello you"
127.0.0.1:6379> select 2
OK
127.0.0.1:6379[2]> get 32
(nil)
127.0.0.1:6379[2]> set 32 "you hello"
OK
127.0.0.1:6379[2]> get 32
"you hello"

在服务器内部,客户端状态的 redisClient 结构的 db 属性记录了客户端当前的目标数据,是一个指向 redisDb 结构的指针

Redis设计与实现 第 9 章 数据库

redisClint.db 指针指向 redisServer.db 数组的一个元素,被指向的元素则是客户端的目标数据库

Redis设计与实现 第 9 章 数据库

9.3 数据库键空间

redis 是键值对数据库服务器,每个数据库都是 redis.h/redisDb 结构表示,dict 字典保存了数据库中的键值对,这个字典为键空间

Redis设计与实现 第 9 章 数据库

键空间与数据库是直接对应的

  • 键空间的键是数据库的键,也是一个字符串对象
  • 键空间的值是数据库的值,每个值可以是任意一种 Redis 对象

Redis设计与实现 第 9 章 数据库

所有针对数据库的操作,实际上都是通过对键空间字典进行操作实现的

添加新键

添加新键值对到数据库,实际上就是将新键值对添加到键空间中,键为字符串对象,值为任意一种 Redis 对象

删除键、更新键、对键取值

同理添加新键

其他命令

FLUSHDB、RANDOMKEY、DESIZE 同样也是通过对键空间进行操作的

9.3.6

不仅只有指定的读写操作,还有一些额外的维护操作,包括

  • 服务器会根据键是否存在来更新键空间命中次数和不命中次数,使用 INFO status 命令的 keyspace_hits 和 keyspace_misses 属性查看

  • 服务器会更新键的 lru (最后一次使用)时间,可以计算键的闲置时间, OBJECT idletime key 查看

  • 读取一个键时发现已经过期了,则会先删除再进行余下的操作

  • 如果客户端使用 watch 命令监视了某个键,则服务器修改后,会将其标记为脏,从而让事务程序注意到键已经修改

  • 每修改一个键后,脏键计数器的值 + 1,会触发服务器的持久化以及复制操作

  • 如果服务器开启数据库通知功能,在对键进行修改之后,服务器会按配置发送相应的数据库通知

9.4 设置键的生存时间或过期时间

9.4.1 设置过期时间

Redis设计与实现 第 9 章 数据库

所有命令底层还是由 PEXPIRE 命令转换而来的

Redis设计与实现 第 9 章 数据库

9.4.2 保存过期时间

redisDb 结构的 expires 字典保存了数据库中所有键的过期时间,即过期字典:

  • 键为指针,指向键空间的某个键对象

  • 值为 long long 类型,保存了键指向的键空间键对象的过期时间,毫秒精度的 UNIX 时间戳

Redis设计与实现 第 9 章 数据库

Redis设计与实现 第 9 章 数据库

键空间和过期字典的键都指向同一个,此处为明显展示而列举两个

9.4.3 移除过期命令

PERSTST 命令移除键的过期时间:

在过期字典中查找给定的键,并解除键和值在过期字典中的关联

9.4.4 计算并返回剩余生存时间

TTL 秒为单位,PTTL 毫秒为单位,通过计算键的过期时间与当前时间之差

9.4.5 过期键的判定

  1. 检查给定键是否存在于过期字典;存在则取得过期时间
  2. 检查当前 UNIX 时间戳是否大于键的过期时间;是则键已经过期,否则未过期

或者使用 TTL 或 PTTL 命令,但实际中是使用 is_expired 函数

9.5 过期键删除策略

  • 定时删除:主动
    • 设置过期时间的同时创建定时器,过期时间到达立即删除
  • 惰性删除:被动
    • 当从键空间获取键时,检查是否过期,过期则删除,没有则返回
  • 定期删除:主动
    • 每隔一段时间,对数据库检查,删除多少,检查多少数据库则由算法决定

9.5.1 定时删除

  • 内存友好

  • CPU 最不友好

  • 需要使用 Redis 的时间事件,时间事件实现方式为无序链表,则查找一个事件的时间复杂度为 O(N)

9.5.2 惰性删除

  • 对 CPU 最友好

  • 对内存最不友好

  • 浪费大量内存

9.5.3 定期删除

每隔一段时间执行一次删除过期键操作,并通过限制删除操作执行的时长和频率来减少删除操作对 CPU 时间的影响,有效减少了过期键的内存浪费

时长和频率难以确定:

  • 执行频繁、时间长,会退化成定时删除

  • 执行少、时间短,退化为惰性删除

9.6 Redis 的过期键删除策略

9.6.1 惰性删除策略的实现

由 db.c/expireIfNeeded 函数实现,所有读写数据库的 Redis 命令在执行之前都会调用此函数对输入键进行检查

  • 过期,此函数将键从数据库中删除
  • 未过期,此函数不做动作

命令执行时也需要按照两种情况执行

Redis设计与实现 第 9 章 数据库

9.6.2 定期删除策略的实现

由 redis.c/activeExpireCycle 函数实现,Redis 周期性操作 redis.c/serverCron 函数执行时,activeExpireCycle 函数会调用,在规定时间内,分多次遍历各个数据库,从 expires 字典中随机检查一部分键的过期时间,并删除过期键

activeExpireCycle:

  1. 运行时,从一定数量的数据库取出一定数量的随机键进行检查,删除过期键
  2. current_db 会记录当前函数检查的进度,并在下一次调用此函数时按上一次进度进行处理
  3. 函数不断执行,所有数据库都会被检查一遍,current_db 置为 0 ,开始新一轮的检查

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

9.7.1 生成 RDB 文件

在执行 save 命令或者 bgsave 命令创建新的 RDB 文件时,对键会进行检查,已经过期的键不会被保存到新创建的 RDB 文件中

9.7.2 载入 RDB 文件

对 RDB 文件进行载入:

  • 以主服务器模式运行,在载入时会对文件中保存的键进行检查,未过期的键会被载入到数据库中,已过期的会被忽略

  • 以从服务器模式运行,文件中所有键都会被载入数据库,但是主从服务器进行数据同步时,从服务器的数据库就会被清空

9.7.3 AOF 文件写入

服务器以 AOF 持久化模式运行时,如果某个键过期,但没有被惰性删除或者定期删除,AOF 文件不会因这个过期键产生任何影响

当过期键被删除后,会向 AOF 文件追加一条 DEL 命令显示记录该键已被删除

9.7.4 AOF 重写

类似生成 RDB 文件,对键检查,已过期的不会被保存

9.7.5 复制

当服务器在复制模式下,从服务器的过期删除动作由主服务器控制:

  • 主服务器删除一个过期键,显示向所有从服务器发送一个 DEL 命令,告知从服务器删除这个过期键
  • 从服务器在执行客户端发送的读命令时,遇到过期键也不会将过期键删除,当成未过期键处理
  • 从服务器只有在接到猪服务器发来的 DEL 命令之后才会删除过期键

通过主服务器控制从服务器统一删除过期键,来保证主从服务器的一致性

9.8 数据库通知

让客户端通过订阅给定的频道或者模式,来获知数据库中键的变化,以及命令的执行情况

  • 键空间通知:
    • 某个键执行了什么命令
  • 键事件通知
    • 某个命令被什么键执行了

notify-keyspace-events 决定发送通知的类型

  • 发送所有类型的键空间通知和键事件通知:AKE

  • 发送所有类型的键空间通知:AK

  • 发送所有类型的键事件通知:AE

  • 发送和字符串键相关的键空间通知:K$

  • 发送和列表键相关的键事件通知:EL

9.8.1 发送通知

发送数据库通知功能由 notify.c/notifyKeyspaceEvent 实现:

void notifyKeyspaceEvent(int type, char *event, robj  *key, int dbid);

type 为当前想要发送的通知的类型,根据这个值来判断通知是否就是服务器配置 notify-keyspace-event 选项所选定的通知类型来决定是否发送通知

event 事件名称

keys 产生事件的键

dbid 产生事件的数据库号

根据这些参数来构建事件通知的内容、以及接受通知的频道名

REDIS_NOTIFY_SET:集合键通知

REDIS_NOTIFY_GENERIC:通用类型通知

notifyKeyspaceEvent:

  1. server.notify_keyspace_events 为 notify-keyspace-events 选项设置的值,如果给定的通知类型 type 不是服务器允许发送的通知类型,函数直接返回
  2. 是服务器允许发送的通知类型,再检测是否允许发送键空间通知,允许则构建并发送事件通知
  3. 最后再检测是否允许发送键事件通知,允许中午构建并发送事件通知

pubsubPublishMessage 是PUBLISH 命令的实现函数

Redis设计与实现 第 9 章 数据库

上一篇:jdbc:几种方式连接数据库


下一篇:Vue 结合 Animate.CSS 实现简单动画效果