前言
✍Redis中有一個經典的問題,在巨大的數據量的情況下,做類似於查找符合某種規則的Key的信息,這裏就有兩種方式:
- keys命令,簡單粗暴,由於Redis單線程這一特性,keys命令是以阻塞的方式執行的,keys是以遍歷的方式實現的複雜度是 O(n),Redis庫中的key越多,查找實現代價越大,產生的阻塞時間越長。
- scan命令,以非阻塞的方式實現key值的查找,絕大多數情況下是可以替代keys命令的,可選性更強,推薦此方式。
✍ 指令示例
Redis 提供了一個簡單暴力的指令 keys 用來列出所有滿足特定正則字符串規則的 key。
查詢指令:
127.0.0.1:1>keys key*
查詢結果:
- “key3”
- “key6”
- “key1”
- “key7”
- “key9”
- “key8”
- “key4”
- “key2”
- “key5”
這個指令使用非常簡單,提供一個簡單的正則字符串即可,但是有很明顯的兩個缺點。
- 沒有 offset、limit 參數,一次性吐出所有滿足條件的 key,萬一實例中有幾百 w 個 key 滿足條件,當你看到滿屏的字符串刷的沒有盡頭時,你就知道難受了。
- keys 算法是遍歷算法,複雜度是 O(n),如果實例中有千萬級以上的 key,這個指令就會導致 Redis 服務卡頓,所有讀寫 Redis 的其它的指令都會被延後甚至會超時報錯,因爲 Redis 是單線程程序,順序執行所有指令,其它指令必須等到當前的 keys 指令執行完了纔可以繼續。
- 建議生產環境屏蔽keys命令;
✍Redis 爲了解決這個問題,它在 2.8 版本中加入了指令 scan 指令
✍ scan 相比 keys 具備有以下特點:
- 複雜度雖然也是 O(n),但是它是通過遊標分步進行的,分次進行不會阻塞線程;
- 提供 limit 參數,可以控制每次返回結果的最大條數,limit 只是對增量式迭代命令的一種提示(hint),返回的結果可多可少;
- 同 keys 一樣,它也提供模式匹配功能;
- 服務器不需要爲遊標保存狀態,遊標的唯一狀態就是 scan 返回給客戶端的遊標整數;
- 返回的結果可能會有重複,需要客戶端去重,這點非常重要;
- 遍歷的過程中如果有數據修改,改動後的數據能不能遍歷到是不確定的;
- 單次返回的結果是空的並不意味着遍歷結束,而要看返回的遊標值是否爲零.
✍ scan 基礎使用
語法:SCAN cursor [MATCH pattern] [COUNT count]
初始執行scan命令例如scan 0。SCAN命令是一個基於遊標的迭代器。
這意味着命令每次被調用都需要使用上一次這個調用返回的遊標作爲該次調用的遊標參數,以此來延續之前的迭代過程。當SCAN命令的遊標參數被設置爲0時,服務器將開始一次新的迭代,而當redis服務器向用戶返回值爲0的遊標時,表示迭代已結束,這是唯一迭代結束的判定方式,而不能通過返回結果集是否爲空判斷迭代結束。
- scan 參數提供了三個參數,第一個是 cursor 整數值,第二個是 key 的正則模式,第三個是遍歷的 limit hint。
- 第一次遍歷時,cursor 值爲 0,然後將返回結果中第一個整數值作爲下一次遍歷的 cursor。
- 一直遍歷到返回的 cursor 值爲 0 時結束。
查詢指令:
127.0.0.1:1>scan 0 match key* count 20
查詢結果:
查詢指令:
127.0.0.1:1>scan 21 match key* count 20
查詢結果:
返回結果分爲兩個部分:
- 第一部分即 1) :下一次迭代遊標;
- 第二部分即 2) :本次迭代結果集。
注意: limit值20 不是限定返回結果的數量,而是限定服務器單次遍歷的字典槽位數量(約等於)。
✍ scan 其他指令
scan 指令是一系列指令,除了可以遍歷所有的 key 之外,還可以對指定的容器集合進行遍歷。
- zscan 遍歷 zset 集合元素;
- hscan 遍歷 hash 字典的元素;
- sscan 遍歷 set 集合的元素。
✍ 設置redis的key值的注意事項
平時業務開發中,應儘量避免大key的產生
原因: 有時候會因爲業務人員使用不當,在 Redis 實例中會形成很大的對象,比如一個很大的 hash,一個很大的 zset 這都是經常出現的。這樣的對象對 Redis 的集羣數據遷移帶來了很大的問題,因爲在集羣環境下,如果某個 key 太大,會讓數據導致遷移卡頓。另外在內存分配上,如果一個 key 太大,那麼當它需要擴容時,會一次性申請更大的一塊內存,這也會導致卡頓。如果這個大 key 被刪除,內存會一次性回收,卡頓現象會再一次產生。