Redis系列(三)--過期策略

制定Redis過期策略萨蚕,是整個Redis緩存策略的關(guān)鍵之一,因為內(nèi)存來說,公司不可能無限大办陷,所以就要對key進(jìn)行一系列的管控绍移。

文章結(jié)構(gòu):(1)理解Redis過期設(shè)置API(命令與Java描述版本)框弛;(2)理解Redis內(nèi)部的過期策略扁誓;(3)對開發(fā)需求而言邓梅,Redis過期策略的設(shè)計實現(xiàn)經(jīng)驗冶共。


本系列文章:

(1) Redis系列(一)--安裝乾蛤、helloworld以及讀懂配置文件

(2)Redis系列(二)--緩存設(shè)計(整表緩存以及排行榜緩存方案實現(xiàn))

一、理解Redis過期設(shè)置API(命令與Java描述版本):

(1)TTL命令:

redis 127.0.0.1:6379> TTL KEY_NAME

返回值

當(dāng) key 不存在時捅僵,返回 -2 家卖。 當(dāng) key 存在但沒有設(shè)置剩余生存時間時,返回 -1 庙楚。 否則上荡,以秒為單位,返回 key 的剩余生存時間馒闷。

注意:在 Redis 2.8 以前酪捡,當(dāng) key 不存在,或者 key 沒有設(shè)置剩余生存時間時纳账,命令都返回 -1 逛薇。

(2)EXPIRE命令

定義:為給定 key 設(shè)置生存時間,當(dāng) key 過期時(生存時間為 0 )疏虫,它會被自動刪除永罚。

redis 127.0.0.1:6379> EXPIRE runooobkey 60
(integer) 1

返回值

設(shè)置成功返回 1 。 當(dāng) key 不存在或者不能為 key 設(shè)置過期時間時(比如在低于 2.1.3 版本的 Redis 中你嘗試更新 key 的過期時間)返回 0 议薪。

key生存時間注意點:

生存時間可以通過使用 DEL 命令來刪除整個 key 來移除尤蛮,或者被 SET 和 GETSET 命令覆寫(overwrite),這意味著斯议,如果一個命令只是修改(alter)一個帶生存時間的 key 的值而不是用一個新的 key 值來代替(replace)它的話,那么生存時間不會被改變醇锚。

比如說哼御,對一個 key 執(zhí)行 INCR 命令,對一個列表進(jìn)行 LPUSH 命令焊唬,或者對一個哈希表執(zhí)行 HSET 命令恋昼,這類操作都不會修改 key 本身的生存時間。

另一方面赶促,如果使用 RENAME 對一個 key 進(jìn)行改名液肌,那么改名后的 key 的生存時間和改名前一樣。

RENAME 命令的另一種可能是鸥滨,嘗試將一個帶生存時間的 key 改名成另一個帶生存時間的 another_key 嗦哆,這時舊的 another_key (以及它的生存時間)會被刪除谤祖,然后舊的 key 會改名為 another_key ,因此老速,新的 another_key 的生存時間也和原本的 key 一樣粥喜。

(3)PEXPIRE命令

設(shè)置成功返回 1 。 當(dāng) key 不存在或者不能為 key 設(shè)置過期時間時(比如在低于 2.1.3 版本的 Redis 中你嘗試更新 key 的過期時間)返回 0 橘券。

(4)PERSIST 命令

返回值:

當(dāng)過期時間移除成功時额湘,返回 1 。 如果 key 不存在或 key 沒有設(shè)置過期時間旁舰,返回 0 锋华。

127.0.0.1:6379> PEXPIRE k2 10000000
(integer) 1

(5)SETEX命令

用于在Redis鍵中的指定超時,設(shè)置鍵的字符串值

返回值:

字符串箭窜,如果在鍵中設(shè)置了值則返回OK毯焕。如果值未設(shè)置則返回 Null。

127.0.0.1:6379> SETEX k1 100 v1
OK
127.0.0.1:6379> ttl k1
(integer) 92
127.0.0.1:6379> get k1
"v1"

(6)補充:(精度不同的時間設(shè)置):

EXPIREAT <key> < timestamp> 命令用于將鍵key 的過期時間設(shè)置為timestamp所指定的秒數(shù)時間戳绽快。

PEXPIREAT <key> < timestamp > 命令用于將鍵key 的過期時間設(shè)置為timestamp所指定的毫秒數(shù)時間戳芥丧。

例子:

    //TTL命令
127.0.0.1:6379> FLUSHDB
OK
127.0.0.1:6379> ttl key
(integer) -2
127.0.0.1:6379> set key value
OK
127.0.0.1:6379> ttl key
(integer) -1


//expire命令
127.0.0.1:6379> expire key 10
(integer) 1
127.0.0.1:6379> ttl key
(integer) 7
127.0.0.1:6379> ttl key
(integer) 3
127.0.0.1:6379> ttl key
(integer) -2


//PEXPIRE命令
127.0.0.1:6379> set k2 v2
OK
127.0.0.1:6379> PEXPIRE k2 10000000
(integer) 1
127.0.0.1:6379> ttl k2
(integer) 9994


//PERSIST 命令
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> EXPIRE k1 100
(integer) 1
127.0.0.1:6379> ttl k1
(integer) 86
127.0.0.1:6379> PERSIST k1
(integer) 1
127.0.0.1:6379> ttl k1
(integer) -1

(6)Java代碼控制:

    @Autowired
    private JedisPool jedisPool;
    Jedis jedis = jedisPool.getResource();

        System.out.println("判斷key是否存在:"+shardedJedis.exists("key"));
        // 設(shè)置 key001的過期時間
        System.out.println("設(shè)置 key的過期時間為5秒:"+jedis.expire("key", 5));
          // 查看某個key的剩余生存時間,單位【秒】.永久生存或者不存在的都返回-1
        System.out.println("查看key的剩余生存時間:"+jedis.ttl("key"));
        // 移除某個key的生存時間
        System.out.println("移除key的生存時間:"+jedis.persist("key"));
        System.out.println("查看key的剩余生存時間:"+jedis.ttl("key"));
        // 查看key所儲存的值的類型
        System.out.println("查看key所儲存的值的類型:"+jedis.type("key"));

二、理解Redis內(nèi)部的過期策略:

(1)總述:

Redis采用的是定期刪除策略和懶漢式的策略互相配合坊罢。

注意续担!是Redis內(nèi)部自主完成!是Redis內(nèi)部自主完成活孩!是Redis內(nèi)部自主完成物遇!

我們只可以通過調(diào)整外圍參數(shù),以及設(shè)計數(shù)據(jù)淘汰模式去調(diào)控我們的Redis緩存系統(tǒng)過期策略憾儒。

(2)定期刪除策略:

1)含義:每隔一段時間執(zhí)行一次刪除過期key操作

2)優(yōu)點:

通過限制刪除操作的時長和頻率询兴,來減少刪除操作對CPU時間的占用--處理"定時刪除"的缺點

定期刪除過期key--處理"懶漢式刪除"的缺點

3)缺點:

在內(nèi)存友好方面,會造成一定的內(nèi)存占用起趾,但是沒有懶漢式那么占用內(nèi)存(相對于定時刪除則不如)

在CPU時間友好方面诗舰,不如"懶漢式刪除"(會定期的去進(jìn)行比較和刪除操作,cpu方面不如懶漢式训裆,但是比定時好)

4)關(guān)鍵點:

合理設(shè)置刪除操作的執(zhí)行時長(每次刪除執(zhí)行多長時間)和執(zhí)行頻率(每隔多長時間做一次刪除)(這個要根據(jù)服務(wù)器運行情況來定了)眶根,每次執(zhí)行時間太長,或者執(zhí)行頻率太高對cpu都是一種壓力边琉。

每次進(jìn)行定期刪除操作執(zhí)行之后属百,需要記錄遍歷循環(huán)到了哪個標(biāo)志位,以便下一次定期時間來時变姨,從上次位置開始進(jìn)行循環(huán)遍歷族扰。

對于懶漢式刪除而言,并不是只有獲取key的時候才會檢查key是否過期,在某些設(shè)置key的方法上也會檢查(例子:setnx key2 value2:如果設(shè)置的key2已經(jīng)存在渔呵,那么該方法返回false怒竿,什么都不做;如果設(shè)置的key2不存在厘肮,那么該方法設(shè)置緩存key2-value2愧口。假設(shè)調(diào)用此方法的時候,發(fā)現(xiàn)redis中已經(jīng)存在了key2类茂,但是該key2已經(jīng)過期了耍属,如果此時不執(zhí)行刪除操作的話,setnx方法將會直接返回false巩检,也就是說此時并沒有重新設(shè)置key2-value2成功厚骗,所以對于一定要在setnx執(zhí)行之前,對key2進(jìn)行過期檢查)兢哭。

5)刪除鍵流程(簡單而言领舰,對指定個數(shù)個庫的每一個庫隨機刪除小于等于指定個數(shù)個過期key):

1. 遍歷每個數(shù)據(jù)庫(就是redis.conf中配置的"database"數(shù)量,默認(rèn)為16)

2. 檢查當(dāng)前庫中的指定個數(shù)個key(默認(rèn)是每個庫檢查20個key迟螺,注意相當(dāng)于該循環(huán)執(zhí)行20次冲秽,循環(huán)體是下邊的描述)

如果當(dāng)前庫中沒有一個key設(shè)置了過期時間,直接執(zhí)行下一個庫的遍歷
隨機獲取一個設(shè)置了過期時間的key矩父,檢查該key是否過期锉桑,如果過期,刪除key
判斷定期刪除操作是否已經(jīng)達(dá)到指定時長窍株,若已經(jīng)達(dá)到民轴,直接退出定期刪除。

對于定期刪除球订,在程序中有一個全局變量current_db來記錄下一個將要遍歷的庫后裸,假設(shè)有16個庫,我們這一次定期刪除遍歷了10個冒滩,那此時的current_db就是11微驶,下一次定期刪除就從第11個庫開始遍歷,假設(shè)current_db等于15了开睡,那么之后遍歷就再從0號庫開始(此時current_db==0)

6)源碼機制閱讀:

定期刪除策略:此部分轉(zhuǎn)載部分此博主此文章

在redis源碼中祈搜,實現(xiàn)定期淘汰策略的是函數(shù)activeExpireCycle,每當(dāng)周期性函數(shù)serverCron執(zhí)行時士八,該函數(shù)會調(diào)用databasesCron函數(shù);然后databasesCron會調(diào)用activeExpireCycle函數(shù)進(jìn)行主動的過期鍵刪除。具體方法是在規(guī)定的時間內(nèi)梁呈,多次從expires中隨機挑一個鍵婚度,檢查它是否過期,如果過期則刪除。

首先這個函數(shù)有兩種執(zhí)行模式蝗茁,一個是快速模式一個是慢速模式醋虏,體現(xiàn)在代碼中就是timelimit這個變量中,這個變量是用來約束這個函數(shù)的運行時間的哮翘,我們可以考慮這樣一個場景颈嚼,就是數(shù)據(jù)庫中有很多過期的鍵需要清理,那么這個函數(shù)就會一直運行很長時間饭寺,這樣一直占用CPU顯然是不合理的阻课,所以需要這個變量來約束,當(dāng)函數(shù)運行時間超過了這個閾值艰匙,就算還有很多過期鍵沒有清理限煞,函數(shù)也強制退出。

在快速模式下员凝,timelimit的值是固定的署驻,是一個預(yù)定義的常量ACTIVE_EXPIRE_CYCLE_FAST_DURATION,在慢速模式下健霹,這個變量的值是通過下面的代碼計算的旺上。

timelimit = 1000000*ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC/server.hz/100;

他的計算依據(jù)是之前預(yù)定義好的每次迭代只能占用的CPU時間比例,以及這個函數(shù)被調(diào)用的頻率糖埋。

Redis中也可能有多個數(shù)據(jù)庫宣吱,所以這個函數(shù)會遍歷多個數(shù)據(jù)庫來清楚過期鍵 ,但是是根據(jù)下面代碼的原則來確定要遍歷的數(shù)據(jù)庫的個數(shù)的阶捆。

 if (dbs_per_call > server.dbnum || timelimit_exit)
        dbs_per_call = server.dbnum;

dbs_per_call變量就是函數(shù)會遍歷的數(shù)據(jù)庫的個數(shù)凌节,他有一個預(yù)定義的值REDIS_DBCRON_DBS_PER_CALL,但是如果這個值大于現(xiàn)在redis中本身的數(shù)據(jù)庫的個數(shù)洒试,我們就要將它的值變成當(dāng)前的數(shù)據(jù)庫的實際個數(shù)倍奢,或者上次的函數(shù)是因為超時強制退出了,說明可能有的數(shù)據(jù)庫在上次函數(shù)調(diào)用時沒有遍歷到垒棋,里面的過期鍵沒有清理掉卒煞,所以也要將這次遍歷的數(shù)據(jù)庫的個數(shù)改成實際數(shù)據(jù)庫的個數(shù)。

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

上面代碼可以看出:數(shù)據(jù)庫的遍歷是在這個大的for循環(huán)里叼架,其中值得留意的是current_db這個變量是一個static變量畔裕,這么做的好處是,如果真的發(fā)生了我們上面說的情況乖订,上一次函數(shù)調(diào)用因為超時而強制退出扮饶,這個變量就會記錄下這一次函數(shù)應(yīng)該從哪個數(shù)據(jù)庫開始遍歷,這樣會使得函數(shù)用在每個數(shù)據(jù)庫的時間盡量平均乍构,就不會出現(xiàn)有的數(shù)據(jù)庫里面的過期鍵一直沒有清理的情況甜无。

每個數(shù)據(jù)庫的過期鍵清理的操作是在下面的這個do while 循環(huán)中(由于代碼過長,所以中間有很多代碼我把它隱藏了,現(xiàn)在看到的只是一個大框架岂丘,稍后我會對其中的部分詳細(xì)講解)

do {
    ... 
    /* If there is nothing to expire try next DB ASAP. */
    if ((num = dictSize(db->expires)) == 0) {
    ... 
    }
    slots = dictSlots(db->expires);
    now = mstime();

    if (num && slots > DICT_HT_INITIAL_SIZE &&
        (num*100/slots < 1)) break;
        ...
    if (num > ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP)
        num = ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP;

    while (num--) {
     ... 
    }
    /* Update the average TTL stats for this database. */
    if (ttl_samples) {
    ...
    }
    iteration++;
    if ((iteration & 0xf) == 0) { /* check once every 16 iterations. */
    ...
    }
    if (timelimit_exit) return;

} while (expired > ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP/4);

注意while循環(huán)條件陵究,ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP是我們每個循環(huán)希望查到的過期鍵的個數(shù),如果我們每次循環(huán)過后奥帘,被清理的過期鍵的個數(shù)超過了我們期望的四分之一铜邮,我們就會繼續(xù)這個循環(huán),因為這說明當(dāng)前數(shù)據(jù)庫中過期鍵的個數(shù)比較多寨蹋,需要繼續(xù)清理松蒜,如果沒有達(dá)到我們期望的四分之一,就跳出while循環(huán)钥庇,遍歷下一個數(shù)據(jù)庫牍鞠。

這個函數(shù)最核心的功能就是清除過期鍵,這個功能的實現(xiàn)就是在while(num--)這個循環(huán)里面评姨。

while (num--) {
    dictEntry *de;
    long long ttl;

    if ((de = dictGetRandomKey(db->expires)) == NULL) break;
    ttl = dictGetSignedIntegerVal(de)-now;
    if (activeExpireCycleTryExpire(db,de,now)) expired++;
    if (ttl < 0) ttl = 0;
    ttl_sum += ttl;
    ttl_samples++;
}

他先從數(shù)據(jù)庫中設(shè)置了過期時間的鍵的集合中隨機抽取一個鍵难述,然后調(diào)用activeExpireCycleTryExpire函數(shù)來判斷這個鍵是否過期,如果過期就刪除鍵吐句,activeExpireCycleTryExpire函數(shù)的源碼如下:

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));

        propagateExpire(db,keyobj);
        dbDelete(db,keyobj);
        notifyKeyspaceEvent(REDIS_NOTIFY_EXPIRED,
            "expired",keyobj,db->id);
        decrRefCount(keyobj);
        server.stat_expiredkeys++;
        return 1;
    } else {
        return 0;
    }
}

這個函數(shù)的邏輯很簡單胁后,就是先獲取鍵de的過期時間,和現(xiàn)在的時間比較嗦枢,如果過期攀芯,就生成該鍵de的對象,然后傳播該鍵de的過期信息文虏,并且刪除這個鍵侣诺,然后增加過期鍵總數(shù)。

最后就是控制函數(shù)運行時間的部分了氧秘,代碼如下:

/* We can't block forever here even if there are many keys to
 * expire. So after a given amount of milliseconds return to the
 * caller waiting for the other active expire cycle. */
iteration++;
if ((iteration & 0xf) == 0) { /* check once every 16 iterations. */
    long long elapsed = ustime()-start;

    latencyAddSampleIfNeeded("expire-cycle",elapsed/1000);
    if (elapsed > timelimit) timelimit_exit = 1;
}
if (timelimit_exit) return;     

這里有一個迭代次數(shù)的變量iteration年鸳,每迭代16次就來計算函數(shù)已經(jīng)運行的時間,如果這個時間超過了之前的限定時間timelimit丸相,就將timelimit_exit這個標(biāo)志置為1搔确,說明程序超時,需要強制退出了灭忠。

(3)懶惰淘汰策略:

1)含義:key過期的時候不刪除膳算,每次通過key獲取值的時候去檢查是否過期,若過期弛作,則刪除涕蜂,返回null。

2)優(yōu)點:刪除操作只發(fā)生在通過key取值的時候發(fā)生映琳,而且只刪除當(dāng)前key宇葱,所以對CPU時間的占用是比較少的瘦真,而且此時的刪除是已經(jīng)到了非做不可的地步(如果此時還不刪除的話,我們就會獲取到了已經(jīng)過期的key了)

3)缺點:若大量的key在超出超時時間后黍瞧,很久一段時間內(nèi),都沒有被獲取過原杂,那么可能發(fā)生內(nèi)存泄露(無用的垃圾占用了大量的內(nèi)存)

4)懶惰式策略刪除流程:

1. 在進(jìn)行g(shù)et或setnx等操作時印颤,先檢查key是否過期;

2. 若過期穿肄,刪除key年局,然后執(zhí)行相應(yīng)操作; 若沒過期咸产,直接執(zhí)行相應(yīng)操作矢否;

5)源碼閱讀:

在redis源碼中,實現(xiàn)懶惰淘汰策略的是函數(shù)expireIfNeeded脑溢,所有讀寫數(shù)據(jù)庫命令在執(zhí)行之前都會調(diào)用expireIfNeeded函數(shù)對輸入鍵進(jìn)行檢查僵朗。如果過期就刪除,如果沒過期就正常訪問屑彻。

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 */

    /* Don't expire anything while loading. It will be done later. */
    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. */
    /*如果我們正在slaves上執(zhí)行讀寫命令验庙,就直接返回,
     *因為slaves上的過期是由master來發(fā)送刪除命令同步給slaves刪除的社牲,
     *slaves不會自主刪除*/
    if (server.masterhost != NULL) return now > when;
    /*只是回了一個判斷鍵是否過期的值粪薛,0表示沒有過期,1表示過期
     *但是并沒有做其他與鍵值過期相關(guān)的操作*/

    /* Return when this key has not expired */
    /*如果沒有過期搏恤,就返回當(dāng)前鍵*/
    if (now <= when) return 0;

    /* Delete the key */
    /*增加過期鍵個數(shù)*/
    server.stat_expiredkeys++;
    /*傳播鍵過期的消息*/
    propagateExpire(db,key);
    notifyKeyspaceEvent(REDIS_NOTIFY_EXPIRED,"expired",key,db->id);
    /*刪除過期鍵*/
    return dbDelete(db,key);
}

以上是expireIfNeeded函數(shù)的源碼违寿,源碼中的注釋已經(jīng)很清楚的描述出了它的邏輯,我只是將他翻譯成中文熟空,然后加了一點自己的注釋藤巢。值得注意的如果是slaves,它是不能自主刪除鍵的,需要由master發(fā)del命令痛阻,然后同步到所有的slaves菌瘪,這樣就不會造成主從數(shù)據(jù)不一致的問題。

(4)策略總述:

懶惰淘汰機制和定時淘汰機制是一起合作的阱当,就好像你開一家餐館一樣俏扩,定時淘汰機制就是你每隔幾小時去查看所有的菜品是否還有,如果有的菜品現(xiàn)在賣光了弊添,就將他從菜單上劃掉录淡。懶惰淘汰機制就是有客人要點宮保雞丁,你馬上去查看還有沒有油坝,如果今天的已經(jīng)賣完了嫉戚,就告訴客人不好意思刨裆,我們賣完了,然后將宮保雞丁從菜單上劃掉彬檀。只有等下次有原料再做的時候帆啃,才又把它放到菜單上去。

所以窍帝,在實際中努潘,如果我們要自己設(shè)計過期策略,在使用懶漢式刪除+定期刪除時坤学,控制時長和頻率這個尤為關(guān)鍵疯坤,需要結(jié)合服務(wù)器性能,已經(jīng)并發(fā)量等情況進(jìn)行調(diào)整深浮,以致最佳压怠。


三、對開發(fā)需求而言飞苇,Redis過期策略的設(shè)計實現(xiàn)經(jīng)驗:代碼在此工程里

(1)分析緩存鍵值的客戶方角度菌瘫,調(diào)和服務(wù)器內(nèi)存壓力

基于服務(wù)器內(nèi)存是有限的,但是緩存是必須的玄柠,所以我們就要結(jié)合起來選擇一個平衡點突梦。所以一般來說,我們采取高訪問量緩存策略---就是給那些經(jīng)常被訪問的數(shù)據(jù)羽利,維持它較長的key生存周期宫患。

(2)估算過期時間

這個就要結(jié)合我們自己的業(yè)務(wù)去估量了。

參考因素:數(shù)據(jù)的訪問量这弧、并發(fā)量娃闲,數(shù)據(jù)的變化更新的時間,服務(wù)器數(shù)據(jù)內(nèi)存大小......

(3)Java演示一策略做法匾浪。

每次訪問刷新對應(yīng)key生存時間:

針對經(jīng)常訪問的數(shù)據(jù)的策略

//加進(jìn)redis時皇帮,設(shè)置生存時間
@Override
    public String set(String key, String value) {
        Jedis jedis = jedisPool.getResource();
        String string = jedis.set(key, value);
        jedis.expire(key,5);
        System.out.println("key :  "+key);
        System.out.println("查看key的剩余生存時間:"+jedis.ttl(key));
        jedis.close();
        return string;
    }
    //從redis獲取時
 @Override
    public String get(String key) {
        Jedis jedis = jedisPool.getResource();
        String string = jedis.get(key);
        jedis.expire(key,5);//每次訪問刷新時間
        jedis.close();
        return string;
    }

源碼下載:Redis系列(三)--過期策略的數(shù)據(jù)庫以及部分實現(xiàn)代碼

好了,Redis系列(三)--過期策略講完了蛋辈,這是redis使用優(yōu)化必須理解的原理属拾,這是積累的必經(jīng)一步,我會繼續(xù)出這個系列文章冷溶,分享經(jīng)驗給大家渐白。歡迎在下面指出錯誤,共同學(xué)習(xí)3哑怠纯衍!你的點贊是對我最好的支持!苗胀!

更多內(nèi)容襟诸,可以訪問JackFrost的博客

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瓦堵,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子歌亲,更是在濱河造成了極大的恐慌菇用,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件应结,死亡現(xiàn)場離奇詭異刨疼,居然都是意外死亡,警方通過查閱死者的電腦和手機鹅龄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來亭畜,“玉大人扮休,你說我怎么就攤上這事∷┩遥” “怎么了玷坠?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長劲藐。 經(jīng)常有香客問我八堡,道長,這世上最難降的妖魔是什么聘芜? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任兄渺,我火速辦了婚禮,結(jié)果婚禮上汰现,老公的妹妹穿的比我還像新娘挂谍。我一直安慰自己,他們只是感情好瞎饲,可當(dāng)我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布口叙。 她就那樣靜靜地躺著,像睡著了一般嗅战。 火紅的嫁衣襯著肌膚如雪妄田。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天驮捍,我揣著相機與錄音疟呐,去河邊找鬼。 笑死厌漂,一個胖子當(dāng)著我的面吹牛萨醒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播苇倡,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼富纸,長吁一口氣:“原來是場噩夢啊……” “哼囤踩!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起晓褪,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤堵漱,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后涣仿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體勤庐,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年好港,在試婚紗的時候發(fā)現(xiàn)自己被綠了愉镰。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡钧汹,死狀恐怖丈探,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情拔莱,我是刑警寧澤碗降,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站塘秦,受9級特大地震影響讼渊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜尊剔,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一爪幻、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧赋兵,春花似錦笔咽、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至历造,卻和暖如春甩十,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背吭产。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工侣监, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人臣淤。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓务荆,卻偏偏與公主長得像屿讽,于是被迫代替她去往敵國和親痘括。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 分布式緩存技術(shù)PK:選擇Redis還是Memcached钱慢? 經(jīng)平臺同意授權(quán)轉(zhuǎn)載 作者:田京昆(騰訊后臺研發(fā)工程師)...
    meng_philip123閱讀 68,933評論 7 60
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理逮京,服務(wù)發(fā)現(xiàn),斷路器束莫,智...
    卡卡羅2017閱讀 134,704評論 18 139
  • 轉(zhuǎn)載地址:http://gnucto.blog.51cto.com/3391516/998509 Redis與Me...
    Ddaidai閱讀 21,453評論 0 82
  • 本文為筆者對在學(xué)習(xí)Redis過程中所收集資料的一個總結(jié)懒棉,目的是為了以后方便回顧相關(guān)的知識,大部分為非原創(chuàng)內(nèi)容。特此...
    EakonZhao閱讀 14,449評論 0 9
  • 一只狗昨天叫喚了一夜览绿。從天剛黑叫到凌晨三點多中間一刻也沒停策严。可真是天生一副好嗓子饿敲。 這讓我想起前些日子每天步行走過...
    綠蘿寶貝閱讀 1,186評論 6 3