Redis数据库
Redis将数据库结构保存在RedisServer数据结构中的db数组中,数组大小由RedisServer结构中的dbnum决定,dbnum的默认大小为16。结构如下:
class RedisServer {
int dbnum;
RedisDb[] db;
}
而对于客户端而言,会选择一个指定的数据库,可以通过select命令来切换数据库,目标数据库保存在redis客户端数据结构RedisClient的db指针中。
class RedisClient {
RedisDb db;
}
RedisDb
Redis是一个K-V数据库,RedisDb中的dict字典保存了数据库中的所有键值对,这个字典被称作键空间。键空间的key是Redis字符串对象,value是RedisObject对象。此外还有expire字典保存了键空间中所有键的过期时间,expire的key是键空间中的key,value是保存的过期时间。
class RedisDb {
// 键空间
Dict dict;
// 键空间设置了过期时间的键的过期时间
Dict expire;
}
当使用Redis命令对数据库进行读写时,会进行一些额外操作
- 更新命中和不命中次数,通过Info status命令的keyspace_hits和keyspace_miss查看。
- 更新键的LRU时间,即ReidisObject的lru属性。
- 读取时发现键过期,会先删除这个过期键
- 若有客户端对该键使用watch命令并且服务端是写操作会标记键为脏(dirty),以便提示watch该键的客户端。每进行一次修改脏计数器会加一,这个计数器会触发持久化操作
通过pexpireat命令可以设置对Key过期时间(此外还有expire、expireat、pexpire命令,但底层实现都是pexpireat),过期时间由expire属性保存,通过过期时间和当前时间对比,可以判断键是否过期。此外,可以使用persist命令来移除键的过期时间。
过期键的删除策略
过期键一共由以下三种删除策略,其中第一种为被动删除策略,另外两种是主动删除策略。
- 惰性删除。即只在获取键值的时候检查是过期,如果过期则删除键。优点是CPU友好,但因为过期键不访问一直存在原因浪费内存。
- 定时删除。设置键过期的时候,创建计时器(timer),让定时器来执行删除。优点是及时快速,缺点是删除时会消耗CPU资源,对大量过期加上大量访问不友好,减少了吞吐量。
- 定期删除。每隔一段时间删除过期键,删除多少和检查几个数据库由算法决定。上面两种方案的折中策略,需谨慎设置执行策略。
AOF和RDB对过期键的处理:
- RDB:生成RDB文件时,过期Key不会被持久化到RDB文件中。读取RDB文件时,主服务器会对RDB文件中的过期Key进行检查,如果Key过期将不被载入,从服务器则全部载入不进行检查,当主从同步时,已删除的键将被同步。
- AOF:AOF写入时,已过期的但没有删除的不会对AOF产生任何影响,只有当键被删除了,才会显式的追加Key的Del命令。AOF重写时会对过期键进行检查。
- 复制:主服务器删除后会向从服务器发送Del命令同步键被删除。从服务器接受到客户端命令时,即使键已过期也会当作没过期处理。
数据库通知
通过订阅给定的频道或者模式来获知数据库的键的变化和命令执行情况。类似于MySQL监听Binlog。