91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

在生產時不要使用哪些redis指令

發布時間:2021-11-20 17:14:43 來源:億速云 閱讀:159 作者:小新 欄目:大數據

這篇文章將為大家詳細講解有關在生產時不要使用哪些redis指令,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

問題原因

小黑哥負責的應用是一個管理后臺應用,權限管理使用 Shiro 框架,由于存在多個節點,需要使用分布式 Session,于是這里使用 Redis 存儲 Session 信息。

畫外音:不知道分布式 Session ,可以看看小黑哥之前寫的 一口氣說出 4 種分布式一致性 Session 實現方式,面試杠杠的~

由于 Shiro 并沒有直接提供 Redis 存儲 Session 組件,小黑哥不得不使用 Github 一個開源組件 shiro-redis。

由于 Shiro 框架需要定期驗證 Session 是否有效,于是 Shiro 底層將會調用 SessionDAO#getActiveSessions 獲取所有的 Session 信息。

shiro-redis 正好繼承 SessionDAO 這個接口,底層使用用 keys 命令查找 Redis 所有存儲的 Session key。

public Set<byte[]> keys(byte[] pattern){
    checkAndInit();
    Set<byte[]> keys = null;
    Jedis jedis = jedisPool.getResource();
    try{
        keys = jedis.keys(pattern);
    }finally{
        jedis.close();
    }
    return keys;
}

找到問題原因,解決辦法就比較簡單了,github 上查找到解決方案,升級一下 shiro-redis 到最新版本。

在這個版本,shiro-redis 采用 scan命令代替 keys,從而修復這個問題。

public Set<byte[]> keys(byte[] pattern) {
    Set<byte[]> keys = null;
    Jedis jedis = jedisPool.getResource();

    try{
        keys = new HashSet<byte[]>();
        ScanParams params = new ScanParams();
        params.count(count);
        params.match(pattern);
        byte[] cursor = ScanParams.SCAN_POINTER_START_BINARY;
        ScanResult<byte[]> scanResult;
        do{
            scanResult = jedis.scan(cursor,params);
            keys.addAll(scanResult.getResult());
            cursor = scanResult.getCursorAsBytes();
        }while(scanResult.getStringCursor().compareTo(ScanParams.SCAN_POINTER_START) > 0);
    }finally{
        jedis.close();
    }
    return keys;

}

雖然問題成功解決了,但是小黑哥心里還是有點不解。

為什么 keys 指令會導致其他命令執行變慢?

為什么 Keys 指令查詢會這么慢?

為什么 Scan 指令就沒有問題?

Redis 執行命令的原理

首先我們來看第一個問題,為什么 keys 指令會導致其他命令執行變慢?

回答這個問題,我們首先看下 Redis 客戶端執行一條命令的情況:

在生產時不要使用哪些redis指令

站在客戶端的視角,執行一條命令分為三步:

  1. 發送命令

  2. 執行命令

  3. 返回結果

但是這僅僅客戶端自己以為的過程,但是實際上同一時刻,可能存在很多客戶端發送命令給 Redis,而 Redis 我們都知道它采用的是單線程模型。

為了處理同一時刻所有的客戶端的請求命令,Redis 內部采用了隊列的方式,排隊執行。

在生產時不要使用哪些redis指令

于是客戶端執行一條命令實際需要四步:

  1. 發送命令

  2. 命令排隊

  3. 執行命令

  4. 返回結果

由于 Redis 單線程執行命令,只能順序從隊列取出任務開始執行。

只要 3 這個過程執行命令速度過慢,隊列其他任務不得不進行等待,這對外部客戶端看來,Redis 好像就被阻塞一樣,一直得不到響應。

所以使用 Redis 過程切勿執行需要長時間運行的指令,這樣可能導致 Redis 阻塞,影響執行其他指令。

KEYS 原理

接下來開始回答第二個問題,為什么 Keys 指令查詢會這么慢?

回答這個問題之前,請大家回想一下 Redis 底層存儲結構。

不太清楚朋友的也沒關系,大家可以回看一下小黑哥之前的文章「阿里面試官:HashMap 熟悉吧?好的,那就來聊聊 Redis 字典吧!」。

這里小黑哥復制之前文章內容,Redis 底層使用字典這種結構,這個結構與 Java HashMap 底層比較類似。

在生產時不要使用哪些redis指令

keys命令需要返回所有的符合給定模式 pattern 的 Redis 中鍵,為了實現這個目的,Redis 不得不遍歷字典中 ht[0]哈希表底層數組,這個時間復雜度為 O(N)(N 為 Redis 中 key 所有的數量)。

如果 Redis 中 key 的數量很少,那么這個執行速度還是也會很快。等到 Redis key 的數量慢慢更加,上升到百萬、千萬、甚至上億級別,那這個執行速度就會很慢很慢。

下面是小黑哥本地做的一次實驗,使用 lua 腳本往 Redis 中增加 10W 個 key,然后使用 keys 查詢所有鍵,這個查詢大概會阻塞十幾秒的時間。

eval "for i=1,100000  do redis.call('set',i,i+1) end" 0

這里小黑哥使用 Docker 部署 Redis,性能可能會稍差。

SCAN 原理

最后我們來看下第三個問題,為什么 scan 指令就沒有問題?

這是因為 scan命令采用一種黑科技-基于游標的迭代器

每次調用 scan 命令,Redis 都會向用戶返回一個新的游標以及一定數量的 key。下次再想繼續獲取剩余的 key,需要將這個游標傳入 scan 命令, 以此來延續之前的迭代過程。

簡單來講,scan 命令使用分頁查詢 redis 。

下面是一個 scan 命令的迭代過程示例:

scan 命令使用游標這種方式,巧妙將一次全量查詢拆分成多次,降低查詢復雜度。

雖然 scan 命令時間復雜度與 keys一樣,都是 O(N),但是由于 scan 命令只需要返回少量的 key,所以執行速度會很快。

最后,雖然scan 命令解決 keys不足,但是同時也引入其他一些缺陷:

  • 同一個元素可能會被返回多次,這就需要我們應用程序增加處理重復元素功能。

  • 如果一個元素在迭代過程增加到 redis,或者說在迭代過程被刪除,那個這個元素會被返回,也可能不會。

以上這些缺陷,在我們開發中需要考慮這種情況。

除了 scan以外,redis 還有其他幾個用于增量迭代命令:

  • sscan:用于迭代當前數據庫中的數據庫鍵,用于解決 smembers 可能產生阻塞問題

  • hscan命令用于迭代哈希鍵中的鍵值對,用于解決 hgetall 可能產生阻塞問題。

  • zscan:命令用于迭代有序集合中的元素(包括元素成員和元素分值),用于產生 zrange 可能產生阻塞問題。

關于“在生產時不要使用哪些redis指令”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

辰溪县| 什邡市| 高邮市| 全南县| 西贡区| 玛多县| 志丹县| 克拉玛依市| 德令哈市| 射洪县| 扎兰屯市| 曲阳县| 道孚县| 太湖县| 伊吾县| 临清市| 禄丰县| 通榆县| 临城县| 福安市| 永寿县| 九龙坡区| 缙云县| 九寨沟县| 普安县| 韩城市| 蒙山县| 汉源县| 东兰县| 个旧市| 琼海市| 新泰市| 句容市| 静乐县| 台湾省| 澄迈县| 会昌县| 资讯| 社旗县| 达日县| 阳东县|