Redis(六):数据库

Redis数据库

本篇将对Redis服务器的数据库实现进行详细介绍,说明服务器保存数据库的方法,客户端切换数据库的方法。数据库保存键值对的方法,以及针对数据库的添加,删除,查看,更新操作的实现方式等。

服务器中的数据库

Redis服务器将所有的数据库都保存在服务器状态redis.h/redisServer结构的db数组中,db数组的每个项都是一个redis.h/redisDB结构,每个redisDB结构代表一个数据库。

struct redisServer{
    //一个数组,保存着服务器中的所有数据库
    redisDB *db;
};

在初始化服务器时,程序会根据服务器状态的dbnum属性来决定应该创建多少个数据库

struct redisServer{
     int dbnum;
     //服务器数据库数量
};

dbnum属性的值由服务器配置的database选项确定,默认情况下,该选项的值为16,所以Redis服务器会创建16个数据库。


切换数据库

每个redis客户端都有自己的目标数据库,每当客户端执行数据库写命令或者数据库读命令的时候,目标数据库就会成为这些命令的操作对象。

默认情况下,Redis客户端的目标数据库为0号数据库,但客户端可以通过执行SELECT命令来切换目标数据库。

以下代码演示了客户端在0号数据库设置并读取键msg,之后切换到2号数据库并执行类似操作的过程。

127.0.0.1:6379> set msg "hello world"
OK
127.0.0.1:6379> get msg
"hello world"
127.0.0.1:6379> select 2
OK
127.0.0.1:6379[2]> get msg
(nil)
127.0.0.1:6379[2]> set msg "ahhh"
OK
127.0.0.1:6379[2]> get msg
"ahhh"
127.0.0.1:6379[2]>

在服务器内部,客户端状态redisClient结构的db属性记录了客户端当前的目标数据库,这个属性是一个指向redisDB结构的指针:

typedef struct redisClient{
    redisDb *db;
}redisClient;

redisClient.db指针指向了redisServer.db数组的其中一个元素,而被指向的元素就是客户端的目标数据库。


这时客户端执行select2,将目标数据库改为2号数据库,那么客户端状态和服务器状态之间的关系就变化如下


通过修改redisClient.db指针,让它指向服务器的不同数据库,从而实现切换目标数据库的功能-这就是select命令的原理。

数据库键空间

Redis是一个键值对(key-value pair)数据库服务器,服务器中的每个数据库都由一个redis.h/redisDb结构表示,其中,redisDb结构的dict字典保存了数据库中的所有键值对,我们将这个字典称为键空间(key space)

typedef struct redisDb{
     dict *dict;
}redisDb;

dict的结构和相关内容可以看我的博客(Redis(四):字典)有详细的介绍。

键空间和用户所见的数据库是直接对应的:

  • 键空间的键也就是数据库的键,每个键都是一个字符串对象
  • 键空间的值也就是数据库的值,每个值可以说字符串对象,列表对象,哈希表对象,集合对象和有序列表对象中的任意一种redis对象。

因为数据库的键空间是一个字典,所以所以针对数据库的操作,比如添加一个键值对到数据库,或者从数据库中删除一个键值对,又或者再数据库中获取某个键值对,实际上都是通过对键空间字典进行操作来实现的。

设置键的生存时间或过期时间

通过expire命令或者pexpire命令,客户端可以以秒或者毫秒精度为数据库中的谋而键设置生存时间(Time to Live,TTL),在经过指定的秒数或者毫秒数之后,服务器就会自动删除生存时间为0的键:

127.0.0.1:6379[2]> set key "123"
OK
127.0.0.1:6379[2]> get key
"123"
127.0.0.1:6379[2]> expire key 10   //设置key的过期时间是10秒
(integer) 1
127.0.0.1:6379[2]> get key    //10秒以内可以获取到key的值
"123"
127.0.0.1:6379[2]> get key
"123"
127.0.0.1:6379[2]> get key
"123"
127.0.0.1:6379[2]> get key   //10秒以后就获取不到key的值了
"123"
127.0.0.1:6379[2]> get key
(nil)

与expire命令和pexpire命令类似,客户端可以通过expireat命令或者pexpireat命令,以秒或者毫秒精度给数据库中的某个键设置过期时间(expire time)

过期时间是一个UNIX时间戳,当键的过期时间来临,服务器就会自动从数据库中删除整个键。

TTL命令和PTTL命令接受一个带有生存时间或者过期时间的键,返回整个键的剩余生存时间,

127.0.0.1:6379[2]> set key "ser"
OK
127.0.0.1:6379[2]> expire key 1000
(integer) 1
127.0.0.1:6379[2]> ttl
(error) ERR wrong number of arguments for 'ttl' command
127.0.0.1:6379[2]> ttl key
(integer) 996
127.0.0.1:6379[2]>
过期键删除策略

如果一个键过期了,那么他什么时候会被删除

  • 定时删除:在设置键的过期时间的同时,创建一个定时器,让定时器在键的过期时间来临时,立即执行对键的删除操作。
  • 惰性删除:放任键过期不管,但是每次从键空间获取键时,都检测获取得的键是否过期,如果过期的话,就删除该键,如果没有过期,就返回该键。
  • 定期删除:每隔一段时间,程序就对数据库进行一次检查,删除里面的过期键。至于要删除多少过期键,以及要检查多少数据库,由程序算法决定。

这三种策略中,第一种和第三种为主动删除策略,而第二种则为被动删除策略。  

Redis的过期键删除策略

我们讨论了定时删除,惰性删除,定期删除三种过期键策略,Redis服务器实际使用的是惰性删除和定期删除两种策略,通过配合使用者两种策略,服务器可以很好的合理的使用CPU和避免浪费内存空间之间取得平衡。

惰性删除策略的实现

1.过期键的惰性删除策略由db.c/expireIfNeeded函数实现,所有的读写数据库的Redis命令在执行之前都会调用expireIfNeeded函数对输入键进行检查:

  •  如果输入键已经过期,那么expireIfNeeded函数将做输入键从数据库中删除。
  • 如果键未过期,那么expiredNeeded函数不做任何操作。


AOF,RDB和复制功能对过期键的处理

我们将探讨过期键对Redis服务器中其他模块的影响,看看RDB持久化功能,AOF持久化功能以及复制功能是如何处理数据库中的过期键的。

  • 生成RDB文件

在执行Save命令或者bgsave命令创建一个新的RDB文件时,程序会对数据库中的键进行检查,已过期的键不会被保存到新创建的RDB文件中。

例如:如果数据库中包含三个键,k1,k2,k3.并且k2已经过期,那么当执行Save命令或者bgsave命令时,程序只将k1,k3的数据保存到RDB文件中,而K2则会被忽略。

因此,数据库中包含过期键不会对生成新的RDB文件造成影响。

  • 载入RDB文件

在启动Redis服务器时,如果服务器开启了RDB功能,那么服务器对RDB文件进行载入

如果服务器以主服务器模式运行,那么载入RDB文件时,程序会对文件中保存的键进行检查,未过期的键会被载入到数据库中,而过期键则会被忽略,所以过期键对载入RDB文件的主服务器不会造成影响。

如果服务器以从服务器模式运行,那么在载入RDB文件时,文件中保存的所有键,不乱是否过期,都会被载入到数据库中,不过因为主从服务器在同步数据的时候,从服务器的数据库就会被清空。所以一般来讲,过期键对载入RDB文件的从服务器也不会造成影响。

2.AOF文件写入

当服务器以AOF持久化模式运行时,如果数据库中的某个键已经过期,但它还没有被惰性删除或者定期删除,那么AOF文件不会因为这个过期键而产生任何影响。

当过期键被惰性删除或者定期删除之后,程序会向AOF文件追加一条DEL命令,来显式地记录该键已被删除。

和生成RDB文件类似,在执行AOF重写的过程中,程序会对数据库中的键进行检查,已过期的键不会被保存到重写的AOF文件中。




猜你喜欢

转载自blog.csdn.net/qq_24210767/article/details/80431394
今日推荐