欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Redis過期時間的設(shè)計與實現(xiàn)代碼

 更新時間:2024年08月25日 13:44:33   作者:逸風尊者  
在?Redis?中,鍵的過期時間設(shè)計與實現(xiàn)是一個重要的功能,這使得?Redis?可以自動刪除在指定時間后不再需要的鍵,下面詳細介紹?Redis?過期時間的設(shè)計和實現(xiàn),包括設(shè)置過期時間、過期鍵的存儲結(jié)構(gòu)、過期鍵的刪除策略等,需要的朋友可以參考下

1. 設(shè)置過期時間

Redis 提供了多個命令來設(shè)置鍵的過期時間,如 EXPIRE、PEXPIRE、EXPIREATPEXPIREAT。這些命令可以以秒或毫秒為單位設(shè)置鍵的過期時間,也可以設(shè)置具體的過期時間點。

  • EXPIRE key seconds
  • PEXPIRE key milliseconds
  • EXPIREAT key timestamp
  • PEXPIREAT key milliseconds-timestamp

示例:

void expireCommand(client *c) {
    long long seconds;
    if (getLongLongFromObjectOrReply(c, c->argv[2], &seconds, NULL) != C_OK)
        return;
    setExpire(c, c->db, c->argv[1], mstime() + seconds*1000);
    addReply(c, shared.cone);
}

2. 過期鍵的存儲結(jié)構(gòu)

每個 Redis 數(shù)據(jù)庫實例(redisDb)中都有一個名為 expires 的字典,用于存儲鍵的過期時間。這個字典將鍵指針映射到以毫秒為單位的到期時間點。

typedef struct redisDb {
    dict *dict;                // 主字典,存儲所有鍵值對
    dict *expires;             // 過期字典,存儲鍵的過期時間
    ...
} redisDb;

3. 設(shè)置過期時間

通過 setExpire 函數(shù)設(shè)置鍵的過期時間。如果鍵已經(jīng)存在于 expires 字典中,則更新其過期時間;否則,將其添加到 expires 字典中。

void setExpire(client *c, redisDb *db, robj *key, long long when) {
    dictEntry *de = dictFind(db->dict, key->ptr);
    if (de == NULL) return;

    /* Set the new expire time */
    if (dictAdd(db->expires, dictGetKey(de), (void*)when) == DICT_ERR) {
        dictReplace(db->expires, dictGetKey(de), (void*)when);
    }
}

4. 刪除過期鍵的策略

Redis 采用了以下三種策略來刪除過期鍵:

  • 惰性刪除(Lazy Deletion) :每次訪問鍵時檢查其是否過期,如果已過期則刪除。這樣只在訪問鍵時才進行過期檢查,節(jié)省了資源。
robj *lookupKeyRead(redisDb *db, robj *key) {
    robj *val;
    expireIfNeeded(db,key);  // 檢查并刪除過期鍵
    val = lookupKey(db,key,LOOKUP_NONE);
    return val ? val : NULL;
}
  • 定期刪除(Periodic Deletion) :Redis 會周期性地隨機抽取一定數(shù)量的鍵進行過期檢查,并刪除其中已過期的鍵。這一過程由后臺任務(wù)定期執(zhí)行,確保盡可能多的過期鍵被及時刪除。
int activeExpireCycle(int type) {
    unsigned int current_db = server.dbnum;
    long long start = ustime();
    long long timelimit = 1000000; // 1秒
    int dbs_per_call = CRON_DBS_PER_CALL;

    current_db = server.current_db;
    while(dbs_per_call--) {
        redisDb *db = server.db + (current_db % server.dbnum);
        activeExpireCycleTryExpire(db, cycle_tickets);
        current_db++;
    }

    long long elapsed = ustime()-start;
    return elapsed > timelimit;
}
  • 主動刪除(Active Expiration) :在內(nèi)存使用接近最大限制時,會觸發(fā)主動刪除策略,通過掃描所有庫的鍵刪除過期數(shù)據(jù),以確保內(nèi)存使用量保持在設(shè)定范圍內(nèi)。
void evictExpiredKeys() {
    for (int j = 0; j < server.dbnum; j++) {
        redisDb *db = server.db+j;
        scanDatabaseForExpiredKeys(db);
    }
}
  • Redis 默認采用以下兩種刪除過期鍵策略:

    惰性刪除(Lazy Deletion) :每次訪問某個鍵時檢查其是否過期,如果過期則刪除。

    定期刪除(Periodic Deletion) :后臺任務(wù)定期掃描數(shù)據(jù)庫中的鍵,隨機抽取部分鍵進行過期檢查并刪除其中已過期的鍵。

5. 檢查并刪除過期鍵

expireIfNeeded 函數(shù)用于檢查某個鍵是否過期,如果過期則刪除該鍵。

int expireIfNeeded(redisDb *db, robj *key) {
    mstime_t when = getExpire(db, key);
    if (when < 0) return 0;

    if (mstime() > when) {
        server.stat_expiredkeys++;
        propagateExpire(db,key);
        dbDelete(db,key);
        return 1;
    } else {
        return 0;
    }
}
  • getExpire:從 expires 字典中獲取鍵的過期時間。
  • mstime:返回當前的毫秒時間戳。
  • 如果鍵已過期,則調(diào)用 dbDelete 刪除該鍵,并增加統(tǒng)計計數(shù)器 stat_expiredkeys。

6. 獲取過期時間

getExpire 函數(shù)用于獲取鍵的過期時間,如果鍵沒有設(shè)置過期時間則返回 -1。

mstime_t getExpire(redisDb *db, robj *key) {
    dictEntry *de;
    if (dictSize(db->expires) == 0 ||
        (de = dictFind(db->expires, key->ptr)) == NULL) return -1;

    return (mstime_t)dictGetSignedIntegerVal(de);
}

總結(jié)

Redis 的過期時間設(shè)計與實現(xiàn)包括以下幾個關(guān)鍵點:

  • 設(shè)置過期時間:通過 EXPIRE、PEXPIRE 等命令設(shè)置鍵的過期時間,并將過期時間存儲在 expires 字典中。

  • 過期字典:每個數(shù)據(jù)庫實例都有一個 expires 字典,用于存儲鍵的過期時間。

  • 刪除策略

    • 惰性刪除:每次訪問鍵時檢查其是否過期,如果已過期則刪除。
    • 定期刪除:通過后臺任務(wù)周期性地檢測并刪除過期鍵。
    • 主動刪除:在內(nèi)存使用接近最大限制時觸發(fā),掃描所有鍵并刪除過期鍵。

定期刪除activeExpireCycle函數(shù)詳細解析

void activeExpireCycle(int type) {
    static unsigned int current_db = 0;  // 記錄上一次處理的數(shù)據(jù)庫索引
    static int timelimit_exit = 0;       // 用于指示是否超出時間限制
    unsigned int j;
    // 每次要處理的數(shù)據(jù)庫數(shù)量
    unsigned int dbs_per_call = CRON_DBS_PER_CALL;
    long long start = ustime();          // 開始時間
    long long timelimit;                 // 時間限制

    if (type == ACTIVE_EXPIRE_CYCLE_FAST) {
        /* Fast cycle: 1 ms */
        timelimit = 1000;
    } else {
        /* Slow cycle: 25% CPU time p. DB / Configurable percentage. */
        timelimit = server.hz < 100 ? 1000 : 10;
        if (server.active_expire_effort != 1)
            timelimit *= server.active_expire_effort-1;
        timelimit /= server.dbnum;
        timelimit_exit = 0;
    }

    for (j = 0; j < dbs_per_call; j++) {
        redisDb *db = server.db + (current_db % server.dbnum);
        current_db++;
        int expired, sampled;

        do {
            long now = mstime();
            expireEntry *de;
            dictEntry *d;

            /* Sample a few keys in the database */
            expired = 0;
            sampled = 0;
            while ((de = dictGetRandomKey(db->expires)) != NULL &&
                   mstime() - now < timelimit) {
                long long ttl = dictGetSignedIntegerVal(de) - mstime();
                if (ttl < 0) {
                    d = dictFind(db->dict, dictGetKey(de));
                    dbDelete(db, dictGetKey(d));
                    server.stat_expiredkeys++;
                    expired++;
                }
                sampled++;
            }
        } while (expired > ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP / 2);

        elapsed = ustime() - start;
        if (elapsed > timelimit) {
            timelimit_exit = 1;
            break;
        }
    }
}

關(guān)鍵步驟解析

1. 初始化變量

static unsigned int current_db = 0;
static int timelimit_exit = 0;
unsigned int j;
unsigned int dbs_per_call = CRON_DBS_PER_CALL;
long long start = ustime();
long long timelimit;
  • current_db:靜態(tài)變量,用于記錄上一次處理的數(shù)據(jù)庫索引。
  • timelimit_exit:用于指示是否耗盡了時間配額,防止無限循環(huán)。
  • dbs_per_call:每次掃描的數(shù)據(jù)庫數(shù)量,通常由配置決定。
  • start:記錄開始執(zhí)行此函數(shù)的時間戳。
  • timelimit:本次調(diào)用允許消耗的最大時間(以微秒為單位)。

2. 確定時間限制

if (type == ACTIVE_EXPIRE_CYCLE_FAST) {
    timelimit = 1000;  // 快速模式:1 毫秒
} else {
    timelimit = server.hz < 100 ? 1000 : 10;
    if (server.active_expire_effort != 1)
        timelimit *= server.active_expire_effort - 1;
    timelimit /= server.dbnum;
    timelimit_exit = 0;
}
  • 如果是快速模式,時間限制為 1 毫秒。
  • 如果是慢速模式,時間限制根據(jù) Redis 配置和當前服務(wù)器負載情況計算。
  • server.active_expire_effort 參數(shù)可以調(diào)整過期鍵清理的力度。

3. 遍歷數(shù)據(jù)庫

每次調(diào)用 activeExpireCycle 時,會遍歷一定數(shù)量的數(shù)據(jù)庫,并在每個數(shù)據(jù)庫中隨機抽取鍵進行過期檢查和刪除。

for (j = 0; j < dbs_per_call; j++) {
    redisDb *db = server.db + (current_db % server.dbnum);
    current_db++;
    int expired, sampled;

    do {
        long now = mstime();
        expireEntry *de;
        dictEntry *d;

        expired = 0;
        sampled = 0;
        while ((de = dictGetRandomKey(db->expires)) != NULL &&
               mstime() - now < timelimit) {
            long long ttl = dictGetSignedIntegerVal(de) - mstime();
            if (ttl < 0) {
                d = dictFind(db->dict, dictGetKey(de));
                dbDelete(db, dictGetKey(d));
                server.stat_expiredkeys++;
                expired++;
            }
            sampled++;
        }
    } while (expired > ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP / 2);

    elapsed = ustime() - start;
    if (elapsed > timelimit) {
        timelimit_exit = 1;
        break;
    }
}

關(guān)鍵點解析:

選擇數(shù)據(jù)庫

redisDb *db = server.db + (current_db % server.dbnum);
current_db++;

使用循環(huán)方式選擇下一個要檢查的數(shù)據(jù)庫實例。current_db 記錄上一次處理的數(shù)據(jù)庫索引,通過取模操作確保索引在有效范圍內(nèi)。

初始化變量

int expired, sampled;
expired = 0;
sampled = 0;

過期檢查循環(huán)

while ((de = dictGetRandomKey(db->expires)) != NULL && mstime() - now < timelimit) {
    long long ttl = dictGetSignedIntegerVal(de) - mstime();
    if (ttl < 0) {
        d = dictFind(db->dict, dictGetKey(de));
        dbDelete(db, dictGetKey(d));
        server.stat_expiredkeys++;
        expired++;
    }
    sampled++;
}
    • 從 expires 字典中隨機獲取一個鍵 de。
    • 檢查當前時間是否超過了本次周期的時間限制 timelimit。
    • 如果鍵已經(jīng)過期(ttl < 0),則刪除該鍵,并增加已過期鍵的計數(shù)器 expired。
    • 增加已檢查鍵的計數(shù)器 sampled

多輪過期檢查

do {
    ...
} while (expired > ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP / 2);
  • 如果在一輪檢查中刪除的過期鍵數(shù)量超過預設(shè)值的一半,則繼續(xù)下一輪檢查。

4. 時間限制檢查

在每次處理完一個數(shù)據(jù)庫后,檢查是否超出時間限制:

elapsed = ustime() - start;
if (elapsed > timelimit) {
    timelimit_exit = 1;
    break;
}
  • 計算已耗費的時間 elapsed。
  • 如果已耗費時間超過 timelimit,設(shè)置 timelimit_exit 為 1 并跳出循環(huán)。

總結(jié)

Redis 的 activeExpireCycle 函數(shù)通過以下步驟實現(xiàn)定期刪除過期鍵:

  • 初始化變量并確定時間限制:根據(jù)當前的模式(快速或慢速)和配置參數(shù),計算本次函數(shù)調(diào)用的時間限制。
  • 遍歷數(shù)據(jù)庫:循環(huán)遍歷一定數(shù)量的數(shù)據(jù)庫。
  • 過期檢查與刪除:從每個數(shù)據(jù)庫中隨機抽取鍵,檢查其是否過期并進行刪除,直到達到時間限制或刪除了一定數(shù)量的過期鍵。
  • 時間限制檢查:確保函數(shù)不會超出規(guī)定的時間配額,以避免影響 Redis 的其他操作。

AOF、RDB和復制功能對過期鍵的處理

在 Redis 中,AOF(Append Only File)、RDB(Redis DataBase)和復制(Replication)功能對過期鍵的處理方式有所不同。下面詳細介紹這些機制如何處理過期鍵:

AOF 持久化

  • 記錄過期時間

    • 在 AOF 文件中,除了寫入每個鍵值的設(shè)置操作外,還會寫入 EXPIRE 或 PEXPIRE 命令來記錄鍵的過期時間。
  • 重寫(Rewrite)過程

    • 當 AOF 文件需要重寫時,Redis 會檢查每個鍵,如果鍵已過期,則不會將其寫入新的 AOF 文件。
  • 加載 AOF 文件

    • 當 Redis 重啟并加載 AOF 文件時,會執(zhí)行文件中的所有命令,包括設(shè)置鍵值和設(shè)置過期時間的命令。如果某些鍵已經(jīng)過期,這些鍵會立即被刪除。

RDB 持久化

  • 保存快照

    • 當 Redis 創(chuàng)建 RDB 快照時,它會將所有鍵及其剩余的過期時間一起保存到快照文件中。
  • 加載快照

    • 當 Redis 從 RDB 文件恢復數(shù)據(jù)時,會載入所有鍵值對,同時載入它們的過期時間。如果某個鍵在載入時已經(jīng)過期,Redis 會立即將其刪除。

復制(Replication)

  • 主從同步

    • 在主從復制架構(gòu)中,主節(jié)點會將過期鍵的刪除操作傳播給從節(jié)點。
    • 如果一個鍵在主節(jié)點上過期并被刪除,主節(jié)點會向從節(jié)點發(fā)送 DEL 操作,從而在從節(jié)點上也刪除該鍵。
  • 延遲過期

    • 從節(jié)點可能因為網(wǎng)絡(luò)延遲等原因,對過期鍵的處理會稍有滯后,但最終主從節(jié)點的數(shù)據(jù)將保持一致。

總結(jié)

  • AOF:

    • 通過記錄 EXPIRE/PEXPIRE 命令來處理過期時間。
    • 在重寫過程中跳過已過期的鍵。
    • 加載時刪除已過期的鍵。
  • RDB:

    • 將過期時間與鍵值一起保存。
    • 加載時立即刪除已過期的鍵。
  • 復制:

    • 主節(jié)點將刪除過期鍵的操作同步到從節(jié)點。
    • 確保主從節(jié)點數(shù)據(jù)的一致性。

以上就是Redis過期時間的設(shè)計與實現(xiàn)代碼的詳細內(nèi)容,更多關(guān)于Redis過期時間的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Redis分布式可重入鎖實現(xiàn)方案

    Redis分布式可重入鎖實現(xiàn)方案

    在單進程環(huán)境下,要保證一個代碼塊的同步執(zhí)行,直接用synchronized 關(guān)鍵字或ReetrantLock 即可,在分布式環(huán)境下,要保證多個節(jié)點的線程對代碼塊的同步訪問,就必須要用到分布式鎖方案,本文介紹一下基于 Redis實現(xiàn)的分布式鎖方案,感興趣的朋友一起看看吧
    2024-02-02
  • Redis數(shù)據(jù)庫安裝部署及基本操作詳解

    Redis數(shù)據(jù)庫安裝部署及基本操作詳解

    這篇文章主要介紹了Redis數(shù)據(jù)庫安裝部署及基本操作,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-08-08
  • 詳解如何發(fā)現(xiàn)并解決Redis熱點Key問題

    詳解如何發(fā)現(xiàn)并解決Redis熱點Key問題

    Redis 熱點 Key 是指在某一時間段內(nèi),被大量的讀寫操作命中的 Key,這種情況可能會導致性能瓶頸,數(shù)據(jù)一致性問題,緩存擊穿等問題,所以本文給大家介紹了如何發(fā)現(xiàn)并解決Redis熱點Key問題,需要的朋友可以參考下
    2024-05-05
  • Redis中有序集合的內(nèi)部實現(xiàn)方式的詳細介紹

    Redis中有序集合的內(nèi)部實現(xiàn)方式的詳細介紹

    本文主要介紹了Redis中有序集合的內(nèi)部實現(xiàn)方式的詳細介紹,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • Python Redis如何執(zhí)行Lua腳本

    Python Redis如何執(zhí)行Lua腳本

    這篇文章主要介紹了Python Redis如何執(zhí)行Lua腳本問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-02-02
  • Redis實現(xiàn)多人多聊天室功能

    Redis實現(xiàn)多人多聊天室功能

    這篇文章主要為大家詳細介紹了Redis實現(xiàn)多人多聊天室功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-11-11
  • Redis教程(十二):服務(wù)器管理命令總結(jié)

    Redis教程(十二):服務(wù)器管理命令總結(jié)

    這篇文章主要介紹了Redis教程(十二):服務(wù)器管理命令總結(jié),本文講解了CONFIGGETparameter、CONFIG SETparameter value、FLUSHALL等命令,需要的朋友可以參考下
    2015-04-04
  • Redis的Spring客戶端使用小結(jié)

    Redis的Spring客戶端使用小結(jié)

    在Spring中使用Redis,可以極大地提升應(yīng)用程序的性能和響應(yīng)速度,本文主要介紹了Redis的Spring客戶端使用小結(jié),具有一定的參考價值,感興趣的可以了解一下
    2025-04-04
  • redis主從復制的原理及實現(xiàn)

    redis主從復制的原理及實現(xiàn)

    Redis主從復制是一種數(shù)據(jù)同步機制,它通過將一個Redis實例的數(shù)據(jù)復制到其他Redis,本文主要介紹了redis主從復制的原理及實現(xiàn),具有一定的參考價值,感興趣的可以了解一下
    2023-08-08
  • 聊一聊Redis與MySQL雙寫一致性如何保證

    聊一聊Redis與MySQL雙寫一致性如何保證

    一致性就是數(shù)據(jù)保持一致,在分布式系統(tǒng)中,可以理解為多個節(jié)點中數(shù)據(jù)的值是一致的。本文給大家分享Redis與MySQL雙寫一致性該如何保證,感興趣的朋友一起看看吧
    2021-06-06

最新評論