本文主要关于:
redis数据库实现的介绍
前面介绍的各种数据,在redis服务器中的内存模型是什么样的的。
RDB文件将这些内存数据持久化后的格式是什么样的
RDB和AOF序列化的区别是什么
redis提供什么机制保障AOF文件不会一直增长
RDB文件转储成json文件和内存分析工具介绍
客户端和服务端数据结构介绍
数据库
服务器的数据库
redis是内存型数据库,所有数据都放在内存中
保存这些数据的是redisServer这个结构体,源码中该结构体包括大概300多行的代码。具体参考server.h/redisServer
和数据库相关的两个属性是:
int类型的dbnum:表示数据库数量,默认16个
redisDb指针类型的db:数据库对象数组
数据库对象 所在文件为server.h。数据库中所有针对键值对的增删改查,都是对dict做操作
typedef struct redisDb {
dict *dict; /* The keyspace for this DB */
dict *expires; /* Timeout of keys with a timeout set */
dict *blocking_keys; /* Keys with clients waiting for data (BLPOP)*/
dict *ready_keys; /* Blocked keys that received a PUSH */
dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS */
int id; /* Database ID */
long long avg_ttl; /* Average TTL, just for stats */
} redisDb;
dict:保存了该数据库中所有的键值对,键都是字符串,值可以是多种类型
expires:保存了该数据中所有设置了过期时间的key
blocking_keys:保存了客户端阻塞的
watched_keys:保存被watch的命令
id:保存数据库索引
avg_ttl
客户端切换数据库
客户端通过select dbnum 命令切换选中的数据库
客户端的信息保存在client这个数据结构中,参考server.h/client
client的类型为redisDb的db指针指向目前所选择的数据库
读写键空间时的其他操作
读写键空间时,是针对dict做操作,但是除了完成基本的增改查找操作,还会执行一些额外的维护操作,包括:
读写键时,会根据是否命中,更新hit和miss次数。
相关命令:info stats keyspace_hits, info stats keyspace_misses
读取键后,会更新键的LRU时间,前面章节介绍过该字段
读取时,如果发现键已经过期,会先删除该键,然后才执行其他操作
如果watch监视了某个键,修改时会标记该键为脏(dirty)
每修改一个键,会对脏键计数器加1,触发持久化和复制操作
如果开启通知功能,修改键会下发通知
设置过期时间
expire key ttl:设置生存时间为ttl秒
pexpire key ttl:设置生存时间为ttl毫秒
expireat key timestamp:设置过期时间为timstamp的秒数时间戳
pexpireat key timestamp:过期时间为毫秒时间戳
persist key:解除过期时间
ttl key:获取剩余生存时间
保存过期时间
过期时间保存在expires的字典中,值为long类型的毫秒时间戳
过期键删除策略
各种删除策略的对比
| 策略类型 | 描述 | 优点 | 缺点 | redis是否采用 |
|---|---|---|---|---|
| 定时删除 | 通过定时器实现 | 保证过期键能尽快释放 | 对cpu不友好,影响相应时间和吞吐量 | 否 |
| 惰性删除 | 放任不管,查询时才去检查 | 对cpu友好 | 没有被访问的永远不会被释放,相当于内存泄露 | 是 |
| 定期删除 | 每隔一段时间检查 | 综合前面的优点 | 难于确定执行时长和频率 | 是 |
redis使用的过期键删除策略
redis采用了惰性删除和定期删除策略
惰性删除的实现
由db.c中的expireIfNeeded实现
每次执行redis命令前都会调用该函数对输入键做检查
定期删除的实现
server.c中的serverCron函数执行定时任务
函数每次运行时,都从一定数量的数据库中取出一定数量的键进行检查,并删除过期键
数据库通知
键空间通知:客户端获取数据库中键执行了什么命令。 实现代码为notify.c文件的notifyKeyspaceEvent函数subscribe keyspace@0:keyname
键事件通知:某个命令被什么键执行了subscribe keyevent@0:del