Redis服务器将所有数据库都保存在服务器状态的redisServer结构中的db数组中,db数组的每个项都是一个redisDb结构,每个redisDb结构代表一个数据库:
struct redisServer { // 一个数组,保存着服务器中的所有数据库 redisDb *db; }在初始化服务器时,程序会根据服务器状态的dbnum属性来决定应该创建多少个数据库:
struct redisServer { // 服务器的数据库数量 int dbnum; }dbnum属性的值由服务器配置的database选项决定,默认情况下,该选项的值为16,所以Redis服务器默认会创建16个数据库,如图所示:
默认情况下,Redis客户端的目标数据库为0号数据库,但客户端可以通过执行SELECT命令来切换目标数据库。 在服务器内部,客户端状态的redisClient结构的db属性记录了客户端当前的目标数据库。
typedef struct redisClient { // 记录客户端当前正在使用的数据库 redisDb *db; }redisClient;redisClient.db指针指向redisServer.db数组的其中一个元素,而被指向的元素就是客户端的目标数据库。 例如,如果某个客户端的目标数据库为1号数据库。 如果这时客户端执行命令SELECT 2,将目标数据库改为2号数据库 通过修改redisClient.db指针,让它指向服务器中的不同数据库,从而实现切换目标数据库的功能–这就是SELECT命令的原理。
Redis是一个键值对(key-value pair)数据库服务器。服务器中的每个数据库都由一个redisDb结构表示,其中,redisDb结构的dict字典保存了数据库中的所有键值对,这个字典被称为键空间。
typedef struct redisDB { // 数据库键空间,保存着数据库中的所有键值对 dict *dict; }redisDb;键空间和用户所见的数据库是直接对应的:
键空间的键也就是数据库的键,每个键都是一个字符串对象。键空间的值也就是数据库的值,每个值可以是字符串对象、列表对象、哈希表对象、集合对象和有序集合对象中的任意一种Redis对象。添加一个新键值对到数据库,实际上就是将一个新键值对添加到键空间字典里面,其中,键为字符串对象,而值对象为任意一种类型的Redis对象。
删除数据库中的一个键,实际上就是在键空间里面删除键所对应的键值对对象。
对一个数据库键进行更新,实际上就是对键空间里面键所对应的值对象进行更新,根据值对象的类型不同,更新的具体方法也会有所不同。
对一个数据库键进行取值,实际上就是在键空间中取出键所对应的值对对象,根据值对象的类型不同,具体的取值方法也会有所不同。
通过EXPIRE命令或者PEXPIRE命令,客户端可以秒或者毫秒精度为数据库中的某个键设置生存时间,在经过指定的秒数或者毫秒数之后,服务器就会自动删除生存时间为0的键。
redisDb结构的expiress字典保存了数据库中所有键的过期时间,称为过期字典:
PERSIST命令可以移除一个键的过期时间;
通过过期字典,程序可以用以下步骤检查一个给定键是否过期: 1)检查给定键是否存在于过期字典:如果存在,那么取得键的过期时间。 2)检查当前UNIX时间戳是否大于键的过期时间:如果是的化,那么键已经过期;否则的话,键未过期。
三种删除策略:
定时删除:在设置键的过期时间同时,创建一个定时器,让定时器在键的过期时间来临时,立即执行对键的删除操作。优点:对内存最友好,缺点:对CPU时间最不友好。惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查取得键是否过期,如果过期的话,就删除该键;如果没有过期,就返回该键。优点:对CPU时间最友好。缺点:对内存最不友好,会出现浪费内存情况。定期删除:每隔一段时间,程序就对数据库进行一次检查,删除里面的过期键。至于要删除多少过期键,以及要检查多少个数据库,则由算法决定。是前两种策略的一种整合和折中。难点是确定删除操作执行的时长和频率。 在这三种策略中,第一种和第三种为主动删除策略,而第二种则为被动删除策略。Redis服务器实际使用的是惰性删除和定期删除策略;
过期键的惰性删除策略由expireIfNeeded函数实现,所有读写数据库的Redis命令在执行前都会调用expireNeeded函数对输入键进行检查:
如果输入键已经过期,那么expireIfNeeded函数将输入键从数据库中删除。如果输入键未过期,那么expireIfNeed函数不做动作。过期键的定期删除策略由activeExpireCycle函数实现,每当Redis的服务器周期性操作serverCron函数执行时,activeExpireCycle函数就会被调用,它在规定的时间内,分多次遍历服务器中的各个数据库,从数据库的expiress字典中随机检查一部分键的过期时间,并删除其中的过期键。
在执行SAVE命令或者BGSAVE命令创建一个新的RSB文件时,程序会对数据库中的键进行检查,已过期的键不会被保存到新创建的RDB文件中。
当服务器以AOF持久化模式运行时,如果数据中的某个键已经过期,但它还没没有被惰性删除或者定期删除,那么AOF文件不会因为这个过期键而产生任何影响。 当过期键被惰性删除或者定期删除之后,程序会向AOF文件追加一条DEL命令,来显示地记录该键已被删除。
在执行AOF重写地过程中,程序会对数据库中地键进行检查,已过期地键不会被保存到重写后地AOF文件中。
当服务器运行在复制模式下时,从服务器地过期键删除动作由主服务器控制:
主服务器在删除一个过期键之后,会显示地向所有从服务器发送一个DEL命令,告知从服务器删除这个过期键。从服务器在执行客户端发送地读命令时,即使碰到过期键也不会将过期键删除,而是继续像处理未过期的键一样来处理过期键。从服务器只有在接到主服务器发来的DEL命令之后,才会删除过期键。《Redis设计与实现》