<del id="d4fwx"><form id="d4fwx"></form></del>
      <del id="d4fwx"><form id="d4fwx"></form></del><del id="d4fwx"><form id="d4fwx"></form></del>

            <code id="d4fwx"><abbr id="d4fwx"></abbr></code>
          • 數(shù)據(jù)庫redisDb與鍵過期刪除方法是什么

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

            天峻網(wǎng)站建設(shè)公司創(chuàng)新互聯(lián)建站,天峻網(wǎng)站設(shè)計(jì)制作,有大型網(wǎng)站制作公司豐富經(jīng)驗(yàn)。已為天峻成百上千家提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\成都外貿(mào)網(wǎng)站建設(shè)要多少錢,請找那個(gè)售后服務(wù)好的天峻做網(wǎng)站的公司定做!

            一. 數(shù)據(jù)庫

            Redis的數(shù)據(jù)庫使用字典作為底層實(shí)現(xiàn),數(shù)據(jù)庫的增、刪、查、改都是構(gòu)建在字典的操作之上的。
            redis服務(wù)器將所有數(shù)據(jù)庫都保存在服務(wù)器狀態(tài)結(jié)構(gòu)redisServer(redis.h/redisServer)的db數(shù)組(應(yīng)該是一個(gè)鏈表)里:

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

            在初始化服務(wù)器時(shí),程序會(huì)根據(jù)服務(wù)器狀態(tài)的dbnum屬性來決定應(yīng)該創(chuàng)建多少個(gè)數(shù)據(jù)庫:

            struct redisServer {
                // ..
                //服務(wù)器中數(shù)據(jù)庫的數(shù)量
                int dbnum;
                //..
            }

            dbnum屬性的值是由服務(wù)器配置的database選項(xiàng)決定的,默認(rèn)值為16;

            二、切換數(shù)據(jù)庫原理

            每個(gè)Redis客戶端都有自己的目標(biāo)數(shù)據(jù)庫,每當(dāng)客戶端執(zhí)行數(shù)據(jù)庫的讀寫命令時(shí),目標(biāo)數(shù)據(jù)庫就會(huì)成為這些命令的操作對象。

            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]>

            在服務(wù)器內(nèi)部,客戶端狀態(tài)redisClient結(jié)構(gòu)(redis.h/redisClient)的db屬性記錄了客戶端當(dāng)前的目標(biāo)數(shù)據(jù)庫,這個(gè)屬性是一個(gè)指向redisDb結(jié)構(gòu)(redis.h/redisDb)的指針:

            typedef struct redisClient {
                //..
                // 客戶端當(dāng)前正在使用的數(shù)據(jù)庫
                redisDb *db;
                //..
            } redisClient;

            redisClient.db指針指向redisServer.db數(shù)組中的一個(gè)元素,而被指向的元素就是當(dāng)前客戶端的目標(biāo)數(shù)據(jù)庫。
            我們就可以通過修改redisClient指針,讓他指向服務(wù)器中的不同數(shù)據(jù)庫,從而實(shí)現(xiàn)切換數(shù)據(jù)庫的功能–這就是select命令的實(shí)現(xiàn)原理。
            實(shí)現(xiàn)代碼:

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

            三、數(shù)據(jù)庫的鍵空間

            1、數(shù)據(jù)庫的結(jié)構(gòu)(我們只分析鍵空間和鍵過期時(shí)間)

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

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

            2、鍵空間的初始化

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

            //創(chuàng)建并初始化數(shù)據(jù)庫結(jié)構(gòu)
             for (j = 0; j < server.dbnum; j++) {
                // 創(chuàng)建每個(gè)數(shù)據(jù)庫的鍵空間
                server.db[j].dict = dictCreate(&dbDictType,NULL);
                // ...
                // 設(shè)定當(dāng)前數(shù)據(jù)庫的編號
                server.db[j].id = j;
            }

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

            3、鍵空間的操作

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

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

            下面我選取幾個(gè)比較典型的操作函數(shù)分析一下:

            查找鍵值對函數(shù)–lookupKey

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

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

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

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

            四、數(shù)據(jù)庫的過期鍵操作

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

            // 創(chuàng)建并初始化數(shù)據(jù)庫結(jié)構(gòu)

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

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

            1、過期鍵處理函數(shù)

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

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

            2、過期鍵刪除策略

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

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

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

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

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

            到此,相信大家對“數(shù)據(jù)庫redis Db與鍵過期刪除方法是什么”有了更深的了解,不妨來實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

            新聞標(biāo)題:數(shù)據(jù)庫redisDb與鍵過期刪除方法是什么
            標(biāo)題鏈接:http://www.jbt999.com/article32/gjsdsc.html

            成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供自適應(yīng)網(wǎng)站、App開發(fā)、網(wǎng)站排名、電子商務(wù)、微信小程序網(wǎng)站設(shè)計(jì)

            廣告

            聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:[email protected]。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

            手機(jī)網(wǎng)站建設(shè)

              <del id="d4fwx"><form id="d4fwx"></form></del>
              <del id="d4fwx"><form id="d4fwx"></form></del><del id="d4fwx"><form id="d4fwx"></form></del>

                    <code id="d4fwx"><abbr id="d4fwx"></abbr></code>
                  • 一区二区精 | 国产内射在线观看 | 欧美内射网站 | 日韩一级A片免费 | 午夜黄片视频 |