記一次redis cpu 100%的緊急bug

前段時間遇到一個棘手問題,mq消費速度突然從30/s變成1/s,而且還持續了很長的一段時間,嚇得我趕緊動手排查問題。但是mq消費速度慢的原因,可能有很多種(例如mysql、jvm、redis、mq、代碼問題等)。爲了可以快速定位問題點,博主直接通過Arthas工具進行問題的排查和追蹤。

Arthas用法可以參照:https://mp.weixin.qq.com/s/FT1gKYDzczWa05xYm-z0Vw

image

通過Arthas的trace命令,可以分析方法中每一行代碼的執行時間,分析得到方法中最耗時的竟然是redis,看到這個結果差點沒臥槽出來,redis怎麼可能會那麼慢呢。因爲公司使用的是騰訊雲的redis,所以可以直接在控制檯上面看到redis各個監控指標,博主火速打開控制檯,然後就看到如下所示cpu持續不斷的飆到100%。

image

那會是什麼操作引起的呢?我們首先要清楚一般什麼情況下會導致redis的cpu飆升到100%。導致cpu飆升的原因,一般可能會有如下幾種:

  1. redis從庫大批量同步到redis主庫
  2. redis請求的qps過高
  3. redis存在大量慢查詢

因爲系統redis目前是單例,所以不存在第一種的情況。至於第二種,從騰訊雲控制檯中看到redis總請求的量,發現cpu高的那個時間段,總請求數並沒有飆升,所以也不是第二種可能。至於第三種可能性,如下所示,發現了很多keys *report:requestId:*的命令出現。

image

到了這一步基本可以確定是因爲keys *report:requestId:*引起的,但是爲了出於嚴謹考慮,我們在測試環境多次請求keys *report:requestId:*命令,發現果然cpu直接飆升上去了。原來罪魁禍首是keys模糊搜索引起的啊,那我們要如何解決這個問題呢?

我們可以用scan來替代keys命令,java代碼如下所示:

Set<String> keys = (Set<String>) redisTemplateForRequestId.execute((RedisCallback<Set<String>>) connection -> {
            Set<String> keysTmp = new HashSet<>();
            Cursor<byte[]> cursor = connection.scan(new ScanOptions.ScanOptionsBuilder().match(multiRedisKey + "*").count(100).build());
            while (cursor.hasNext()) {
                keysTmp.add(new String(cursor.next()));
            }
            return keysTmp;
        });

優化之後可以明顯看到,redis的超過2s的命令都不存在了,cpu也恢復到之前平緩的狀態了。所以以後在寫redis的api時候要慎用甚至不用keys命令,如果數據量特別大的情況下,嚴重可能會導致redis持續性宕機。

大家是不是以爲到這邊就結束了呢?不!不管使用keys還是scan都比一般的命令耗時。如果我們想要查詢的命令可以通過枚舉一一列出來(數量不大,允許一一枚舉),那我們一定要用枚舉來查詢,這樣效率會比用scan高很多(有點類似數據庫的like和=操作)。所以最後代碼博主優化爲:

redisTemplate.opsForValue().multiGet("list集合,裏面存儲着redis的key值")

之所以能這麼快定位到redis對應的問題,很大一部分是因爲騰訊雲給我們提供了很多便捷式的查詢。如果你的redis沒有這些指標監控呢,你又該如何監控這些指標呢?哈哈,後面有機會給大家介紹。

想要更多幹貨、技術猛料的孩子,快點拿起手機掃碼關注我,我在這裏等你哦~

林老師帶你學編程https://wolzq.com

發佈了236 篇原創文章 · 獲贊 1343 · 訪問量 202萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章