服务器中的数据库
struct redisServer {
// 一个数组,保存着服务器中的所有数据库
redisDb *db;
// 服务器的数据库数量
// 值由服务器配置的database选项决定,默认情况下是16个数据库
int dbnum;
};
切换数据库
默认情况下,redis客户端的目标数据库为0号数据库,可以通过SELECT
命令来切换数据库
redis> SET msg "hello world"
OK
redis> GET msg
"hello world"
redis> SELECT 2
OK
redis[2]> GET msg
(nil)
客户端状态redisClient
结构的db属性记录了客户端当前的目标数据库,这是属性是一个指向redisDb
结构的指针
typedef struct redisClient {
// 记录客户端当前正在使用的数据库
redisDb *db;
} redisClient;
通过修改redisClient.db
指针,可以实现数据库的切换,即SELECT
命令的实现
数据库键空间
redisDb
结构的dict字典保存了数据库中所有键值对
typedef struct redisDb {
dict *dict;
}
键空间和用户所见的数据库是直接对应的:
- 键空间的键也就是数据库的键, 每个键都是一个字符串对象。
- 键空间的值也就是数据库的值, 每个值可以是字符串对象、列表对象、哈希表对象、集合对象和有序集合对象在内的任意一种 Redis 对象。
添加新键
添加一个新键值对到数据库,实际上就是将一个新键值对添加到键空间字典里面
删除键
删除数据库中的一个键,实际上就是在键空间里面删除键所对应的键值对对象
DEL key [...]
其他键空间操作
FLUSHDB
:清空整个数据库
RANDOMKEY
:随机返回一个键
DBSIZE
:数据库键数量
读写键空间时的维护操作
- 在读取一个键之后(读操作和写操作都要对键进行读取), 服务器会根据键是否存在, 以此来更新服务器的键空间命中(hit)次数或键空间不命中(miss)次数, 这两个值可以在 INFO stats 命令的
keyspace_hits
属性和keyspace_misses
属性中查看。 - 在读取一个键之后, 服务器会更新键的 LRU (最后一次使用)时间, 这个值可以用于计算键的闲置时间, 使用命令 OBJECT idletime 命令可以查看键
key
的闲置时间。 - 如果服务器在读取一个键时, 发现该键已经过期, 那么服务器会先删除这个过期键, 然后才执行余下的其他操作。
- 如果有客户端使用 WATCH 命令监视了某个键, 那么服务器在对被监视的键进行修改之后, 会将这个键标记为脏(dirty), 从而让事务程序注意到这个键已经被修改过。
- 服务器每次修改一个键之后, 都会对脏(dirty)键计数器的值增一, 这个计数器会触发服务器的持久化以及复制操作执行。
- 如果服务器开启了数据库通知功能, 那么在对键进行修改之后, 服务器将按配置发送相应的数据库通知。
设置键的生存时间或过期时间
EXPIRE
:设置生存时间,单位:秒。EXPIRE <key> <ttl>
SET key value
// 五秒的生存时间
EXPIRE key 5
PEXPIRE
:设置生存时间,单位:毫秒。PEXPIRE <key> <ttl>
EXPIREAT
:设置过期时间,单位秒(时间戳)EXPIREAT <key> <timestamp>
PEXPIREAT
:设置过期时间,单位毫秒。PEXPIREAT <key> <timestamp>
上面四个命令最后都是使用PEXPIREAT
命令来实现的
TTL
:接受一个带有生存时间的键,返回这个键的剩余生存时间
PTTL
:接受一个带有过期时间的键,返回这个键的剩余生存时间
保存过期时间
redisDb结构的expires
字典保存了数据库中所有键的过期时间
- 过期字典的键是一个指针,指向键空间中某个键对象。
- 过期字典的值是一个long long类型的整数,即过期时间,是一个毫秒精度的UNIX时间戳
typedef struct redisDb {
// 过期字典,保存键的过期时间
dict *expires;
} redisDb;
移除过期时间
PERSIST <key>
过期键的判定
通过过期字典,检查以下步骤
- 判断键是否在过期字典中,如果存在则返回过期时间
- 判断当前UNIX时间戳是否大于键的过期时间,如果是,那么键已经过期
过期键删除策略
定时删除
在设置键的过期时间的同时,创建一个定时器,让定时器在键的过期时间来临时,立即执行对键的删除操作
- 对内存友好,尽快释放过期键占用的内存
- 对CPU不友好,特别在过期键比较多的情况下
惰性删除
每次从键空间中获取键时,都检查取得的键是否过期,如果过期的话,就删除该键,如果没过期,就返回该键
- 对CPU友好
- 对内存不友好,特别是有很多过期键但又没有访问的时候
定期删除
每隔一段时间,程序就对数据库进行一次检查,删除里面的过期键。
最主要的是合理地设置删除操作的执行时长和执行频率
Redis的过期键删除策略
惰性删除策略的实现
过期键的惰性删除策略由db.c/expireIfNeeded
函数实现,所有读写数据库的Redis命令在执行之前都会调用该函数对输入键进行检查:
参考:Redis设计与实现