redis随笔

一.字典

1.dictEntry:哈希表节点,保存key及其对应的value,并且每个dictEntry有一个next指针,当发生键冲突时,指向下一个dictEntry

例如:对于redis命令 lpush mylist a b c,redis使用一个dictEntry来保存,其key是mylist,value是a b c组成的链表

2.dictht: 哈希表

table是dictEntry**类型,它指向图中的dictEntry*[4],每个dictEntry指向一个dictEntry组成的链表

size一定是2的n次方

3.dict:字典

typedef struct dict {

    dictType *type;

    void *privdata;

    dictht ht[2];

    long rehashidx;

} dict; 

4.rehash

       当hash table的负载因子需要维持在一个合理的范围,否则就需要扩展或者收缩hash table,可通过rehash来执行。如果字典比较大的话,一次集中式rehash会使服务器一段时间停止服务(redis是单线程),所以需要使用多次渐进式rehash,这也是使用两个dictht的原因。

       

       渐进式rehash具体步骤如下:

            (1) 为ht[1]分配空间,并将rehashidx置0,表示正在rehash过程中

            (2) 在rehash期间,每次对字典的增删改查操作,首先要执行一步rehash,即将ht[0]中的dictEntry迁移到ht[1]中,然后再做各自的操作。

            (3) 当全部迁移完成后,将ht[0]空间释放,ht[0]与ht[1]交换指针,并令rehashidx为-1

       在这个过程中,rehashidx还起到了索引的作用

       在渐进式rehash过程中,会同时使用ht[0]和ht[1]两个hash table,所以对字典的增删改查需要在两个hash table上进行,例如查找,则先在ht[0]上查找,如果没有,再从ht[1]中查找。插入的话,则直接插入到ht[1]中

5.dictScan(src/dict.c +836),每次dictScan都遍历一个key下的所有元素,并返回下一个遍历的key的游标

     游标演变:

          v |= ~m0;

          v = rev(v);

          v++;

          v = rev(v);

 

       遍历顺序:

            table size为8时:         000------------->100------------>010------------>110------------->001------------->101------------>011------------>111

            table size为16时:0000→1000→0100→1100→0010→1010→0110→1110-->0001→1001→0101→1101→0011→1011→0111→1111  

            优点:哈希表扩展时不会重复遍历,缩小时重复遍历很少

       数字增长顺序:

            table size为8时:000-->001-->010-->011-->100-->101→110→111

            table size为16时:0000-->0001-->0010-->0011-->0100-->0101-->0110-->0111-->1000-->1001-->1010-->1011-->1100-->1101-->1110-->1111

            缺点:哈希表扩展时重复遍历较多,缩小时有些游标会被漏掉

二、数据库

db中每个元素的类型是redisDb,其定义如下:

struct redisDb {

    dict *dict;                    // 保存键值对的字典

    dict *expires;              // 过期字典,保存数据库中所有键的过期时间,key-dict中的某个键对象,value-long long类型,保存该键过期时间

过期键删除:

    1.惰性删除策略实现:expireIfNeeded,所有的读写数据库的Redis命令在执行前都会调用。在该函数执行时,如果该键已经过期,则删除;如果没有过期,则该函数不做任何操作

    2.定期删除策略实现:activeExpireCycle。每当Redis的周期操作函数ServerCron函数执行时,便调用一遍activeExpireCycle函数。该函数每次运行时,都选取部分数据库中的部分键进行检查,删除过期键。 

    activeExpireCycle代码(src/expire.c +97):

          每次操作dbs_per_call个db,对每个db,分多次去操作,每次操作ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP个key,当其中一次删除的键数量少于ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP / 4时停止,同时有一个该函数总共运行时间,如果超过1ms直接退出

三、订阅PUB/SUB功能

1.每个redisServer内有一个dict(pubsub_channels),保存该redis server上的所有channel以及监听该channel的所有client(channel为key,client list为value)

2.每个redisServer内有一个list(pubsub_patterns),保存该server使用模式订阅的所有channel及其client 

3.发送消息

    例:PUBLISH channel msg

    流程:

         1> 在dict pubsub_channels中找到key为channel的client链表,逐个发送msg

         2> 遍历list pubsub_patterns,找到所有与channel匹配的pubsubPattern,向其中的client发送msg

 

 
 
posted @ 2018-06-26 16:11  levy5307  阅读(78)  评论(0编辑  收藏  举报