深入了解Redis之Redis数据库的实现
Redis这个key-value
的存储系统,由于其高性能,支持主从复制和丰富的数据结构等特性,目前已经广泛应用于我们的各个场景之中。所以了解整理其相关的一些内容,用以加深我们对其的了解,方便之后更好的使用它实现我们的各种功能。
数据结构与对象
Redis底层的实现,主要用到的数据结构有简单动态字符串(SDS)、双端链表、字典、压缩列表、整数集合等。但REdis没有直接用这些数据结构来实现数据库的设计。而是基于这些数据结构创建了一个对象系统。
使用这种对象系统的好处
- 对不同的使用场景,可以为对象设置多种不同的数据结构实现,从而可以优化对象在不同场景下的使用效率
- 基于对象方便实现基于引用计数技术的内存回收机制
键值对的具体组成:
- 键 => 自字符串对象
- 值
- 字符串对象
- 列表对象
- 哈希对象
- 集合对象
- 有序集合对象
数据库的实现
Redis服务器的所有数据库都保存在”redisSeriver”结构中的“db” 的数组中。而db的结构则主要由“dict”和“expires”另个字典构成,dict字典负责保存键值对,而expires字典负责保存键的过期时间。
redisSeriver结构
struct redisSeriver{
//...
redisDb *db;
//...
};
redisDb结构
typeed struct redisDb{
//...
//数据库键值字典,保存数据库中所有键值对(又称之为,键空间)
dict *dict;
//过期字典,保存键的过期时间
dict *expires;
} redisDb;
服务器保存键值对的方法
键值字典和用户所见的数据库是直接对应的
- 该字典里的键就是我们数据库的键(每个键都是一个字符串对象)
- 字典的值就是数据库的值,每个值可以是字符串对象、列表对象、哈希表对象、集合对象和有序集合对象中的任一种对象。
相应的操作
当添加新键值对时
实际就是将一个新键值对添加到该“dict”键值字典中,其键为字符串对象,值为任意的一个Redis对象
删除键
实际上就是在该“dict”键值字典中删除键对应的键值对对象
更新键
对该键取值
当我们对这些键值进行这些读写操作的时候,redis还会执行一些额外的(维护)操作。这些操作有
- 当读取一个键后,会更新服务器键空间命中次数(hit)或不命中次数(miss)
- 读取一个键后,还会更新该键的LRU(最后一次使用时间)
- 当读取一个键时,发现该键已经过期,那么出返回结果还需要删除这个键
- 如果有客户端watch这个键,那么操作过这个键之后还需要将这个键标记为“dirty”,让watch这个键的客户端知道该键已经被修改过
- 当每次修改过一个键之后,都会对“dirty”计数器的值增1,以便触发服务器的持久化以及复制操作。
- 如果服务器开启了数据库通知的功能,那么修改了键之后,还需要按配置发送相应的通知。
服务器保存键值对过期时间的方法
过期时间是一个UNIX时间戳,当键的过期时间来临时,服务器就会自动从数据库中删除这个键。
redis有四个不同的命令来设置键的过期时间
- EXPIRE
(秒级) - PEXPIRE
(毫秒级) - EXPIREAT
(秒级) - PEXPIREAT
(毫秒级)
实际上,EXPIRE、PEXPIRE、EXPIREAT三个命令最终都是使用PEXPIREAT命令来实现的。
redisDb结构中,expires字典保存了数据库中所有键的过期时间。
- 过期字典的键是一个指针,这个指针指向键空间中的某个键对象
- 过期字典的值是一个long类型的整数,这个整数保存着过期时间(一个毫秒精度的UNIX时间戳)
服务器删除过期键值对的方法
过期键的判定
- 1、检查给定的键是否存在于过期字典,如果存在,那么取得键的过期时间
- 2、检查当前UNIX时间戳是否大于取得的键的过期时间,如果大于的,那么该键已过期;否则该键未过期。
一般的常见的过期键删除机制
- 定时删除,在设置键的时候设定一个定时器,让其在定时器结束时执行对键的删除操作
- 惰性删除,设置后不管,当需要再次获取该键时,先检查该键是否已经过期,如果过期的话,执行删除操作,没有过期的话,返回键值
- 定期删除,每隔一段时间,对所有键检查一次,有过期的则主动删除
Redis采用的过期键删除机制(惰性+定期)
惰性删除策略的实现
redis在所有读写数据库的Redis命令之前都会执行一个叫”expireIfNeeded”的函数对要操作的键进行检查
- 如果要操作的键已经过期,那么”expireIfNeeded”函数将该键从数据库中删除
- 如果未过期,那么”expireIfNeeded”函数不做操作
注:对所操作键存在与否的判断先与是否过期的判断
- 定期删除策略的实现
redis中会有一个周期性运行的操作函数“serverCron”。过期键的定期删除是由一个叫“activeExpireCycle”的函数实现。当serverCron函数运行时,activeExpireCycle 函数就会被调用。它会在规定的时间内分多次遍历服务器中各个数据库,从数据库的expires字典中随机检查一部分键的过期时间,并删除其中的过期键。
redis集群中过期键的删除
在Redis集群中 当主服务器删除一个过期键之后,它会向所有从服务器发送一条DEL命令,而从服务器即使发现过期键也不会自作主张的将其删除,而是等待主节点发来DEL命令,显式的进行删除。这种统一的、中心化的过期键策略可以保证主从服务器数据一致。