redis数据库详细介绍

Redis是key-value数据库

Redis优势

  1. 高性能,数据存在内存
  2. 支持多种数据结构(字符串,列表,哈希表,集合,有序集合)
  3. 单个操作和多个操作(MULTI和EXEC执行包裹)均支持事物,即原子性
  4. 支持发布订阅
  5. 支持key过期(秒和毫秒),过期key删除有惰性删除和定期删除两种策略
  6. 支持持久化(数据保存到磁盘), RDB(保存快照,有数据丢失风险,但恢复快,文件小,性能消耗低(会fork子进程进行备份,对父进程性能无影响))和AOF(保存操作日志,数据完整,但恢复慢,文件大(有优化策略,可能合并多个操作),性能消耗大)
  7. 支持主从

服务器中的数据库

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

    1
    2
    3
    4
    5
    6
    7
    struct redisServer {
    //...
    redisDb *db; // 一个数组,保存着服务器中的所有数据库 数组大小为dbnum
    //...
    int dbnum; // 服务器的数据库数量 由服务器配置的database选项决定 默认情况下该值为16
    //...
    };
  • 初始化服务器时,程序更具服务器状态的dbnum属性决定创建多少个数据库。dbnum属性的值有服务器配置的database选项决定。该值默认是16,所以Redis数据库默认情况下会创建16个数据库。

切换数据库

  • 每个Redis客户端都有自己的目标数据库,每当客户端执行数据库写命令或读命令的时候,目标数据库就会成为这些命令的操作对象。
  • 默认情况下,Redis客户端的目标数据库为0号数据库。使用SELECT命令切换数据库。在客户端状态client结构的db属性中记录了客户端当前的目标数据库。client.db指针指向redisServer.db数组中的其中一个元素,被指向的元素就是客户端的目标数据库。通过修改client.db指针,让它指向服务器中的不同数据库,实现切换目标是数据库的功能,这就是SELECT命令的实现原理。

    1
    2
    3
    4
    5
    typedef struct client {
    //...
    redisDb *db; // 记录客户端目前的目标数据库
    //...
    } client;
  • Redis没有返回客户端目标数据库的命令。如果使用多数据库,那么最好每次在执行危险操作时,先显示地切换到指定的数据库,然后才执行别的命令。

数据库键空间

  • Redis是一个键值对数据库服务器,服务器中的每个数据库都由server.h/redisDb结构表示。redisDb结构的dict字典保存了数据库中的所有键值对, 称为键空间。
  • 键空间和用户所见的数据库直接对应:
    1. 键空间的键即数据库的键,每个键都是一个字符串对象。
    2. 键空间的值即数据库的值,每个值可以是字符串对象,列表对象,哈希表对象,集合对象,有序集合对象中的任意一种Redis对象。

添加新键

  • 添加一个新键值对到数据库,实际就是将一个新键值对添加到键空间字典里面。

删除键

  • 删除数据库中的一个键,实际上就是在键空间里面删除键对应的键值对对象。

更新键

  • 对一个数据库键进行更新,实际上是对键空间里面键对应的值对象进行更新,根据值对象的类型不同,更新的具体方法也有所不同。

对键取值

  • 对一个数据库键取值,实际就是去除键所对应的值对象,根据值对象的类型不同,具体的取值方法也不同。

其它键空间操作

读写键空间时的维护操作

  • 当使用Redis命令对数据库进行读写操作时,服务器不仅会对键空间执行指定的读写操作,还会执行一些额外的维护操作:
    1. 读取一个键之后(读操作和写操作),服务器会根据键释放存在来更新服务器的键空间命中次数和不命中次数,这两个值在INFO stats命令的keyspace_hits属性和keyspace_misses属性中可查看。
    2. 读取一个键之后,服务器会更新键的LRU(最后一次使用)时间,用于计算键的闲置时间,使用OBJECT idletime 命令可以查看键的闲置时间。
    3. 服务器读取一个键,发现这个键过期了,会先删除这个过期键,在执行余下的操作。
    4. 如果客户端使用WATCH命令监视了某个键,服务器会对被监视的键进行修改后标记为脏,让事物注意到这个键已经被修改。
    5. 服务器每修改一次键,都会对脏键计数器的值增1,该计数器会触发服务器的持久化以及复制操作。
    6. 如果服务器开启了数据库通知功能,那么键更新后服务器会按照配置发送相应的数据库通知。

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

  • 通过EXPIRE命令或者PEXPIRE命令,客户端可以以秒或者毫秒精度为数据库中的某个键设置生存时间(TTL),在经过指定时间后,服务器会自动删除生存时间为0的键。
  • 通过EXPIREAT命令或者PEXPIREAT命令,客户端可以以秒或者毫秒精度给数据库中的某个键设置过期时间,过期时间是一个UNIX时间戳,当键的过期时间来临,服务器就会自动删除这个键。
  • TTL命令和PTTL命令接受一个带有生存时间或者过期时间的键,返回这个键的剩余生存时间,即该键被服务器自动删除还有多长时间。

设置过期时间

  • Redis有四个不同命令用于设置键的生存时间或过期时间。
    1. EXPIPE 命令用于键key的生存是假设置为ttl秒
    2. PEXPIPE 命令用于键key的生存是假设置为ttl毫秒
    3. EXPIPEAT 命令用于将键key的过期时间设置为timestamp所制定的秒数时间戳
    4. PEXPIPEAT 命令用于将键key的过期时间设置为timestamp所制定的毫秒数时间戳
  • EXPIPE、PEXPIPE、EXPIPEAT最终都会转化为PEXPIPEAT命令。

保存过期时间

  • redisDb结构的expires字典保存了数据库中所有键的过期时间,称为过期字典
    1. 过期字典的键是一个指针,指向键空间中的某个键对象
    2. 过期字典的值是一个long long类型的整数,保存了键所指向的数据库键的过期时间(一个毫秒精度的UNIX时间戳)。
  • 当客户端执行PEXPIREAT命令(或其它三个命令)为一个数据库键设置过期时间时,服务器会在数据库的过期字典中关联给定的数据库键和过期时间。

移除过期时间

  • PERSIST命令移除一个键的过期时间,PERSIST命令是PEXPIREAT命令的反操作,PERSIST命令在过期字典中查找给定的键,并解除键和值在过期字典中的关联。

计算并返回剩余生存时间

  • TTL命令以秒为单位返回键的剩余生存时间,而PTTL命令则以毫秒为单位返回键的剩余生存时间,这两个命令都是通过计算键的过期时间和当前时间之间的差来实现的。

过期键的判定

  • 通过过期字典,1.检查给定键是否存在于过期字典,如果存在,取得键的过期时间,2.检查当前UNIX时间戳是否大于键的过期时间,如果是的话,那么键已经过期,否则键未过期。

Redis的过期键删除策略

  • Redis实际使用惰性删除和定期删除两种过期键删除策略。

惰性删除策略的实现

  • 过期键的惰性删除策略由db.c/expireIfNeeded函数实现,所有读写数据库的Redis命令在执行之前都会调用expireIfNeeded函数对输入键进行检查:1.如果输入键已经过期,expireIfNeeded函数将输入键从数据库中删除。2.如果输入键未过期,expireIfNeeded函数不做动作。

    定期删除策略的实现

  • 过期键的定期删除策略由server.c/activeExpireCycle函数实现,每当Redis的服务器周期性操作server.c/serverCron函数执行时,activeExpireCycle函数就会被调用,在规定时间内,分多次遍历服务器中的各个数据库,从数据库的expires字典中随机检查一部分键的过期时间,并删除其中的过期键。
  • activeExpireCycle函数的工作模式如下:
    1. 函数每次运行时,都从一定数量的数据库中取出一定数量的随机键进行检查,并删除其中的过期键
    2. 全局变量current_db记录当前activeExpireCycle函数检查的进度,并在下次activeExpireCycle函数调用时从上次的进度进行处理。
    3. 当服务器中所有数据库都被检查一遍,current_db变量重置为0,然后再开始下一轮的检查。

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

生成RDB文件

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

载入RDB文件

  • 启动服务器时,如果服务器开启RDB功能,服务器将对RDB文件进行载入
    1. 服务器以主服务器模式运行,载入RDB文件时,程序会对文件中保存的键进行检查,未过期的键会被载入数据库,过期键会被忽略。
    2. 服务器以从服务器模式运行,载入RDB文件时,文件中保存的所有键都会被载入数据库。因为主从服务器进行数据同步时,从服务器数据库会被清空,所以过期键对载入RDB文件的从服务器不会造成影响。

AOF文件写入

  • 当服务器以AOF持久化模式运行时,如果数据中的某个键已经过期,但它还没有被惰性删除或者定期删除,那么AOF文件不会因为这个过期键而产生任何影响。当过期键被惰性删除或定期删除后,程序会向AOF文件追加一条DEL命令,来显示记录该键已被删除。

AOF重写

  • 在执行AOF重写的过程中,程序对数据库中的键进行检查,已过期的键不会被保存到重写后的AOF文件中。

复制

  • 当服务器运行在复制模式下时,从夫妻的过期键删除动作由主服务器控制:
    1. 主服务器在删除一个过期键之后,会显式地向所有从服务器发送一个DEL命令,告知从服务器删除这个过期键。
    2. 从服务器在执行客户端发送的读命令时,即使碰到过期键也不删除,而是继续像处理未过期键一样来处理过期键。
    3. 从服务器只有在接到主服务器发来的DEL命令之后,才会删除过期键。

数据库通知

  • 该功能让客户端通过订阅给定的频道或者模式,获知数据库中键的变化,以及数据库中命令的执行情况。
  • 关注某个键执行了什么命令的通知称为键空间通知。
  • 关注某个命令被什么键执行了的通知成为键事件通知。

参考资料