日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

redis查询所有key命令_想在生产搞事情?那试试这些 Redis 命令

發布時間:2023/12/10 数据库 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 redis查询所有key命令_想在生产搞事情?那试试这些 Redis 命令 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

作者:鴨血粉絲

出自:Java極客技術

原文:mp.weixin.qq.com/s/WeAamgYYGQfxlsppsn9_lg


哎,最近阿粉又雙叒叕犯事了。

事情是這樣的,前一段時間阿粉公司生產交易偶發報錯,一番排查下來最終原因是因為 Redis 命令執行超時。

可是令人不解的是,生產交易僅僅使用 Redis set 這個簡單命令,這個命令講道理是不可能會執行這么慢。

那到底是什么導致這個問題那?

為了找出這個問題,我們查看分析了一下 Redis 最近的慢日志,最終發現耗時比較多命令為 keys XX*

看到這個命令操作的鍵的前綴,阿粉才發現這是自己負責的應用。可是阿粉排查一下,雖然自己的代碼并沒有主動去使用 keys命令,但是底層使用框架卻在間接使用,于是就有了今天這個問題。

問題原因

阿粉負責的應用是一個管理后臺應用,權限管理使用 Shiro 框架,由于存在多個節點,需要使用分布式 Session,于是這里使用 Redis 存儲 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,而 Redis 我們都知道它采用的是單線程模型。

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

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

  • 發送命令
  • 命令排隊
  • 執行命令
  • 返回結果
  • 由于 Redis 單線程執行命令,只能順序從隊列取出任務開始執行。

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

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

    KEYS 原理

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

    回答這個問題之前,請大家回想一下 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

    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 使用單線程執行操作命令,所有客戶端發送過來命令,Redis 都會現放入隊列,然后從隊列中順序取出執行相應的命令。

    如果任一任務執行過慢,就會影響隊列中其他任務的,這樣在外部客戶端看來,遲遲拿不到 Redis 的響應,看起來就很阻塞了一樣。

    所以不要在生產執行 keys、smembers、hgetall、zrange這類可能造成阻塞的指令,如果真需要執行,可以使用相應的scan 命令漸進式遍歷,可以有效防止阻塞問題。

    總結

    以上是生活随笔為你收集整理的redis查询所有key命令_想在生产搞事情?那试试这些 Redis 命令的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。