zoukankan      html  css  js  c++  java
  • Redis的结构和运作机制

    1.数据库的结构

    Redis 中的每个数据库,都由一个 redis.h/redisDb 结构表示。

    typedef struct redisDb {
    // 保存着数据库以整数表示的号码
    int id;
    // 保存着数据库中的所有键值对数据
    // 这个属性也被称为键空间(key space)
    dict *dict;
    // 保存着键的过期信息
        dict *expires;
    // 实现列表阻塞原语,如 BLPOP,可用于列表
    dict *blocking_keys;
    dict *ready_keys;
    // 用于实现 WATCH 命令,可用于事务
    dict *watched_keys;
    } redisDb;
    

    Redis有id 、dict 和 expires 三个重要属性:

    • id 保存数据库的号码。Redis 服务器初始化时, 它会创建出 redis.h/REDIS_DEFAULT_DBNUM 个数据库, 并 将所有数据库保存到 redis.h/redisServer.db 数组中, 每个数据库的 id 为从 0 到 REDIS_DEFAULT_DBNUM - 1 的值。当执行 SELECT number 命令时,程序直接使用 redisServer.db[number] 来切换数据库。
    • dict 保存着数据库的所有键值对数据。Redis的结构可以看成字典的嵌套,类似json的数据结构。dict的内部依然是字典结构,dict的key是字符串对象,表示name,dict的值则是从string到sort-set中的任意一种对象。而删除数据库的健,实际上就是删除dict中对应的健对象和值对象。
    • expires也是一个字典,保存键的过期时间,注意只保存设置过的过期时间,如果没设置,则默认为永久。

    1.1 字典的底层实现

    hashtable

    冲突解决:链表

    扩容:渐进式hash,方法是复制出一个hash表,重算hash值(java8已不再重算)。重点是,扩容和收缩不是一次性完成,而是分多次完成。期间,字典的删改查操作可以在两个hashtable上进行,则增加操作只在新hashtable上进行。当字典中保存的数据很多事,可以避免扩容影响性能。

    2.过期键的检查和清除

    根据不同的清楚策略,通过expires 字典来检查键是否过期:

    • 检查键是否存在于 expires 字典:如果存在,那么取出键的过期时间。
    • 判断当前 UNIX 时间是否大于键的过期时间,如果是,那么键已经过期。

    过期键的清除有三种方式:定时删除、惰性删除和定期删除。

    2.1 定时删除

    创建一个定时事件,由事件处理 器自动执行键的删除操作。

    优点:对内存友好

    缺点:可能占用大量cpu时间

    2.2 惰性删除

    每次从dict字典取出键值时,检查是否过期,如果过期则删除,并返回空。

    优点:对cpu友好

    缺点:过期键占用内存

    核心是 db.c/expireIfNeeded 函数。在读取或写入数据库之前,调用 expireIfNeeded 对输入键进行检查。如果输入键已经过期的话,那么将键、键的值、键保存在 expires 字典中的过期时间都删除掉。

    2.3 定期删除

    是上面两个策略的结合。每隔一段时间,对 expires 字典进行检查,并执行惰性删除。

    核心是redis.c/activeExpireCycle,每当 Redis 的例行处理程序 serverCron 执行时,activeExpireCycle 都会被调用。这个函数在规定的时间限制内,尽可能地遍历各个数据库的 expires 字典,随机地检查一部分键的过期时间,并删除过期键。

    2.4 对RDB、AOF和复制的影响

    RDB:在创建新的 RDB 文件时,程序会对键进行检查,过期的键不会被写入到更新后的 RDB 文件 中。

    AOF:当过期键被惰性删除、或者定期删除后,程序会向 AOF 文件追加一条 DEL 命令,来显式地记录该键已被删除。

    复制:当服务器带有附属节点时,过期键的删除由主节点统一控制。主节点再删除过期键后,会会显式地向所有附属节点发送一 个 DEL 命令。附属节点只按DEL命令行动,当它自己碰到过期键时,只向主节点返回键已过期。

    3.持久化机制

    把数据由内存同步到磁盘,会Fork一个子进程来异步的完成。有三种方式,RDB、AOF和混合方式。

    3.1 RDB方式

    即快照,定期一次全量备份,将所有缓存进行序列化存到磁盘。

    优势:灾难恢复、性能好

    劣势:1、归档前断线,则这个归档周期的数据无法恢复。2、子进程工作,如果数据量大,可能影响性能。

    配置:

    1、修改redis.conf中的save时间:

    第一个save的表示每900秒,至少一个key发生变化,则归档一次。第二个save则表示每300秒,至少10个key变化,则归档。第三个同理,是为了应对短时间内的大量服务。

    2、也可以修改rdb文件的命名和保存路径:

    3.2 AOF方式

    以redis网络协议的格式记录对数据库进行的写命令。

    优势:append模式写日志,即使宕机,不会影响已记录的日志。

    劣势:同数量的数据集,AOF体量比RDB大,效率低。

    配置:
    在redis.conf中允许打开AOF模式,改为yes:

    配置AOF的同步方式,always表示每次修改都要追加日志:

    AOF的原理,两个核心函数:

    save():aof_buf -> aof文件

    write():aof文件 -> 磁盘

    共三种模式,第二种综合性性能较好。

    3.3 混合方式

    增大定期归档的时间跨度,归档间隔期,用AOF记录修改命令。

    4.事件

    文件事件和时间事件

    4.1 文件事件

    Redis使用socket进行client和server的通信,来完成实现高效的命令请求处理。采用非阻塞、多路复用IO模式。

    在多个客户端中实现多路复用,接受它们发来的命令请求,并将命令的执 行结果返回给客户端。

    Redis 将这类因为对套接字进行多路复用而产生的事件称为文件事件。文件事件分为读事件和写事件。

    读事件实现了命令请求的接收,生命周期与该客户端和服务器的连接状态相同。

    写事件实现了命令结果的返回。

    4.2 时间事件

    时间事件完成服务器的常规操作,分为单次执行事件和循环执行事件,服务器常规操作 serverCron 就是循环事件。

    其实现结构是无序链表,所以查询的时间复杂度为O(N)。

    文件事件和时间事件之间是合作关系:一种事件会等待另一种事件完成之后再执行,不会出现抢占情况。由于优先级的问题,时间事件的实际执行时间通常会比预定时间晚一些。

    5.参考

    《Redis设计和实现》黄健宏
    《Redis实战》

  • 相关阅读:
    福大软工 · BETA 版冲刺前准备(团队)
    福大软工 · 第十一次作业
    Alpha 冲刺 (9/10)
    Alpha 冲刺 (8/10)
    Alpha 冲刺 (7/10)
    Alpha 冲刺 (6/10)
    Alpha 冲刺 (5/10)
    Alpha 冲刺 (4/10)
    福大软工1816 · 团队现场编程实战(抽奖系统)
    阿里八八β阶段Scrum(5/5)
  • 原文地址:https://www.cnblogs.com/ChengzhiYang/p/12505859.html
Copyright © 2011-2022 走看看