Redis-单机数据库的实现-数据库原理
目录
1. 服务器中的数据库
Redis也是个程序,用C语言写的,那么保存数据是需要一个数据结构的,那么Redis中的数据库是什么结构呢
大概的样子如上图所示
RedisServer通过dbnum来确定初始化的时候,需要新建多少个数据库,也就是db数组
而redisClient连接的时候需要选定操作是那个数据库,默认是0号库,也能通过select命令选择使用那个数据库,例如select 1,选择了1号库,那么这个时候就如上图所示
每一个client连接到server,都会再server保存一个client实例
2. 数据库键空间
数据库键是一个字符串对象
数据库值可以是字符串对象,列表,集合,哈希对象等等,任意一种Redis对象
如下图所示
“xx” 表示一个字符串对象
可以看出,数据库的底层是用dict,也就是哈希对象实现的
2.1. 读写空间时的维护操作
当使用Redis命令对数据库进行读写时候,服务器不仅会对键空间执行指定的读写操作,还会执行一项额外的维护操作
- 读取键之后,会根据键是否存在更新键空间命中(hit)次数和键空间不命中(miss)次数,可以通过info stats命令查看
- 读取之后,会更新键LRU时间
- 如果服务器发现读取一个键时间这个键已经过期,会先删除这个键,然后再执行余下操作
- 如果客户端使用了watch命令监视某个键,那么服务器对被监视的键进行修改,会将这个键标注为赃读
- 服务器修改一个键后,赃读+1,这个计数器引发持久化操作
- 如果服务器开启通知功能,那么对键修改后,会发生相应的数据库通知
3. 过期时间
expire命令和pexpire命令
前者是设置秒,后者是设置毫秒
本质上是通过将秒转换为时间戳实现的,例如expire 5 这样会将当期时间+5算到时间戳,当时间戳的时间来临就过期
pexpireat <key> <时间戳> 所有设置过期时间的命令都是用这个命令实现的 这个时间戳是毫秒级别,对应有个expireat秒级别
这个过期时间保存地方如下图所示
这里过期时间的expires中的key和dict中的key是同一个对象,redis做了复用,不会造成2倍的内存消耗
4. 过期删除策略
一般来说删除策略分为3种
- 定时删除
- 惰性删除
- 定期删除
4.1. 定时删除
对内存最友好,通过定时器实现,保证过期键会尽可能快的被删除,并释放过期键所占用的内存,缺点就是对CPU不友好,过期键很多情况下,删除过期键会占用很多CPU,容易造成吞吐量影响
Redis创建定时器需要用到Redis服务器中的时间事件,时间事件的实现方式无序列表,时间复杂度是O(n)
而且对每个键都创建个定时器,也不正常,不现实
4.2. 惰性删除
对CPU最友好,程序只会在取出键的时候检查,判断是否过期,同理对内存不友好,可能过期之后,长期没有访问,一直存在,可以看成是一种内存泄漏
4.3. 定期删除
定期删除可以看成上面两种的折中
定期删除的策略是每隔一段时间执行一次删除过期键的操作,并通过限制操作执行频率和时长减少对CPU影响,简单来说,限制时间,限制删除键数量来操作
4.4. Redis删除策略
Redis采用定期删除和惰性删除两种策略配合使用
惰性删除
Redis在调用读写命令之前都会判断键是否过期,过期就删除
定期删除
在规定时间内,分多次遍历服务器中各个数据库,从数据库的expires字典中随机拿出一部分键的过期时间,检查是否过期,并删除过期键
有个全局变量记录当前操作那个数据库,当执行完最后一个数据库时候,计数器归0,从新开始
每次开始的操作时候,有两个变量控制,时间,清除过期键梳理,如果在操作过程中,时间到期,则操作结束,如果时间没有到期,但是清除了规定的键梳理,操作也结束
5. AOF、RDB和复制功能对对过期键的处理
5.1. RDB
过期键在生成RDB文件时候,不会保存进RDB文件中
在载入RDB文件时候,主服务器会根据键是否过期来判断是否载入这个键,从服务器则载入所有,不管是否过期,但是主从同步数据之后,过期键还是会被删除
5.2. AOF
当一个键过期了,会追加一条DEL命令进AOF文件
重写AOF时候,会判断键是否过期,过期的键会被忽略
5.3. 复制
在主从模式下,从服务器的过期键删除动作由主服务器控制
主服务器删除一个键,显示向所有服务器发送一个DEL命令,告知所有从服务器这个键被删除
从服务器在执行客户端读命令,遇见过期键也不会删除
6. 数据库通知
当Redis命令对数据库进行了修改之后,服务器会根据配置向客户端发送数据库通知,包含事件名称,产生事件的键,产生事件的数据库编号确定事件内容