redis學習筆記——expire、pexpire、expireat、pexpireat的執行過程

來源:互聯網
上載者:User

標籤:

在瞭解到期鍵問題前我們首先需要對redis的資料庫和資料庫鍵空間有一定的瞭解:

struct redisServer {
    // ...
    // 一個數組,儲存著伺服器中的所有資料庫
    redisDb *db;

  //伺服器的資料庫數量,dbnum屬性的值由伺服器配置的database選項決定,預設情況下,該選項的值為16
    int dbnum;

    // ...
};

在伺服器內部,用戶端狀態redisClient結構的db屬性記錄了用戶端當前的目標資料庫,這個屬性是一個指向redisDb結構的指標:

typedef struct redisClient {
// ...
// 記錄用戶端當前正在使用的資料庫
redisDb *db;
// ...
} redisClient;

 

現在我們再來看看redisDb 結構,redisDb結構的dict字典儲存了資料庫中的所有索引值對,我們將這個字典稱為鍵空間(key space):

typedef struct redisDb {
    // ...
    // 
資料庫鍵空間,儲存著資料庫中的所有索引值對
    dict *dict;
    // ...
} redisDb;

  • 鍵空間的鍵也就是資料庫的鍵,每個鍵都是一個字串對象
  • 鍵空間的值也就是資料庫的值,每個值可以是字串對象、列表對象、雜湊表對象、集合對象和有序集合對象中的任意一種Redis對象

下面是一個例子:

根據這個鍵空間,執行相關的添加、刪除、更新等操作的便可以比較容易理解,我們此處也忽略不講了。

下面我們再來介紹一下redisDb結構中的另外一個字典expires,這個字典儲存了資料庫中所有鍵的到期時間,我們稱這個字典為到期字典(注意這裡面只儲存著鍵的到期時間,可不是說這個字典裡面的鍵都是到期的):

  • 到期字典的鍵是一個指標,這個指標指向鍵空間中的某個鍵對象(也即是某個資料庫鍵);
  • 到期字典的值是一個long long類型的整數,這個整數儲存了鍵所指向的資料庫鍵的到期時間——一個毫秒精度的UNIX時間戳記;

 

有了上面的知識我們下載便可以來看看四個命令:expire、pexpire、expireat、pexpireat的實現過程。

四個命令的使用是比較簡單的:EXPIRE <key> <seconds> 如:EXPIRE book 100

              PEXPIRE <key> <millionseconds>

              EXPIREAT <key> <timemap> 

              PEXPIREAT <key> <timemap> 如:PEXPIREAT book 1388556000000(2014年1月1日零時)其實我也不知道這是怎麼算出來的!!!

注意:利用PERSIST命令可以移除一個鍵的到期時間。

如:redis> PEXPIREAT message 1391234400000
(integer) 1

其他的命令類似。不過值得一提的是:EXPIRE、EXPIREAT、PEXPIRE全部是轉換成PEXPIREAT來實現的。下面來看看每個命令的實現函數:

void expireCommand(redisClient *c) {
expireGenericCommand(c,mstime(),UNIT_SECONDS);
}
void expireatCommand(redisClient *c) {
expireGenericCommand(c,0,UNIT_SECONDS);
}
void pexpireCommand(redisClient *c) {
expireGenericCommand(c,mstime(),UNIT_MILLISECONDS);
}
void pexpireatCommand(redisClient *c) {
expireGenericCommand(c,0,UNIT_MILLISECONDS);
}

他們都調用了expireGenericCommand()函數進行實現,那我們現在就來分析一下expireGenericCommand函數是怎麼實現的:

 

/*----------------------------------------------------------------------------- * Expires Commands *----------------------------------------------------------------------------*//* This is the generic command implementation for EXPIRE, PEXPIRE, EXPIREAT * and PEXPIREAT. Because the commad second argument may be relative or absolute * the "basetime" argument is used to signal what the base time is (either 0 * for *AT variants of the command, or the current time for relative expires). * * 這個函數是 EXPIRE 、 PEXPIRE 、 EXPIREAT 和 PEXPIREAT 命令的底層實現函數。 * 命令的第二個參數可能是絕對值,也可能是相對值。 * 當執行 *AT 命令時, basetime 為 0 ,在其他情況下,它儲存的就是當前的絕對時間。 * * unit is either UNIT_SECONDS or UNIT_MILLISECONDS, and is only used for * the argv[2] parameter. The basetime is always specified in milliseconds.  * * unit 用於指定 argv[2] (傳入到期時間)的格式, * 它可以是 UNIT_SECONDS 或 UNIT_MILLISECONDS , * basetime 參數則總是毫秒格式的。 */void expireGenericCommand(redisClient *c, long long basetime, int unit) {    robj *key = c->argv[1], *param = c->argv[2];    long long when; /* unix time in milliseconds when the key will expire. */    // 取出param中的整數值或者嘗試將param中的資料儘可能轉換成整數值存在when中,成功返回REDIS_OK失敗則返回REDIS_ERR    if (getLongLongFromObjectOrReply(c, param, &when, NULL) != REDIS_OK)        return;    // 如果傳入的到期時間是以秒為單位的,那麼將它轉換為毫秒    if (unit == UNIT_SECONDS) when *= 1000;    when += basetime;    /* No key, return zero. */    // 查詢一下該鍵是否存在    if (lookupKeyRead(c->db,key) == NULL) {        addReply(c,shared.czero);        return;    }    /* EXPIRE with negative TTL, or EXPIREAT with a timestamp into the past     * should never be executed as a DEL when load the AOF or in the context     * of a slave instance.     *     * 在載入AOF資料時,或者伺服器為附屬節點時,     * 即使 EXPIRE 的 TTL 為負數,或者 EXPIREAT 提供的時間戳記已經到期,     * 伺服器也不會主動刪除這個鍵,而是等待主節點發來顯式的 DEL 命令。     *     * Instead we take the other branch of the IF statement setting an expire     * (possibly in the past) and wait for an explicit DEL from the master.      *     * 程式會繼續將(一個可能已經到期的 TTL)設定為鍵的到期時間,     * 並且等待主節點發來 DEL 命令。     */    if (when <= mstime() && !server.loading && !server.masterhost) {        // when 提供的時間已經到期,伺服器為主節點(注意主伺服器的masterhost==NULL),並且沒在載入資料        robj *aux;        //刪除該鍵        redisAssertWithInfo(c,key,dbDelete(c->db,key));        server.dirty++;        /* Replicate/AOF this as an explicit DEL. */        // 傳播 DEL 命令到AOF或者從伺服器        aux = createStringObject("DEL",3);        //修改用戶端的參數數組        rewriteClientCommandVector(c,2,aux,key);        decrRefCount(aux);        //訊號:索引值已經改變了。調用touchWatchedKey(db,key)        signalModifiedKey(c->db,key);        //發送鍵空間通知和鍵事件通知        notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",key,c->db->id);        addReply(c, shared.cone);        return;    } else {        // 設定鍵的到期時間        // 如果伺服器為附屬節點,或者伺服器正在載入,        // 那麼這個 when 有可能已經到期的        setExpire(c->db,key,when);        addReply(c,shared.cone);        signalModifiedKey(c->db,key);        notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"expire",key,c->db->id);        server.dirty++;        return;    }}

下面我將對自己在看代碼時裡面不太熟悉的幾個函數進行說明:

  • getLongLongFromObjectOrReply(redisClient *c,robj *o,long long *target,const char *msg)

函數目的:嘗試從對象o中取出整數值,或者嘗試將對象o中的值換成整數值,並將得到的值儲存在target中。同時如果轉換取出/轉成功的話,返回REDIS_OK,否則返回REDIS_ERR,並向用戶端發送一條出錯回複。

大致實現過程: getLongLongFromObjectOrReply——>getLongLongFromObject——>stroll()最後主要看看stoll函數的實現過程就OK了。

  • lookupKeyRead(redisDb *db,robj *key)

函數目的:為執行讀取操作而取出鍵key在資料庫中的值。並根據是否成功找到值,補救伺服器中的命中和不命中資訊。找搞則傳回值,沒找到則返回NULL

函數實現

  • rewriteClientCommandVector(redisClient *c,int argc,...)

函數目的:修改用戶端的參數數組。這其中涉及到C語言可變參數的運用,可以自行學習。

函數實現:如果理解了C語言的可變參數的大致運用的話,函數的實現過程已經大致理解了。其中需要注意的是lookupCommandOrOriginal()函數,lookupCommandOrOriginal()目的是在命令被更名之後,將更名後正確的redisCommand進行返回。

  • signalModifiedKey(c->db,key)索引值已經改變了的訊號。調用touchWatchedKey(db,key)(“觸碰”一個鍵,如果這個鍵在某個用戶端watch下,那這個用戶端執行的EXEC時事務將失敗)
  • notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",key,c->db->id) 發送鍵空間通知和鍵事件通知(該部分知識今後會提到)

這裡主要講的Redis是怎麼樣設定到期鍵的,可以算作後續"Redis到期鍵的刪除策略"的前篇或者說預備知識。

 

redis學習筆記——expire、pexpire、expireat、pexpireat的執行過程

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.