数据库结构及过期删除

本文主要记录下数据库的相关源码以及过期删除策略。

数据库

Redis的服务器端中,有两个数据结构:redisServerclient,分别表示着当前Redis中服务端的状态以及客户端的状态。

server

定义在src/server.h中:

struct redisServer {
      ...
      redisDb *db;
      ...
      int dbnum;  /* Total number of configured DBs */
      ...
}

这个代表服务端的数据结构太长了,这里只列出dbdbnum两个属性。db是指向Redis服务端的数据库的数组,dbnum代表这个数组的长度。默认情况下,server端会有16个数据库。

数据库的切换通过select命令。

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]>

client

typedef struct client {
    ...
    redisDb *db;            /* Pointer to currently SELECTed DB. */
    ...
} client;

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

因此,select命令的原理就是改变这个db的指向了。

redisDb

redisDb的定义如下:

typedef struct redisDb {
    dict *dict;                 /* The keyspace for this DB */
    dict *expires;              /* Timeout of keys with a timeout set */
    dict *blocking_keys;        /* Keys with clients waiting for data (BLPOP)*/
    dict *ready_keys;           /* Blocked keys that received a PUSH */
    dict *watched_keys;         /* WATCHED keys for MULTI/EXEC CAS */
    int id;                     /* Database ID */
    long long avg_ttl;          /* Average TTL, just for stats */
    unsigned long expires_cursor; /* Cursor of the active expire cycle. */
    list *defrag_later;         /* List of key names to attempt to defrag one by one, gradually. */
} redisDb;

一个redisDb就是一个数据库。其中dict是该数据库的所有键。expires是该数据库的所有键的过期时间的集合。

过期字典中的key是指向redisDb.dict的键的指针,value是过期时间。

过期删除

redisDb结构的expires字典保存了数据库中所有键的过期时间,它有两种情况:

  • 过期字典的键是一个指针,这个指针指向键空间中的某个键对象(也即是某个数据库键)。
  • 过期字典的值是一个long long类型的整数,这个整数保存了键所指向的数据库键的过期时间(一个毫秒精度的UNIX时间戳)

设置过期时间(Time to Live, TTl)的一些命令:

  • expire <key> <ttl>:将键key的生存时间设置为ttl秒
  • pexpire <key> <ttl>:将键key的生存时间设置为ttl毫秒
  • expireat <key> <timestamp:将键key的过期时间设置为timestamp所指定的秒数是时间戳
  • pexpireat <key> <timestamp:将键key的过期时间设置为timestamp所指定的毫秒数时间戳

实际上expirepexpire以及expireat命令都是由pexpireat命令实现的。

移除过期时间:persist <key>

过期键删除策略

过期键删除一共有三种策略:

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

定时删除

定时删除策略可以保证内存不被浪费,但是容易集中在某一段时间出现大量的过期键,导致CPU被占满。

惰性删除

有大量内存被占用而浪费,可以看成是内存泄漏了。

定期删除

均衡CPU和内存的一种策略,保证CPU时间和内存都不会出现太多的争抢和浪费。主要是策略和算法难以确定。

Redis服务器实际使用的是惰性删除和定期删除两种策略。

惰性删除策略的实现在db.c/expireIfNeeded中,定期删除策略的实现在expire.c/activeExpireCycle中。

暂无评论

发表评论

电子邮件地址不会被公开。 必填项已用*标注

浙ICP备19002997号