採坑記錄-Redis使用scan代替keys

[提前聲明]
文章由作者:張耀峯 結合自己生產中的使用經驗整理,最終形成簡單易懂的文章
寫作不易,轉載請註明,謝謝!
spark代碼案例地址: https://github.com/Mydreamandreality/sparkResearch


線上問題
  • 定時任務通過keys* 通配符匹配對應的key
  • 在這段時間內的其它服務(需要用到Redis)告警,無法進行正常服務
  • 在運維平臺查看日誌:服務告警這段時間內的請求全部拋出redis連接超時,服務中設置的redis超時時間爲200ms
問題定位
  • 首先確定是keys命令引起的
  • 而且Redis中數據量要特別大,否則是不會觸發這個bug的,之前數據量小的時候根本沒有這個問題
  • 其次keys命令本身在redis中數據量特別大的情況下(百萬及以上)就會特別慢
  • 最致命的是這個命令會阻塞redis多路複用的io主線程,如果這個線程被阻塞了,在這個期間其它服務到redis的請求會全部被阻塞,導致一系列反應,響應卡頓,連接超時等等
問題解決
  • 在線上環境,這種會阻塞主線程的操作,並且算法時間複雜度是O(n)的就應該禁止使用
替代方案
  • redis.scan
scan如何解決線上問題
  • scan和keys都是O(n)複雜度
  • scan同樣支持通配字符模糊查找
  • scan不會阻塞主線程
  • scan支持遊標按批次迭代返回數據
    注:scan返回的數據有可能會重複,需要客戶端主動去重
scan注意事項
  • scan命令的遊標從0開始從0結束
  • scan命令每次返回的數據都會攜帶下次遊標所需的值,根據這個值再進行下一次的訪問,如果返回的數據爲空,不一定是沒有數據了,需要通過遊標來確認
scan命令使用案例

scan 0 match my*key count 10000

JavaRedisApi-scan命令使用案例
    public Set<String> scan(String key, long count) {
        Set<String> keys = redisTemplate.execute(
                (RedisCallback<Set<String>>) connection -> {
                    Set<String> keyTmp = new HashSet<>();
                    Cursor<byte[]> cursor = connection.scan(new ScanOptions.ScanOptionsBuilder().match(key + "*").count(count).build());
                    while (cursor.hasNext()) {
                        keyTmp.add(new String(cursor.next()));
                    }
                    return keyTmp;
                });
        return keys;
    }
迴歸測試結果
  • 服務正常執行
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章