redis设计与实现-数据库篇

Redis 数据库

1服务器中的数据库

redis服务器将所有数据库都保存在服务器状态redisServer 结构的db数组中,db数组的每个项都是redisDb结构

struct redisServer{
    
    // 保存者服务器中的所有数据库
    redisDb * db;
    // 服务器的数据库数量
    int  dbnum;
    
}

redis 服务器默认会创键16个数据库。

2切换数据库

每个redis 都有自己的目标数据库,每当客户端执行数据库写命令或者数据库读命令的时候,目标数据库就会成为这些命令的操作对象。

默认下 redis 客户端的目标数据库为0号数据库,但客户端可以通过执行SELECT命令切换目标数据库。

127.0.0.1:6379> get msg
"sheng ming de zhen di zai yu yun dong , shi jian shi jian yan zhen li de wei yi biao zhun"
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> get msg
(nil)
127.0.0.1:6379[1]> 


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

struct redisClient{
    
    // 记录客户端当前正在使用的数据库
    redsiDb *db;
    
}
    

3 数据库键空间

redis 是一个键值对数据库服务器,每个数据库都由redisDb结构表示,其结构中的字典保存了数据库中的所有键值对,我们将这个字典称为键空间。

typedef struct redisDb{
    
    //数据库键空间,保存者数据库中的1所有键值对。
    dict *dict ;
    
}
  • 键空间的键就是数据库键,每个键都是一个字符串对象
  • 键空间的值也就是数据库中的值,每个值可以是字符串对象,列表对象,哈希对象,集合对象,有序集合对象中的任意一种redis对象
3.1 添加新建

添加一个键值对到数据库,实际上就是将一个键值对添加到键空间字典里面,其中建为字符串对象,而值则为任意一种类型的redis对象。

# 设置值对象为 String类型的键
127.0.0.1:6379> set data "2021-10-11"
OK

3.2 删除键

删除数据库中的一个键,实际上就是在键空间里删除键所对应的键值对对象

127.0.0.1:6379> del data 
(integer) 1
127.0.0.1:6379> get data
(nil)

3.3 更新键

对键空间里面键所对应的值对象进行更新,根据值对象类型的不同,更新的具体方法也会有所不同

string 键值对更新

127.0.0.1:6379> get msg
"sheng ming de zhen di zai yu yun dong , shi jian shi jian yan zhen li de wei yi biao zhun"
127.0.0.1:6379> set msg "www"
OK
127.0.0.1:6379> get msg
"www"

hset 更新

127.0.0.1:6379> hset man name "ws" age 18
(integer) 2
127.0.0.1:6379> hset man age 17
(integer) 0
127.0.0.1:6379> hget man age
"17"
127.0.0.1:6379> hget man name
"ws"

3.4 读写键空间时的维护操作
  • 读取一个键之后,服务器会根据键是否存在来更新服务器键空间的命中。
  • 在读取一个键后,会更新键的lru时间,用于计算键的闲置时间
  • 如果服务器在读取一个键时发现键已经过期,会先删除这个过期键然后才执行余下操作
  • 服务器每修改一个键都会对脏键计数器的值增一。

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

通过EXPIRE命令或者PEXPIRE 命令,客户端可以通过秒或者毫秒为数据库中的某个键设置生存时间,在经过指定的时间后,服务器会自动删除生存时间为0的键 通过 TTL和pttl返回剩余时间,这个键被服务器自动删除还有多长时间

127.0.0.1:6379> expire msg 1
(integer) 1
127.0.0.1:6379> get msg
(nil)

还可以通过expireAt 命令或者 pexpireAt 命令以秒或者毫秒精度设置unix时间戳。

  • expire 用于将键key的生存时间设置为ttl秒
  • pexpire 用于将键key的生存时间设置为ttl毫秒
  • expireat 将键的key的过期时间设置为timestamp所指定秒数时间戳
  • pexpireat 将键的key的过期时间设置为timestamp所指定豪秒数时间戳
4.1 保存过期时间

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

  • 过期字典的键是一个指针,指向键空间中的某个键对象
  • 过期字典的值是一个longlong类型的整数,这个数保存了所指向键的过期时间。
4.2 移除过期时间

persist命令可以移除一个键的过期时间。

presist命令在过期字典中查找给定的键,并接触键和值(过期时间)在过期字典的关联。

127.0.0.1:6379> expire msg 30
(integer) 1
127.0.0.1:6379> ttl msg
(integer) 26
127.0.0.1:6379> persist msg
(integer) 1
127.0.0.1:6379> ttl msg
(integer) -1
127.0.0.1:6379> get msg
"111"

4.3 计算并返回剩余生存空间

TTL命令以秒返回键的剩余生存空间,而pttl则以毫秒为单位返回键的生存时间。

4.4 过期键的判定

通过过期字典,可以检查给定键是否过期

  • 检查给定键是否存在过期字典:存在则取得键的过期时间
  • 检查当前时间是否大于键的过期时间:如果是,那么键已经过期;否则的话键未过期。
4.5 过期键删除策略
  • 定时删除:在设置键的过期时间的同时,创建一个定时器,让定时器在键过期的过期时间来临时,立即执行对键的删除操作。
  • 惰性删除:放任过期键不管,但是每次从空间中获取键时,都检查取得的键是否过期,如果过期就删除键,没有过期则返回键。
  • 定期删除:每隔一段时间,程序就会对数据库进行一次检查 删除里面的过期键。
4.5.1 定时删除

定时删除对内存是友好的:通过定时器,定时删除策略可以保证过期键会尽快的被删除,并释放过期键所占用的内存。

其缺点:对cpu时间是最不友好的,占用cpu

4.5.2 惰性删除

惰性删除的策略对cpu时间来说是最友号的:程序只会在取出键时,才对键进行过期检查,这可以保证删除过期键的操作只会在非做不可的情况下进行。

其缺点是:对内存不友好的,如果一个键已经过期,仍然会被保存在数据库中。

4.5.3 定期删除

定时删除占用太多cpu时间,影响服务器的响应时间与吞吐量。

惰性删除浪费太多内存,有内存泄漏的风险

定期删除策略是每隔一段时间执行一次删除过期键的策略,通过限制删除操作执行的时长和频率来减少删除操作对cpu时间的影响,有效地减少了因为过期键而带来的内存浪费。

定期删除的策略的难点是确定删除操作执行的时长和频率。

4.6 Redis 过期删除策略

redis服务器实际使用的是惰性删除和定期删除的策略。

4.6.1 惰性删除策略的实现

所有读写数据库redis的命令在执行前都会调用expireIfneeded 函数对输入键进行检查

  • 如果输入键已经过期,那么expireIfNeeded 函数将输入键从数据库中删除。
  • 如果输入键未过期,那么expireIfNeeded函数不做动作
4.6.2 定期删除策略的实现

过期键的定期删除策略由redis activeExpireCycle函数实现,没当redis的服务器周期性操作serverCron函数执行时,activeExpireCycle函数被调用,它在规定时间内,分多次遍历服务器中的各个数据库,从数据库的expires字典中随机检查一部分键的过期时间,并删除其中的过期键。

  • 函数每次运行时,都从一定的数据库中取出一定数量的随机键进行检查,并删除其中的过期键。
  • 全局变量current_db 会记录当前函数检查的进度,并在下一次ExpireCycle函数调用时,接着上一次的进度进行处理。
  • 随着函数的不断执行,服务器中的所有数据库都会被检查一遍,这时函数current_db重置为0,然后再次开始新一轮的检查工作
4.7 AOF,RDB和复制功能对过期键的处理
4.7.1 生成RDB文件

在执行save文件命令创建一个新的RDB文件时,程序会对数据库中的键进行检查,已过期的键不会被保存到新创建的RDB文件

4.7.2 载入RDB文件
  • 如果服务器以主服务器模式运行,那么在载入rdb文件时,程序会对文件中保存的键进行检查,未过期的键会被载入到数据库中,而过期键则会被忽略,所以过期键对载入RDB文件主服务器不会造成影响。
  • 如果服务器以从服务器模式运行,则在载入RDB文件时,文件中保存的键无论是否过期都会被载入,因为主从服务器在进行数据同步的时候,从服务器数据库会被清空
4.7.3 AOF文件写入

当服务器以AOF文件持久化模式运行时,如果数据库的键已经过期,但还未删除AOF文件不会因为这个过期键而产生任何影响。

当过期键被删除时,程序会向AOF文件追加一条DEL命令来显式的记录该键被删除。

4.7.4 AOF重写

在执行aof重写时会先进行检查,已过期的键不会被保存到重写后的AOF文件。

4.7.5 复制
  • 主服务器在删除一个过期键之后会显示地向所有从服务器发送DEL命令,告知从服务器删除这个过期键

  • 从服务器在执行客户端发送的命令时,即使碰到过期键也不会将过期键删除,而是继续处理

  • 从服务器只有在接到主服务器发来的DEL命令后,才会删除过期键。

通过主服务器来控制从服务器统一的删除过期键,保证主从服务器数据的一致性,当一个过期键仍然存在于主服务器的数据库时,这个过期键在从服务器里的复制品也会继续存在。

5 重点回顾
  • 数据库主要由dict和expire两个字典构成,其中dict字典负责保存键值对,而expire字典负责保存键的过期时间
  • expires字典指向数据库中的某个键,而值则记录了数据库键过期时间,过期时间是一个以毫秒为单位的时间戳。
  • Redis 使用惰性删除和定期删除两种策略:惰性删除策略只有在碰到过期键时才进行删除操作,定期删除策略则每隔一段时间主动查找并删除过期的键
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值