老熟女激烈的高潮_日韩一级黄色录像_亚洲1区2区3区视频_精品少妇一区二区三区在线播放_国产欧美日产久久_午夜福利精品导航凹凸

重慶分公司,新征程啟航

為企業提供網站建設、域名注冊、服務器等服務

數據庫redisDb與鍵過期刪除方法是什么

本篇內容主要講解“數據庫redis Db與鍵過期刪除方法是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“數據庫redis Db與鍵過期刪除方法是什么”吧!

天峻網站建設公司創新互聯建站,天峻網站設計制作,有大型網站制作公司豐富經驗。已為天峻成百上千家提供企業網站建設服務。企業網站搭建\成都外貿網站建設要多少錢,請找那個售后服務好的天峻做網站的公司定做!

一. 數據庫

Redis的數據庫使用字典作為底層實現,數據庫的增、刪、查、改都是構建在字典的操作之上的。
redis服務器將所有數據庫都保存在服務器狀態結構redisServer(redis.h/redisServer)的db數組(應該是一個鏈表)里:

struct redisServer {
  //..
  // 數據庫數組,保存著服務器中所有的數據庫
    redisDb *db;
  //..
}

在初始化服務器時,程序會根據服務器狀態的dbnum屬性來決定應該創建多少個數據庫:

struct redisServer {
    // ..
    //服務器中數據庫的數量
    int dbnum;
    //..
}

dbnum屬性的值是由服務器配置的database選項決定的,默認值為16;

二、切換數據庫原理

每個Redis客戶端都有自己的目標數據庫,每當客戶端執行數據庫的讀寫命令時,目標數據庫就會成為這些命令的操作對象。

127.0.0.1:6379> set msg 'Hello world'
OK
127.0.0.1:6379> get msg
"Hello world"
127.0.0.1:6379> select 2
OK
127.0.0.1:6379[2]> get msg
(nil)
127.0.0.1:6379[2]>

在服務器內部,客戶端狀態redisClient結構(redis.h/redisClient)的db屬性記錄了客戶端當前的目標數據庫,這個屬性是一個指向redisDb結構(redis.h/redisDb)的指針:

typedef struct redisClient {
    //..
    // 客戶端當前正在使用的數據庫
    redisDb *db;
    //..
} redisClient;

redisClient.db指針指向redisServer.db數組中的一個元素,而被指向的元素就是當前客戶端的目標數據庫。
我們就可以通過修改redisClient指針,讓他指向服務器中的不同數據庫,從而實現切換數據庫的功能–這就是select命令的實現原理。
實現代碼:

int selectDb(redisClient *c, int id) {
    // 確保 id 在正確范圍內
    if (id < 0 || id >= server.dbnum)
        return REDIS_ERR;
    // 切換數據庫(更新指針)
    c->db = &server.db[id];
    return REDIS_OK;
}

三、數據庫的鍵空間

1、數據庫的結構(我們只分析鍵空間和鍵過期時間)

typedef struct redisDb {
    // 數據庫鍵空間,保存著數據庫中的所有鍵值對
    dict *dict;                 /* The keyspace for this DB */
    // 鍵的過期時間,字典的鍵為鍵,字典的值為過期事件 UNIX 時間戳
    dict *expires;              /* Timeout of keys with a timeout set */
    // 數據庫號碼
    int id;                     /* Database ID */
    // 數據庫的鍵的平均 TTL ,統計信息
    long long avg_ttl;          /* Average TTL, just for stats */
    //..
} redisDb

上圖是一個RedisDb的示例,該數據庫存放有五個鍵值對,分別是sRedis,INums,hBooks,SortNum和sNums,它們各自都有自己的值對象,另外,其中有三個鍵設置了過期時間,當前數據庫是服務器的第0號數據庫。現在,我們就從源碼角度分析這個數據庫結構:
我們知道,Redis是一個鍵值對數據庫服務器,服務器中的每一個數據庫都是一個redis.h/redisDb結構,其中,結構中的dict字典保存了數據庫中所有的鍵值對,我們就將這個字典成為鍵空間。
Redis數據庫的數據都是以鍵值對的形式存在,其充分利用了字典高效索引的特點。
a、鍵空間的鍵就是數據庫中的鍵,一般都是字符串對象;
b、鍵空間的值就是數據庫中的值,可以是5種類型對象(字符串、列表、哈希、集合和有序集合)之一。
數據庫的鍵空間結構分析完了,我們先看看數據庫的初始化。

2、鍵空間的初始化

在redis.c中,我們可以找到鍵空間的初始化操作:

//創建并初始化數據庫結構
 for (j = 0; j < server.dbnum; j++) {
    // 創建每個數據庫的鍵空間
    server.db[j].dict = dictCreate(&dbDictType,NULL);
    // ...
    // 設定當前數據庫的編號
    server.db[j].id = j;
}

初始化之后就是對鍵空間的操作了。

3、鍵空間的操作

我先把一些常見的鍵空間操作函數列出來:

// 從數據庫中取出鍵key的值對象,若不存在就返回NULL
robj *lookupKey(redisDb *db, robj *key);
/* 先刪除過期鍵,以讀操作的方式從數據庫中取出指定鍵對應的值對象
 * 并根據是否成功找到值,更新服務器的命中或不命中信息,
 * 如不存在則返回NULL,底層調用lookupKey函數 */
robj *lookupKeyRead(redisDb *db, robj *key);
/* 先刪除過期鍵,以寫操作的方式從數據庫中取出指定鍵對應的值對象
 * 如不存在則返回NULL,底層調用lookupKey函數,
 * 不會更新服務器的命中或不命中信息
 */
robj *lookupKeyWrite(redisDb *db, robj *key);
/* 先刪除過期鍵,以讀操作的方式從數據庫中取出指定鍵對應的值對象
 * 如不存在則返回NULL,底層調用lookupKeyRead函數
 * 此操作需要向客戶端回復
 */
robj *lookupKeyReadOrReply(redisClient *c, robj *key, robj *reply);
/* 先刪除過期鍵,以寫操作的方式從數據庫中取出指定鍵對應的值對象
 * 如不存在則返回NULL,底層調用lookupKeyWrite函數
 * 此操作需要向客戶端回復
 */
robj *lookupKeyWriteOrReply(redisClient *c, robj *key, robj *reply);
/* 添加元素到指定數據庫 */
void dbAdd(redisDb *db, robj *key, robj *val);
/* 重寫指定鍵的值 */
void dbOverwrite(redisDb *db, robj *key, robj *val);
/* 設定指定鍵的值 */
void setKey(redisDb *db, robj *key, robj *val);
/* 判斷指定鍵是否存在 */
int dbExists(redisDb *db, robj *key);
/* 隨機返回數據庫中的鍵 */
robj *dbRandomKey(redisDb *db);
/* 刪除指定鍵 */
int dbDelete(redisDb *db, robj *key);
/* 清空所有數據庫,返回鍵值對的個數 */
long long emptyDb(void(callback)(void*));

下面我選取幾個比較典型的操作函數分析一下:

查找鍵值對函數–lookupKey

robj *lookupKey(redisDb *db, robj *key) {
    // 查找鍵空間
    dictEntry *de = dictFind(db->dict,key->ptr);
    // 節點存在
    if (de) {
        // 取出該鍵對應的值
        robj *val = dictGetVal(de);
        // 更新時間信息
        if (server.rdb_child_pid == -1 && server.aof_child_pid == -1)
            val->lru = LRU_CLOCK();
        // 返回值
        return val;
    } else {
        // 節點不存在
        return NULL;
    }
}

添加鍵值對–dbAdd
添加鍵值對使我們經常使用到的函數,底層由dbAdd()函數實現,傳入的參數是待添加的數據庫,鍵對象和值對象,源碼如下:

void dbAdd(redisDb *db, robj *key, robj *val) {
    // 復制鍵名
    sds copy = sdsdup(key->ptr);
    // 嘗試添加鍵值對
    int retval = dictAdd(db->dict, copy, val);
    // 如果鍵已經存在,那么停止
    redisAssertWithInfo(NULL,key,retval == REDIS_OK);
    // 如果開啟了集群模式,那么將鍵保存到槽里面
    if (server.cluster_enabled) slotToKeyAdd(key);
 }

好了,關于鍵空間操作函數就分析到這,其他函數(在文件db.c中)大家可以自己去分析,有問題的話可以回帖,我們可以一起討論!

四、數據庫的過期鍵操作

在前面我們說到,redisDb結構中有一個expires指針(概況圖可以看上圖),該指針指向一個字典結構,字典中保存了所有鍵的過期時間,該字典稱為過期字典。
過期字典的初始化:

// 創建并初始化數據庫結構

 for (j = 0; j < server.dbnum; j++) {
        // 創建每個數據庫的過期時間字典
        server.db[j].expires = dictCreate(&keyptrDictType,NULL);
        // 設定當前數據庫的編號
        server.db[j].id = j;
        // ..
    }

a、過期字典的鍵是一個指針,指向鍵空間中的某一個鍵對象(就是某一個數據庫鍵);
b、過期字典的值是一個long long類型的整數,這個整數保存了鍵所指向的數據庫鍵的時間戳–一個毫秒精度的unix時間戳。
下面我們就來分析過期鍵的處理函數:

1、過期鍵處理函數

設置鍵的過期時間–setExpire()

/*
 * 將鍵 key 的過期時間設為 when
 */
void setExpire(redisDb *db, robj *key, long long when) {
    dictEntry *kde, *de;
    // 從鍵空間中取出鍵key
    kde = dictFind(db->dict,key->ptr);
    // 如果鍵空間找不到該鍵,報錯
    redisAssertWithInfo(NULL,key,kde != NULL);
    // 向過期字典中添加該鍵
    de = dictReplaceRaw(db->expires,dictGetKey(kde));
    // 設置鍵的過期時間
    // 這里是直接使用整數值來保存過期時間,不是用 INT 編碼的 String 對象
    dictSetSignedIntegerVal(de,when);
}
獲取鍵的過期時間–getExpire()
long long getExpire(redisDb *db, robj *key) {
    dictEntry *de;
    // 如果過期鍵不存在,那么直接返回
    if (dictSize(db->expires) == 0 ||
       (de = dictFind(db->expires,key->ptr)) == NULL) return -1;
    redisAssertWithInfo(NULL,key,dictFind(db->dict,key->ptr) != NULL);
    // 返回過期時間
    return dictGetSignedIntegerVal(de);
}
刪除鍵的過期時間–removeExpire()
// 移除鍵 key 的過期時間
int removeExpire(redisDb *db, robj *key) {
    // 確保鍵帶有過期時間
    redisAssertWithInfo(NULL,key,dictFind(db->dict,key->ptr) != NULL);
    // 刪除過期時間
    return dictDelete(db->expires,key->ptr) == DICT_OK;
}

2、過期鍵刪除策略

通過前面的介紹,大家應該都知道數據庫鍵的過期時間都保存在過期字典里,那假如一個鍵過期了,那么這個過期鍵是什么時候被刪除的呢?現在來看看redis的過期鍵的刪除策略:
a、定時刪除:在設置鍵的過期時間的同時,創建一個定時器,在定時結束的時候,將該鍵刪除;
b、惰性刪除:放任鍵過期不管,在訪問該鍵的時候,判斷該鍵的過期時間是否已經到了,如果過期時間已經到了,就執行刪除操作;
c、定期刪除:每隔一段時間,對數據庫中的鍵進行一次遍歷,刪除過期的鍵。
其中定時刪除可以及時刪除數據庫中的過期鍵,并釋放過期鍵所占用的內存,但是它為每一個設置了過期時間的鍵都開了一個定時器,使的cpu的負載變高,會對服務器的響應時間和吞吐量造成影響。
惰性刪除有效的克服了定時刪除對CPU的影響,但是,如果一個過期鍵很長時間沒有被訪問到,且若存在大量這種過期鍵時,勢必會占用很大的內存空間,導致內存消耗過大。
定時刪除可以算是上述兩種策略的折中。設定一個定時器,每隔一段時間遍歷數據庫,刪除其中的過期鍵,有效的緩解了定時刪除對CPU的占用以及惰性刪除對內存的占用。
在實際應用中,Redis采用了惰性刪除和定時刪除兩種策略來對過期鍵進行處理,上面提到的lookupKeyWrite等函數中就利用到了惰性刪除策略,定時刪除策略則是在根據服務器的例行處理程序serverCron來執行刪除操作,該程序每100ms調用一次。

惰性刪除函數–expireIfNeeded()
源碼如下:

/* 檢查key是否已經過期,如果是的話,將它從數據庫中刪除 
 * 并將刪除命令寫入AOF文件以及附屬節點(主從復制和AOF持久化相關)
 * 返回0代表該鍵還沒有過期,或者沒有設置過期時間
 * 返回1代表該鍵因為過期而被刪除
 */
int expireIfNeeded(redisDb *db, robj *key) {
    // 獲取該鍵的過期時間
    mstime_t when = getExpire(db,key);
    mstime_t now;
    // 該鍵沒有設定過期時間
    if (when < 0) return 0;
    // 服務器正在加載數據的時候,不要處理
    if (server.loading) return 0;
    // lua腳本相關
    now = server.lua_caller ? server.lua_time_start : mstime();
    // 主從復制相關,附屬節點不主動刪除key
    if (server.masterhost != NULL) return now > when;
    // 該鍵還沒有過期
    if (now <= when) return 0;
    // 刪除過期鍵
    server.stat_expiredkeys++;
    // 將刪除命令傳播到AOF文件和附屬節點
    propagateExpire(db,key);
    // 發送鍵空間操作時間通知
    notifyKeyspaceEvent(NOTIFY_EXPIRED,
        "expired",key,db->id);
    // 將該鍵從數據庫中刪除
    return dbDelete(db,key);
}

定期刪除策略
過期鍵的定期刪除策略由redis.c/activeExpireCycle()函數實現,服務器周期性地操作redis.c/serverCron()(每隔100ms執行一次)時,會調用activeExpireCycle()函數,分多次遍歷服務器中的各個數據庫,從數據庫中的expires字典中隨機檢查一部分鍵的過期時間,并刪除其中的過期鍵。
刪除過期鍵的操作由activeExpireCycleTryExpire函數(activeExpireCycle()調用了該函數)執行,其源碼如下:

/* 檢查鍵的過期時間,如過期直接刪除*/
int activeExpireCycleTryExpire(redisDb *db, dictEntry *de, long long now) {
    // 獲取過期時間
    long long t = dictGetSignedIntegerVal(de);
    if (now > t) {
        // 執行到此說明過期
        // 創建該鍵的副本
        sds key = dictGetKey(de);
        robj *keyobj = createStringObject(key,sdslen(key));
        // 將刪除命令傳播到AOF和附屬節點
        propagateExpire(db,keyobj);
        // 在數據庫中刪除該鍵
        dbDelete(db,keyobj);
        // 發送事件通知
        notifyKeyspaceEvent(NOTIFY_EXPIRED,
            "expired",keyobj,db->id);
        // 臨時鍵對象的引用計數減1
        decrRefCount(keyobj);
        // 服務器的過期鍵計數加1
        // 該參數影響每次處理的數據庫個數
        server.stat_expiredkeys++;
        return 1;
    } else {
        return 0;
    }
}

到此,相信大家對“數據庫redis Db與鍵過期刪除方法是什么”有了更深的了解,不妨來實際操作一番吧!這里是創新互聯網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!


網頁名稱:數據庫redisDb與鍵過期刪除方法是什么
文章鏈接:http://www.xueling.net.cn/article/gjsdsc.html

其他資訊

在線咨詢
服務熱線
服務熱線:028-86922220
TOP
主站蜘蛛池模板: 一边摸一边做爽的视频17国产有奶水 | 精品国产日韩欧美一区二区 | 久久久免费的成人性教育片 | 国产成人精品成人a在线观看 | 国产成人av一区二区在线观看 | 国产精品久久人妻无码hd | 国产日韩欧美一区二区三区在线 | 国产一区二区三区欧美日韩 | 国产激情久久久久影院老熟女免费 | 欧美人与动人物牲交免费观看久久 | 色视频在线观看免费 | 和人妻隔着帘子按摩中字 | 亚洲成人色图网 | 亚洲成人手机在线观看 | 中文字幕久久久人伦 | 国产精品国产三级国产普通话一 | 国产欧美一区二区在线播放 | 亚洲国产福利成人一区 | 美女内射无套日韩免费播放 | 久久男女视频 | 播放灌醉水嫩大学生国内精品 | 国产对白叫床清晰在线播放 | 国产成人精品无码A区在线观看 | 综合99 | 免费无码一区二区三区 | 久久久精彩 | 99毛片| 91成人午夜性a一级毛片 | 精品国品一二三产品区别在线观看 | 久久亚洲激情 | 亚洲日产精品一二三四区新增区域 | 丰满少妇熟乱XXXXX视频 | 亚洲男人的天堂视频 | 亚洲成人日韩在线 | 日本国产欧美大码a视频 | 狼群视频在线观看www | 亚洲精品乱码久久久久久9色 | 亚洲精品免费播放 | 无码国产精品一区二区免费VR | 91多人xxx少妇 | 91精品国自产在线观看 |