<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          一個 randomkey 命令導(dǎo)致的 Redis 事故。。

          共 2707字,需瀏覽 6分鐘

           ·

          2020-09-21 10:59

          Java技術(shù)棧

          www.javastack.cn

          關(guān)注閱讀更多優(yōu)質(zhì)文章



          最近在公司對redis做一些二次開發(fā)時,發(fā)現(xiàn)一個randomkey命令可能導(dǎo)致整個redis實例長時間阻塞的問題,redis版本為3.2.9,以此記錄。

          問題

          由于我們公司使用的是redis集群版Codis,Codis內(nèi)置的redis版本比較低,為3.2.9版本。

          我們近期在做Codis雙機(jī)房時,需要對redis增加一些功能以此支持雙機(jī)房,在開發(fā)和測試中發(fā)現(xiàn),執(zhí)行randomkey命令有可能導(dǎo)致整個redis長時間阻塞的問題。

          randomkey主要功能是在redis中隨機(jī)返回一個key出來,它隨機(jī)選取key的代碼如下。

          robj?*dbRandomKey(redisDb?*db)?{
          ????dictEntry?*de;

          ????//?死循環(huán)從哈希表中找到一個不過期的key
          ????while(1)?{
          ????????sds?key;
          ????????robj?*keyobj;

          ????????//?從實例的哈希表里隨機(jī)一個元素
          ????????de?=?dictGetRandomKey(db->dict);
          ????????if?(de?==?NULL)?return?NULL;

          ????????//?獲取這個元素的key
          ????????key?=?dictGetKey(de);
          ????????keyobj?=?createStringObject(key,sdslen(key));
          ????????//?如果key已經(jīng)過期?則把這個key從實例中刪除
          ????????//?注意:expireIfNeeded對于過期的key只針對master有效
          ????????//?如果是slave則永遠(yuǎn)不會刪除key
          ????????if?(dictFind(db->expires,key))?{
          ????????????if?(expireIfNeeded(db,keyobj))?{
          ????????????????decrRefCount(keyobj);
          ????????????????continue;
          ????????????}
          ????????}
          ????????return?keyobj;
          ????}
          }?

          從上面代碼可以看出來,如果當(dāng)前是個slave,并且整個實例中存在大量已經(jīng)過期的key(key已過期,但redis還未來得及刪除key),執(zhí)行randomkey命令時,由于找不到不過期的key,那么這個邏輯就會陷入死循環(huán),阻塞住整個實例,整個實例不可用。

          如果當(dāng)前是master,執(zhí)行randomkey命令時,redis會一直隨機(jī)選擇key,直到找到一個不過期的key,同時會把已經(jīng)過期的key從整個實例中刪除。

          也就是說,在這種場景下,雖然不會長時間阻塞整個實例,但也會比執(zhí)行一個普通的命令耗時要久。如果你在一個大量已過期的實例上執(zhí)行randomkey命令,那可能會導(dǎo)致業(yè)務(wù)訪問redis變慢。

          解決

          我們對比了官方最新版的redis,已經(jīng)針對此問題進(jìn)行了修復(fù)。

          robj?*dbRandomKey(redisDb?*db)?{
          ????dictEntry?*de;
          ????//?當(dāng)前實例全部都是過期key?最大循環(huán)100次
          ????int?maxtries?=?100;
          ????int?allvolatile?=?dictSize(db->dict)?==?dictSize(db->expires)

          ????//?死循環(huán)從哈希表中找到一個不過期的key
          ????while(1)?{
          ????????sds?key;
          ????????robj?*keyobj;

          ????????//?從實例的哈希表里隨機(jī)一個元素
          ????????de?=?dictGetRandomKey(db->dict);
          ????????if?(de?==?NULL)?return?NULL;

          ????????//?獲取這個元素的key
          ????????key?=?dictGetKey(de);
          ????????keyobj?=?createStringObject(key,sdslen(key));
          ????????//?如果key已經(jīng)過期?則把這個key從實例中刪除
          ????????//?注意:expireIfNeeded對于過期的key只針對master有效
          ????????//?如果是slave則永遠(yuǎn)不會刪除key
          ????????if?(dictFind(db->expires,key))?{
          ????????????//?如果整個實例都是過期key?在slave上執(zhí)行此命令最多循環(huán)100次?避免長時間阻塞
          ????????????if?(allvolatile?&&?server.masterhost?&&?--maxtries?==?0)?{
          ????????????????return?keyobj;
          ????????????}
          ????????????if?(expireIfNeeded(db,keyobj))?{
          ????????????????decrRefCount(keyobj);
          ????????????????continue;
          ????????????}
          ????????}
          ????????return?keyobj;
          ????}
          }?

          解決方案就是增加一個最大重試次數(shù),如果整個實例都是過期key,那么最多尋找maxtries次就返回,避免阻塞整個實例。

          注意點

          但要注意的是,如果達(dá)到了maxtries,那么返回的key是已經(jīng)過期的key,你雖然在randomkey中看到了這個key,但對這個key執(zhí)行其他命令時,還是拿不到這個key的。

          這個方案只針對slave上執(zhí)行這個命令進(jìn)行了修復(fù),也就是不會再讓redis陷入死循環(huán)。

          但在master上執(zhí)行這個命令還是會發(fā)生上述的變慢問題,如果你在使用redis時,經(jīng)常使用這個命令,同時實例中存在大量已經(jīng)過期的key,那么redis變慢很有可能是這個問題導(dǎo)致的。

          最后,歡迎大家關(guān)注公眾號Java技術(shù)棧獲取更多Redis系列教程。

          作者:Kaito
          鏈接:kaito-kidd.com/2020/06/25/redis-randomkey-issue/





          關(guān)注Java技術(shù)??锤喔韶?/strong>



          戳原文,獲取更多福利!
          瀏覽 48
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  波多野结衣小视频 | 高清无码人妻 | 殴美成人精品 | 9l视频自拍九色9l视频成人 | 手机在线看片日韩 |