数据库结构实现
redis的数据库保存在redisServer结构体总的db数组中,而db数组的元素则是由redisDb结构体描述。
redisserver中的dbNum代表了所能创建的最多的数据库,由配置项database决定,默认为16个
数据库的键空间实现
redis的键值对保存与redisDb结构体的dict属性中,这个dict称为键空间。该字典的键只能为字符串,值可以是任意支持的数据类型。
对redis执行 SET、DEL、GET 、LRANGE、FLUSHDB、RANDOMKEY、DBSIZE、EXISTS、RENAME、KEYS等涉及到数据操作命令,本质上依然是对redisServer中db内元素的dict字典(键空间)进行操作
数据库的切换实现
redisClient中的db属性指向redisServer中其中一个db数组的属性,切换只是改变redisClient的db指向
数据库数据的超时处理实现
1.通过命令设置过期时间
expire:设置指定键空间生存时间(秒)
pexpire:设置指定键空间生存时间(毫秒)
expireAt:设置指定键空间的过期时间(秒时间戳)
pexpirreAt:设置指定键空间的过期时间(毫秒时间戳)
*本质上来说所有的命令最后执行的都是pexpireAt命令,其他命令只不过进行了时间单位的换算
2.过期设置的实现
redisDb结构体中有一个键与其过期时间的字典expires(时间戳)。
3.过期时间的移除
移除expires过期字典中的键空间。
4.计算剩余过期时间
由TTL和PTTL指令来实现,原理即通过计算expires中保存的时间和当前时间的时间差。
5.过期判定
检查expires中是否存在指定key,如果存在进一步比较当前时间与过期时间值。
6.过期处理
定时删除
通过定时器,通过expires中设置的过期进行删除
惰性删除
获取数据时,进行判断删除
定期删除
通过定时器,按一定时间阈值去扫描expires发现过期后删除
redis的删除策略
1.redis通过expireIfNeeded函数在获取数据时进行判断,如果过期删除并返回空,
2.通过activeExpireCycle函数随机对expires字典扫描,发现过期删除
- 执行SAVE、BGSAVE产生的RDB不包含过期的键。
- 执行BGREWRITE产生的重写AOF不包含过期的键,当键过期后AOF会追加一条DEL指令。
- 主服务器删除过期键后,会向所有从服务器发布DEL指令删除过期键,从服务器不会进行过期处理。
- 对数据库进行处理时,会向所有客户端发送修改通知。
RDB持久化
RDB的创建
- SAVE指令:暂停所有线程,保存后才响应。
- BGSAVE指令:创建子线程对数据进行保存处理。
- SAVE指令+时间阈值:
- 通过redisServer的saveparms保存时间阈值
- 通过redisServer的dirty计数器记录数据变更次数
- 通过redisServer的lastsave记录最后修改的时间戳
- 通过redisServer的serverCorn每隔100毫秒根据dirty计算器和lastsave时间戳与saveparam保存的参数进行比较,符合条件则进行保存
RDB的载入
RDB的载入通过rdb.c/rdbLoad函数实现
- 如果开启了AOF功能则优先读取AOF
- AOF功能关闭才会读取RDB
RDB的格式
RDB的为二进制压缩文件,组成部分如下:
REDIS | db version | databases | EOF | check_sum |
AOF持久化
AOF(Append Only File),与mysql的binlog类似记录详细的数据变动指令。
AOF实现
命令追加:redisServer 的aof_buf缓存区追加修增修改命令。
命令写入与同步:调用flushAppendOnlyFile(函数进行数据保存同步)
flushAppendOnlyFile函数根据服务器配置appendfsync来进行保存与同步
1.no:写入文件,但不同步。(由操作系统决定)
2.everysec:由单独异步线程检查,每一秒都会同步(只差一秒的数据变化,默认配置)
3.always:立即保存并同步(最慢最安全 )
AOF重写
AOFREWITE:停止响应,对数据库当前状态直接保存,保存后重命名(不解析旧文件)
BGREWITEAOF:Redis会维护一个缓存区,用于将新的数据变更保存至此,当子进程保存完成后,将缓存区的内容再次追加到新的AOF文件中,最后由服务器将完成AOF文件的替换