Redis单机数据库

1 服务器中的数据库

  Redis服务器所有数据库都保存在服务器状态的redisServer结构的db数组中,db数组中的每一项都是一个redisDb结构,每个redisDb结构代表一个数据库:

struct redisServer{
    // ...
    // 服务器数据库数量
    // 在服务器初始化时,程序会根据服务器状态的dbnum属性来决定创建多少个数据库,默认值为16
    int dbnum;
   //   一个数组,保存着服务器中所有数据库
    redisDb *db;
    // ...
}

  默认情况下,dbnum属性的值是16,及Redis服务器默认会创建16个数据库。如下图所示

服务器数据库示例

  每个Redis客户端都有自己的目标数据库,每当客户端执行数据库写命令或者数据库读命令的时候,目标数据库就会成为这些命令的操作对象。
  默认情况下,Redis客户端的目标数据库为0号数据库,但客户端可以通知执行SELECT命令来切换目标数据库。
  在服务器内部,客户端状态redisClient结构的db属性记录了客户端当前的目标数据库,这个属性是一个指向redisDb结构的指针:

struct redisClient{
    //...
    // 记录客户端当前正在使用的数据库
    redisDb *db;
    // ...
}

  在默认情况下,客户端的目标数据库:

默认情况下客户端的目标数据库

  若客户端执行SELECT 1,那么客户端的目标数据库就变为1号数据库:
客户端的目标数据库为1号数据库

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

2 数据库键空间

  Redis是一个键值对(key value pair)数据库服务器,服务器中的每个数据库都有一个redisDb结构表示,其中redisDb结构中的dict字典保存了数据库所有的键值对,这个字典称为键空间。

struct redisDb{
  
    // ...
    // 数据库键空间,保存着数据库中所有键值对
    dict *dict;
  // ...
}

  键空间的键都是字符串类型。
  键空间的值可以是字符串对象、列表对象、哈希对象、集合对象和有序表对象。
  例如执行以下命令:

redis> set message "hello redis";
OK
redis> rpush alphabet "a" "b" "c";
(integer) 3
redis> hset book name "Redis";
(integer) 1
redis> hset book author "Carlson";
(integer) 1
redis> hset book publisher "Manning";
(integer) 1

  那么数据库的键空间如下图所示:


数据库键空间

  因为数据库的键空间是一个字典,所以所有对数据库的操作,如添加一个键值对或删除一个键值对等,都是通过对键空间字典进行操作来实现的。如下图表示删除键为book的键值对,对于其他操作与之类似。


删除book键后数据库键空间

3 键的生存时间或过期时间

  通过EXPIRE命令或PEXPIRE命令,客户端可以以秒或毫秒精度为数据库某个键设置生存时间(time to live,TTL),经过指定的秒数或毫秒数之后,服务器就会自动删除生存时间为0的键。

 3.1 设置过期时间

  Redis有四个不同的命令用于设置键的生存时间和过期时间:

(1) EXPIRE<key> <ttl>:将键key的生存时间设置为ttl秒。
(2) PEXPIRE<key> <ttl>:将键key的生存时间设置为ttl毫秒。
(3) EXPIREAT<key> <ttl>:将键key的过期时间设置为ttl秒。
(4) PEXPIREAT<key> <ttl>:将键key的生存时间设置为ttl毫秒。

  这四种方法内部都是使用PEXPIREAT命令实现的。

 3.2 保存过期时间

  redisDb结构的expires字典保存了数据库中所有键的过期时间,称这个字典为过期字典:

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

struct redisDb{
  
    // ...
    // 过期字典,保存着键的过期时间
    dict *expires;
  // ...
}

带有过期字典的数据库

  注:实际中,键空间的键和过期字典的键都是指向同一个键对象,所以不会出现任何重复对象,也不会浪费任何空间。这里仅仅是为了展示效果。
  上图的过期字典中保存了两个键值对,同理新增一个键值对、移除(PERSIT)或更改一个键值对,以及计算并返回剩余生存时间(TTL)等都是对过期字典操作。

4 过期键删除策略

  过期的键会被保存在哈希表中,不会立刻被删除。Redis过期删除策略有三种:
  (1) 定时删除:对内存友好,对CPU不友好。通过创建一个定时器,到过期时间点上就把所有过期的键删除掉。
  (2) 惰性删除:对内存不友好,对CPU友好。每次从键空间取键的时候,判断该键是否过期了,如果过期了就删除该键。
  (3) 定期删除:这是对内存和CPU影响折中的策略。每隔一段时间,程序就对数据库进行一次检查,删除里面过期的键。删除操作执行的时长和频率通过实际业务设置确定。
   Redis服务器使用的是惰性删除和定期删除两种策略:通过配合使用两种删除策略,服务器可以很好的合理使用CPU时间和避免浪费内存之间取得平衡。

5 内存溢出机制

  当Redis所用的内存达到maxmomery上限时会触发相应的溢出控制机制。溢出策略有以下几种:

策略 描述
volatile-lru 根据LRU算法删除设置过期属性的键。
volatile-ttl 根据键值对象的ttl属性,删除最近将要过期数据。
volatile-random 从已设置过期的键中任意选择数据删除。
allkeys-lru 从所有数据集中挑选最近最少使用的数据删除。
allkeys-random 从所有数据集中任意选择数据删除。
noeviction 默认策略,不删除任何数据,拒绝所有写入操作并返回客户端错误信息。

  内存溢出策略可以根据实际需求灵活定制,如当设置volatile-lru策略时,保证具有过期属性的键可以根据LRU剔除,而未设置过期属性的键可以永远保留。同理,还可以使用allkeys-lru策略把Redis变成纯缓存服务器使用。

6 AOFRDB和复制功能对过期键的处理

 6.1 生成RDB文件

  执行SAVE命令或BGSAVE命令生成新的RDB文件时,对已经过期的键不会保存到新创建的RDB文件中
  数据库中包含过期键不会对生成新的RDB文件造成影响。

 6.2 载入RDB文件

(1) 如果服务器以主服务器模式运行,那么载入RDB文件时,只会载入未过期的键,对已经过期的键不会载入到数据库中
(2) 如果服务器以从服务器模式运行,那么在载入RDB文件时,文件中保存的所有的键,无论是过期还是未过期都会被载入到数据库中。

 6.3 AOF文件写入

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

 6.4 AOF重写

  和生成RDB文件时类似,对于已经过期的键不会保存到重写后的AOF文件中。

 6.5 复制

  当服务器运行在复制模式下时,从服务器的过期键删除动作由主服务器控制:

(1) 主服务器在删除一个过期键后,会显示地向所有从服务器发送一个DEL命令,告知从服务删除这个过期键。
(2) 从服务器在执行客户端发送的读命令时,即使碰到过期键也不会将过期键删除,而是继续像处理未过期的键一样来处理过期键。
(3) 从服务器只有在接到 主服务器发来的DEL命令之后,才会删除过期键。

  通过由主服务器来控制从服务器统一地删除过期键,可以保证主从服务器数据的一致性,也正是因为这个原因,当一个过期键仍然存在于主服务器的数据库时,这个过期键在从服务器里的复制品也会继续存在。
  如有一对主从服务器,它们数据中都保存三个相同的键message、xxx、yyy,其中message为过期键。


  这时客户端向从服务器发送get message命令,那么从服务器将发现message键已经过期,但是从服务器并不会删除message键,而是将message的键的值返回给客户端,就好像message键没有过期一样。

  当有客户端向主服务器发送相同的命令时,那么主服务发现message键过期并将其删除,同时向从服务器发送DEL message命令,最后向客户端返回空回复。

  从服务器接收到主服务器的DEL命令后,将数据库中的message键删除,那么主从服务器的数据库状态又会保持一致了。

小结

(1) Redis服务器所有的数据库都保存在redisServer.db数组中,而数据库的数量则是由redisServer.dbnum属性保存,默认数量是16个。
(2) 客户端通过修改目标数据库的指针来切换不同的数据库,默认使用的是0号数据库。
(3) 数据库的键值对保存在dict字典中,键的过期时间保存在expires数组中。
(4) 过期键的删除策略有定时删除、惰性删除和定期删除三种,Redis服务器使用的是定期删除和惰性删除两种。
(5) Redis内存溢出策略有7中,应该根据实际需求灵活定制。
(6) 执行SAVEBGSAVE命令生成的RDB文件不会包含过期的键。
(7) AOF重写的文件也不会包含过期的键。当一个过期的键被删除之后,服务器会追加一条DEL命令到现有的AOF文件的末尾,显示的删除过期键。
(8) 当主服务删除一个过期键后,它会向所有从服务器发送一条DEL命令,显示的删除过期键。
(9) 从服务器即使发现过期键也不会删除这个过期键,而是等待主节点发送来DEL命令,这种统一化、中心化的过期策略可以保证主从服务器数据的一致性。
  本文完


  注:本文参考《Redis设计与实现》,如发现错误,请指证!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容