Redisの研究ノート(チャプター6):データベース

Redisのは、NoSQLのは、ここで使用するC言語のデータベースに書き込まれ、これはRedisのデータベースが格納されている方法を説明しますか?同様の操作と関連するデータベースの一部として。

次のようにすべてのデータベースのDB配列はRedisのredis.h / redisServer構造に格納されます。

struct redisServer {
    ......

    // 数据库
    redisDb *db;

    ......
}

Redisのデフォルトは、それぞれ互いに独立して、16個のデータベースを作成します。

データベースの切り替え

各Redisのクライアントは、独自のターゲット・データベースを持ち、デフォルトでは、ターゲット・データベースRedisのクライアントデータベースは0です。しかし、クライアントは、selectコマンドによってターゲット・データベースを切り替えることができます。

次のようにサーバーの内部では、DBクライアントのステータスは、記録電流ターゲット・クライアント・データベースのredisClient構造属性:

typedef struct redisClient {

    // 套接字描述符
    int fd;

    // 当前正在使用的数据库
    redisDb *db;

    // 当前正在使用的数据库的 id (号码)
    int dictid;

    // 客户端的名字
    robj *name;             /* As set by CLIENT SETNAME */

} redisClient;

第1のデータベースを対象とするクライアントのデータベースには、クライアントおよび対応するサーバ、クライアントの状態ステータスの関係は次のようにした場合(第9章「のRedisの第二版の設計と実装」から:データベース):

「の第二版のRedisの設計と実装」

注:これまでのところ、Redisのは、まだクライアントコマンドターゲット・データベース端に戻ったので、混乱を避けるために、プロジェクト内の複数のデータベースを使用しないようにしようとしません。

データベースの鍵空間

次のようにRedisのは、データベース・サーバ、データベース・サーバへの鍵であり、具体的な構造によって表さredis.h / redisDb構造によってそれぞれです。

typedef struct redisDb {

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

    // 键的过期时间,字典的键为键,字典的值为过期事件 UNIX 时间戳
    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 */

    // 正在被 WATCH 命令监视的键
    dict *watched_keys;         /* WATCHED keys for MULTI/EXEC CAS */

    struct evictionPoolEntry *eviction_pool;    /* Eviction pool of keys */

    // 数据库号码
    int id;                     /* Database ID */

    // 数据库的键的平均 TTL ,统计信息
    long long avg_ttl;          /* Average TTL, just for stats */

} redisDb;

スペースキー(DBプロパティ)とユーザデータベースに見えるが直接対応しています。

  • キーは、キーのスペースキーデータベースであり、各キーは、文字列オブジェクトです。
  • すなわち、鍵空間データベース値の値が、それぞれの値は、文字列、リスト、ハッシュテーブルオブジェクト、およびRedisのオブジェクトは、オブジェクトのセット注文したオブジェクトのコレクションのいずれかになります。

そして、追加、変更するには、データベースに、DBの辞書は、キーも動作している削除します。

有効期限ポリシーキー

4つの書面で設定Redisのキー有効期限:

  • キーT1の有効期限:キーの有効期間のT1秒にキーを示しています。
  • キーT1 pexpire:キーの有効期間の設定キーをt1ミリ秒であることを示しています。
  • キーT1をexpireat:秒のタイムスタンプ指定された番号へのキーの有効期限の時間t1にキーを示しています。
  • キーT1 pexpireat:ミリ秒単位のタイムスタンプ指定された番号へのキーの有効期限の時間t1にキーを示しています。

そこに四つの異なる言い回しですが、彼らが行うが、それは、統一されたアプローチに圧送することができ、一つのことです。実際にはRedisのも、ちょうどその、期限切れpexpireをした三つのコマンドがpexpireatコマンドを使用して実装されexpireat。

それのRedisのキーの有効期限を保存する方法は?

すべてのキーのデータベースの有効期限に保存されている辞書redisDb構造を満了し、我々は辞書を期限切れにこの辞書を呼び出します。

  • 期限切れ辞書キーは、キーオブジェクトのキー・スペースを指すポインタ(すなわち、データベース・キー)です。
  • 期限切れの辞書は、長い長い整数値型、キー(ミリ秒精度UNIXタイムスタンプ)にキーポイントの整数格納されたデータベースの有効期限です。

それの有効期限をどのようにRedisのキーを削除するには?

コマンドは次のとおりです。キーを持続します

操作を行うためのRedisデータベースは、対応する辞書がペアで期限が切れる削除することだけです。

どのようにRedisのキーを判断するためには、それが期限切れになりましたか?

辞書の有効期限が切れたことで、プログラムは、与えられたキーは、次の手順で有効期限が切れているかどうかを確認することができます。

  1. 指定されたキーが辞書に存在するかどうかの期限が切れて:がある場合、キーの有効期限を取得します。
  2. 現在のUNIXタイムキルキー有効期限よりも大きいかどうかを確認しますので、キーの有効期限が切れている場合は、そうでない場合、キーの有効期限が切れていません。

Redisのは、正確にどのように期限切れのキーを削除するには?

我々は、彼らが、キーが残っている問題は、有効期限に基づいており、期限が切れているかどうかを判断する方法を知って、データベースキーが辞書に格納されている有効期限が満了していることを知っている:キーの有効期限が切れている場合、それはときにそれを削除されますか?

この質問は、3つの異なる削除ポリシーを表す3つの可能な答えを持っています:

  • 同時に設定したキーの有効期限を、タイマー(タイマー)を作成します。定期的に削除します。タイマー満了するキーの時間はすぐにキー側の実装に加えて、来てみましょう。
  • 不活性削除:有効期限が切れた場合放任キーは関係なく、期限が切れるが、それぞれの時間は、鍵空間からキーを取得するには、キーの側面に加えて、それから、キーの有効期限が切れたかどうかを確認するために作られています。有効期限が切れていない場合は、キーを返します。
  • 定期的に削除された:時々、プログラムが内部の期限切れのキーを削除し、一度データベースをチェック。あなたが期限切れのキーを削除するだけでなく、データベースの数、アルゴリズムの決定を確認したいどのくらいのためとして。

これら3つの戦略では、第一及びアクティブ削除ポリシー第3、及び第二は、受動側ポリシーに加えました。かかわらず、戦略のどのような種類の長所と短所があります。

定期的にそれを削除するために:

  • 利点は、あなたが期限切れの鍵を保証することができ、できるだけ早く削除され、メモリが期限切れのキー占有リリースです。
  • 欠点は、それは耐え難いある、キー多くの時間、CPU時間が増加します場合は特に、CPU時間の一部を占有することになるです。

不活性のためにそれを削除します。

  • 唯一のプログラムキーを取り出すことは確認され、その利点は、ほとんどのCPU時間ではありません。
  • 欠点は、このようなキーは、この時間はメモリリークで訪れることはありません満了したときのようにメモリリークを引き起こす可能性があります。

定期的にそれを削除するためには、定期的に時代遅れのポリシーの施行は、随時キー操作を削除し、削除、および実行さ削除操作の継続時間や頻度を制限することにより、CPU時間への影響を軽減するための操作を削除し、これら2つの戦略の統合です;加えて、定期的に期限切れのキーを削除することで、定期的に有効期限が切れているため、キーを効果的に低減したポリシーを削除し、無駄なメモリをもたらしました。
定期的に政策課題を削除すると、削除操作や周波数を実行する時間の長さを決定することです。

  • あなたも実行に時間があまりにも頻繁に実行または削除する場合は、定期的に、ポリシーは、ポリシーを削除するタイミングに退化します削除するように上記で過剰期限切れのキー操作反対側のCPU時間。
  • 削除操作は、メモリの廃棄物の出現の場合のように、戦略と不活性が削除されたポリシーを削除し、定期的に実行するには、あまりにも少しの時間や短すぎるを実行する場合。

したがって、ポリシー側に加えて、定期的にあれば、サーバは状況、妥当な長削除操作のセットと、実行頻度の下でなければなりません。

そして、Redisの中でもCPU上で、定期的に不活性な2つの戦略を削除し、削除するために使用され、メモリがバランスを実現しています。

不活性削除

不活性がdb.c / expireIfNeeded機能によって実装キー削除ポリシーを有効期限が切れ、すべての読み取りおよび書き込みコマンドのRedisのデータベースをチェックするために、Enterキーを実行する前に、expireIfNeeded関数を呼び出されます。

  • 入力キーの有効期限が切れている場合は、入力キーの機能は、データベースから削除されexpireIfNeeded。
  • 入力キーの有効期限が切れていない場合、関数は、アクションをexpireIfNeededありません。

それは実際にコマンドキーの有効期限が切れるとの接触を避けるために、期限切れの入力キーをフィルタリングするためのコマンドを実行する前に、フィルタのような機能をexpireIfNeeded。特定の機能コードは次のとおりです。

/*
* 检查 key 是否已经过期,如果是的话,将它从数据库中删除。
*
* 返回 0 表示键没有过期时间,或者键未过期。
*
* 返回 1 表示键已经因为过期而被删除了。
*/
int expireIfNeeded(redisDb *db, robj *key) {

    // 取出键的过期时间
    mstime_t when = getExpire(db,key);
    mstime_t now;

    // 没有过期时间
    if (when < 0) return 0; /* No expire for this key */

    // 如果服务器正在进行载入,那么不进行任何过期检查
    if (server.loading) return 0;

    /* If we are in the context of a Lua script, we claim that time is
    * blocked to when the Lua script started. This way a key can expire
    * only the first time it is accessed and not in the middle of the
    * script execution, making propagation to slaves / AOF consistent.
    * See issue #1525 on Github for more information. */
    now = server.lua_caller ? server.lua_time_start : mstime();

    /* If we are running in the context of a slave, return ASAP:
    * the slave key expiration is controlled by the master that will
    * send us synthesized DEL operations for expired keys.
    *
    * Still we try to return the right information to the caller, 
    * that is, 0 if we think the key should be still valid, 1 if
    * we think the key is expired at this time. */
    // 当服务器运行在 replication 模式时
    // 附属节点并不主动删除 key
    // 它只返回一个逻辑上正确的返回值
    // 真正的删除操作要等待主节点发来删除命令时才执行
    // 从而保证数据的同步
    if (server.masterhost != NULL) return now > when;

    // 运行到这里,表示键带有过期时间,并且服务器为主节点

    /* Return when this key has not expired */
    // 如果未过期,返回 0
    if (now <= when) return 0;

    /* Delete the key */
    server.stat_expiredkeys++;

    // 向 AOF 文件和附属节点传播过期信息
    propagateExpire(db,key);

    // 发送事件通知
    notifyKeyspaceEvent(REDIS_NOTIFY_EXPIRED,
        "expired",key,db->id);

    // 将过期键从数据库中删除
    return dbDelete(db,key);
}

コールは、期限切れのキープロセスと実行がgetコマンドを削除するには、コマンドをexpireIfNeeded(第9章「のRedisの第二版の設計と実装」から:データベース)は次のとおりです。

「の第二版のRedisの設計と実装」

定期的に削除します

期限切れのキーは定期的にredis.c / activeExpireCycle機能により実現したポリシーを削除し、コールフローがあるserverCron() - > databasesCron() - > activeExpireCycle()。コアコード次のように(コードで撮影コア部の便宜上参照)。

int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
    ......

    // 对数据库执行各种操作
    databasesCron();

    ......
}

// 对数据库执行删除过期键,调整大小,以及主动和渐进式 rehash
void databasesCron(void) {

    // 函数先从数据库中删除过期键,然后再对数据库的大小进行修改

    /* Expire keys by random sampling. Not required for slaves
    * as master will synthesize DELs for us. */
    // 如果服务器不是从服务器,那么执行主动过期键清除
    if (server.active_expire_enabled && server.masterhost == NULL)
        // 清除模式为 CYCLE_SLOW ,这个模式会尽量多清除过期键
        activeExpireCycle(ACTIVE_EXPIRE_CYCLE_SLOW);

    /* Perform hash tables rehashing if needed, but only if there are no
    * other processes saving the DB on disk. Otherwise rehashing is bad
    * as will cause a lot of copy-on-write of memory pages. */
    // 在没有 BGSAVE 或者 BGREWRITEAOF 执行时,对哈希表进行 rehash
    if (server.rdb_child_pid == -1 && server.aof_child_pid == -1) {

        ......
        
    }
}
void activeExpireCycle(int type) {

    ......

    // 遍历数据库
    for (j = 0; j < dbs_per_call; j++) {
        int expired;
        // 指向要处理的数据库
        redisDb *db = server.db+(current_db % server.dbnum);

        // 为 DB 计数器加一,如果进入 do 循环之后因为超时而跳出
        // 那么下次会直接从下个 DB 开始处理
        current_db++;

        do {
            unsigned long num, slots;
            long long now, ttl_sum;
            int ttl_samples;

            // 获取数据库中带过期时间的键的数量
            // 如果该数量为 0 ,直接跳过这个数据库
            if ((num = dictSize(db->expires)) == 0) {
                db->avg_ttl = 0;
                break;
            }
            // 获取数据库中键值对的数量
            slots = dictSlots(db->expires);
            // 当前时间
            now = mstime();

            // 这个数据库的使用率低于 1% ,扫描起来太费力了(大部分都会 MISS)
            // 跳过,等待字典收缩程序运行
            if (num && slots > DICT_HT_INITIAL_SIZE &&
                (num*100/slots < 1)) break;

            /*  
            * 样本计数器
            */
            // 已处理过期键计数器
            expired = 0;
            // 键的总 TTL 计数器
            ttl_sum = 0;
            // 总共处理的键计数器
            ttl_samples = 0;

            // 每次最多只能检查 LOOKUPS_PER_LOOP 个键
            if (num > ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP)
                num = ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP;

            // 开始遍历数据库
            while (num--) {
                dictEntry *de;
                long long ttl;

                // 从 expires 中随机取出一个带过期时间的键
                if ((de = dictGetRandomKey(db->expires)) == NULL) break;
                // 计算 TTL
                ttl = dictGetSignedIntegerVal(de)-now;
                // 如果键已经过期,那么删除它,并将 expired 计数器增一
                if (activeExpireCycleTryExpire(db,de,now)) expired++;
                if (ttl < 0) ttl = 0;
                // 累积键的 TTL
                ttl_sum += ttl;
                // 累积处理键的个数
                ttl_samples++;
            }

            ......

            // 已经超时了,返回
            if (timelimit_exit) return;

            // 如果已删除的过期键占当前总数据库带过期时间的键数量的 25 %
            // 那么不再遍历
        } while (expired > ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP/4);
    }
}

いくつかの説明:

  1. serverCron()関数は、Redisのタイマー、デフォルトの実行ごとに100ミリ秒です。
  2. databasesCron()関数では、期限切れのキーはまた、焼き直し操作を行った削除します。
  3. サーバは、サーバからの有効期限が切れクリアするためのイニシアチブの唯一の実装ではない場合。
  4. ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOPのredis.hは20で定義されています。それは20の有効期限が切れてキーを削除します。あなたが設定された時間を超えない場合は、各バンクは、複数回を削除することができます。

おすすめ

転載: www.cnblogs.com/wind-snow/p/11249489.html
おすすめ