Redis的数据库

1、服务器中的数据库 

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

struct redisServer {

    redisDb *db; // 一个数组,保存着服务器中所有的数据库

    int dbnum; // 服务器的数据库数量

}

    在初始化服务器的时候 ,创建多少个数据库由dbnum决定,而dbnum由服务器配置的database决定的。

2、切换数据库

    客户端可以执行SELECT命令来切换数据库。

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

struct redisClient {

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

}

    通过修改db指针,从而实现切换数据库,这也是SELECT的原理。

3、数据库实现

struct redisDb {

    dict *dict; // 数据库键空间,保存着数据库中的所有键值对

}

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

   1、键空间的键就是数据库的键,每个键都是一个字符串对象

    2、键空间的值也是数据库的值,每个值可以是string、list、set、zset、hash中任意对象的一种。

3.1、 读取键空间时的维护操作

      当对redis数据库读写的时候,除了会执行指定的命令外,还会执行一些维护操作,包括:

   1、读写一个键后,服务器会根据键是否存在,更新hit和miss次数,更新键的命中率。可以通过INFO status 命令的keyspaces_hits 和 keyspaces_misses查看。

    2、读写一个键之后,会更新redisObject的lru时间。

    3、如果发现一个键已经过期,那么服务器会删除这个过期键(惰性删除)。

    4、如果有客户端WATCH命令监视某个键,那么服务器对这个在对被监视的键进行修改之后,会标记这个键为脏(dirty),从而让事务程序注意到这个键已经被修改了。

    5、服务器每修改一个键之后,都会对脏(dirty)键计数器的值加1,这个计数器会触发对数据库的持久化以及复制操作


4、设置键的过期时间

4.1、如何设置

通过 EXPIRE 、 PEXPIRE 、 EXPIREAT 和 PEXPIREAT 四个命令, 客户端可以给某个存在的键设置过期时间, 当键的过期时间到达时, 键就不再可用。(实际上,都是通过使用PEXPIZREAT命令来实现的)

命令 TTL 和 PTTL 则用于返回给定键距离过期还有多长时间:

redis> SETEX key 10086 value

OK

redis>TTL key

(integer) 10082

redis> PTTL key

(integer) 10068998

4.2、保存过期时间

redisDb结构中又一个字典,这个字典保存了数据库中所有键的过期时间, 也就是所谓的过期字典。

struct redisDb {

    dict *expires; // 过期字典,保存着键的过期时间

}


过期字典的键是一个指针,这个指针指向键空间的某个键对象。

过期字典的值是一个long long类型的整数,这个整数保存了键所指向的数据库键的过期时间。

注意:为了展示的方便, 图中重复出现了两次 number 键和 book 键。 在实际中, 键空间字典的键和过期时间字典的键都指向同一个字符串对象, 所以不会浪费任何空间

4.3、删除过期时间

    使用PERSIST命令删除一个键的过期时间。

    PERSIST命令和PEXPIREST是反操作的,  PERSIST命令在过期字典中查找给定的键,并解除键和值(过期时间)在过期字典的关联。  

4.4、过期键的判定

    两个步骤:

1、检查给定键是否存在于过期字典,如果存在,那么取得键的过期时间。

2、检查当前UNIX时间戳是否大于键的过期时间,如果是的话,那么键已经过期。否则的话,键未过期。


5、过期删除策略

    过期字典中的键如果过期了,什么时候会被删除呢?

    定时删除:设置键的过期时间的同时,创建一个定时器,让定时器在键的过期时间来临时,立即执行对键的删除操作。

    惰性删除:放任过期键不管,当真正使用的时候查看是否过期,如果过期删除,不过期返回该键。

    定期删除:每隔一段时间,程序就会对数据库进行一次检查,删除里面的过期键。至于删除多少,检查多少个数据库,由算法决定。

5.1、定时删除

    定时策略对内存是友好的。通过使用定时器,定时删除策略可以保证过期键尽快被删除。

    但是,定时删除策略的缺点很明显,他对CPU时间是很不友好的。因为在过期键比较多的情况下,删除过期键这一行为会占用很大一部分CPU时间,在内存不紧张但CPU时间非常紧张的情况下,将CPU时间用在删除和当前任务无关的过期键上,无疑会对服务器的相应时间和吞吐量造成影响。

    另外,创建一个定时器需要用到redis服务器中的时间事件,而当前时间事件的实现方式——无序链表,查找一个事件的时间复杂度为O(N),并不能高效地处理大量时间事件。

    因此,定时删除策略一般不实用。

5.2、惰性删除    

    惰性删除对CPU是最友好的,这个策略不会在删除其他无关的过期键上花费任何CPU事件。

    惰性删除的缺点是对内存是最不友好的:如果一个键过期了,而这个键不再使用,将会永远被保存在内存中。

    如果仅使用惰性删除策略,很有可能会造成内存泄漏,很多过期键永远没有被访问到。

5.3、定期删除

    定时删除太占用CPU时间,惰性删除浪费太多内存。定期删除策略算是一种折中。

    定期删除策略每隔一段时间执行一次删除过期键操作,并通过限制删除操作执行的时长和频率来减少删除操作对CPU时间的影响。

    定期删除策略的难点始载于如何确定删除操作的时长和频率。

    如果删除操作太频繁,或者执行的时间太长,定期删除策略就会退化成定时删除策略,CPU占用时间就会过多。

    如果删除操作执行的太少,或者执行的太短,就会和惰性删除没啥区别,出现浪费内存的情况。

5.4、redis的过期键删除策略

    redis采用了两种集合,惰性删除加上定期删除。

 5.4.1、redis惰性删除

    redis命令在执行之前都会调用expireIfNeeded函数对输入键进行检查,这个函数就相当于一个过滤器,过滤掉过期的输入键,从而避免命令接触过期键。

    


5.4.2、redis定期删除

    redis的定期删除策略由redis.c/activeExpireCycle函数实现,每当redis的服务器周期性操作redis.c/serverCron(配置文件中hz制定具体执行频率)函数执行时,activeExpireCycle就会执行。    

    它在规定的时间内,分多次遍历服务器的各个数据库,从数据库的expires字典中随机检查一部分键的过期时间,并删除其中的过期键。

    

    全局变量current_db会记录当前activeExpireCycle函数检查的进度,并在下一次activeExpireCycle函数调用时,接着上一次的进度继续执行。随着activeExpireCycle函数的不断执行,服务器的所有数据都会被检查一边,这时候activeExpireCycle会把current_db设置为0,重新开始。


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

6.1、生成RDB文件

    执行SAVE或者BGSAVE命令创建一个新的RDB文件的时候,已过期的键不会被保留到新创建的RDB文件中。

6.2、载入RDB文件

    如果服务器是主服务器,载入RDB的时候,会对键进行检查,过期键会被忽略。

    如果是从服务器,载入RDB的时候,文件所有的键都会被载入,不论是否过期。不过,因为主从服务器在数据进行同步的时候,从服务器的数据会被清空。所以,一般来讲,过期键载入从服务器也不会造成影响。    

6.3、AOF文件写入

    当服务器AOF持久化的时候,如果一个键已经过期,但是还没有被定期删除或者惰性删除,那么AOF文件不会因为这个过期键而产生任何影响。

    当过期键被删除之后,程序会向AOF文件追加append一条DEL命令,来显示地记录该键已经被删除。

6.4、AOF重写

    和生成RDB文件类似,在执行AOF重写的过程中,已经过期的键不易被保存到重写的AOF文件中。

6.5、复制

    当服务器带有从节点时, 过期键的删除由主节点统一控制:    

    1、如果服务器是主节点,那么它在删除一个过期键之后,会显式地向所有从节点发送一个 DEL 命令。

    2、如果服务器是从节点,那么当它碰到一个过期键的时候,不会执行删除操作,而是像处理为过期的键一样来处理过期键。只有收到主服务器发送过来的DEL命令之后,才会删除过期键。

    这样做的目的:可以保证主从数据库的一致性。也正是这样,当一个过期键仍然存在于主服务器的数据库时,这个过期键在从服务器的复制品也会继续存在。


7、数据库通知

    数据库通知是redis2.8之后新增加的功能,这个功能可以让客户端通过订阅给定的频道或者模式,来获知数据库中键的变化,以及数据库中命令的执行情况

    SUBSCRIBE  _ _keyspace@0_ _:message

    上述命令是针对message键执行的所有命令(包括后台的Expire、DEL命令),订阅键空间。

     SUBSCRIBE  _ _keyevent@0_ _:del

     上述命令是针对0号数据库中所有的del命令,订阅键事件。

      但是如果真的想让服务器可以订阅键空间和键事件,还需要配置服务器的notify-keyspace-events。这个选项决定了服务器所发送通知的类型。







    

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