Redis遍歷所有key的兩個命令 -- KEYS 和 SCAN
當我們需要遍歷Redis所有key或者指定模式的key時,首先想到的是KEYS命令:
KEYS pattern
KEYS
, 你最好還是用 Redis 的集合結構 SETS 來代替。KEYS命令使用很簡單。
- redis> MSET one 1 two 2 three 3 four 4
- OK
- redis> KEYS *o*
- 1) "four"
- 2) "one"
- 3) "two"
- redis> KEYS t??
- 1) "two"
- redis> KEYS *
- 1) "four"
- 2) "three"
- 3) "one"
- 4) "two"
- redis>
SCAN cursor [MATCH pattern] [COUNT count]
SCAN 每次執行都只會返回少量元素,所以可以用於生產環境,而不會出現像 KEYS 或者 SMEMBERS 命令帶來的可能會阻塞服務器的問題。
SCAN命令是一個基於遊標的叠代器。這意味著命令每次被調用都需要使用上一次這個調用返回的遊標作為該次調用的遊標參數,以此來延續之前的叠代過程
當SCAN命令的遊標參數(即cursor)被設置為 0 時, 服務器將開始一次新的叠代, 而當服務器向用戶返回值為 0 的遊標時, 表示叠代已結束。
簡單的叠代演示:
- redis 127.0.0.1:6379> scan 0
- 1) "17"
-
2) 1) "key:12"
- 2) "key:8"
- 3) "key:4"
- 4) "key:14"
- 5) "key:16"
- 6) "key:17"
- 7) "key:15"
- 8) "key:10"
- 9) "key:3"
- 10) "key:7"
- 11) "key:1"
- redis 127.0.0.1:6379> scan 17
- 1) "0"
- 2) 1) "key:5"
- 2) "key:18"
- 3) "key:0"
- 4) "key:2"
- 5) "key:19"
- 6) "key:13"
- 7) "key:6"
- 8) "key:9"
- 9) "key:11"
在上面這個例子中, 第一次叠代使用 0 作為遊標, 表示開始一次新的叠代。第二次叠代使用的是第一次叠代時返回的遊標 17 ,作為新的叠代參數 。
顯而易見,SCAN命令的返回值 是一個包含兩個元素的數組, 第一個數組元素是用於進行下一次叠代的新遊標, 而第二個數組元素則又是一個數組, 這個數組中包含了所有被叠代的元素。
註意:返回的遊標不一定是遞增的,可能後一次返回的遊標比前一次的小。
在第二次調用 SCAN 命令時, 命令返回了遊標 0 , 這表示叠代已經結束, 整個數據集已經被完整遍歷過了。
full iteration :以 0 作為遊標開始一次新的叠代, 一直調用 SCAN 命令, 直到命令返回遊標 0 , 我們稱這個過程為一次完整遍歷。
SCAN增量式叠代命令並不保證每次執行都返回某個給定數量的元素,甚至可能會返回零個元素, 但只要命令返回的遊標不是 0 , 應用程序就不應該將叠代視作結束。
不過命令返回的元素數量總是符合一定規則的, 對於一個大數據集來說, 增量式叠代命令每次最多可能會返回數十個元素;而對於一個足夠小的數據集來說,可能會一次叠代返回所有的key
COUNT選項
對於增量式叠代命令不保證每次叠代所返回的元素數量,我們可以使用COUNT選項, 對命令的行為進行一定程度上的調整。COUNT 選項的作用就是讓用戶告知叠代命令, 在每次叠代中應該從數據集裏返回多少元素。使用COUNT 選項對於對增量式叠代命令相當於一種提示, 大多數情況下這種提示都比較有效的控制了返回值的數量。
註意:COUNT選項並不能嚴格控制返回的key數量,只能說是一個大致的約束。並非每次叠代都要使用相同的 COUNT 值,用戶可以在每次叠代中按自己的需要隨意改變 COUNT 值, 只要記得將上次叠代返回的遊標用到下次叠代裏面就可以了。MATCH 選項
類似於KEYS 命令,增量式叠代命令通過給定 MATCH 參數的方式實現了通過提供一個 glob 風格的模式參數, 讓命令只返回和給定模式相匹配的元素。
MATCH 選項對元素的模式匹配工作是在命令從數據集中取出元素後和向客戶端返回元素前的這段時間內進行的, 所以如果被叠代的數據集中只有少量元素和模式相匹配, 那麽叠代命令或許會在多次執行中都不返回任何元素。
以下是這種情況的一個例子:
- redis 127.0.0.1:6379> scan 0 MATCH *11*
- 1) "288"
- 2) 1) "key:911"
- redis 127.0.0.1:6379> scan 288 MATCH *11*
- 1) "224"
- 2) (empty list or set)
- redis 127.0.0.1:6379> scan 224 MATCH *11*
- 1) "80"
- 2) (empty list or set)
- redis 127.0.0.1:6379> scan 80 MATCH *11*
- 1) "176"
- 2) (empty list or set)
- redis 127.0.0.1:6379> scan 176 MATCH *11* COUNT 1000
- 1) "0"
- 2) 1) "key:611"
- 2) "key:711"
- 3) "key:118"
- 4) "key:117"
- 5) "key:311"
- 6) "key:112"
- 7) "key:111"
- 8) "key:110"
- 9) "key:113"
- 10) "key:211"
- 11) "key:411"
- 12) "key:115"
- 13) "key:116"
- 14) "key:114"
- 15) "key:119"
- 16) "key:811"
- 17) "key:511"
- 18) "key:11"
- redis 127.0.0.1:6379>
- $redis = new Redis();
- $redis->connect(‘127.0.0.1‘, 6379);
- /* 設置遍歷的特性為不重復查找,該情況下擴展只會scan一次,所以可能會返回空集合 */
- $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY);
- $it = NULL;
- $pattern = ‘*‘;
- $count = 50; // 每次遍歷50條,註意是遍歷50條,遍歷出來的50條key還要去匹配你的模式,所以並不等於就能夠取出50條key
- do
- {
- $keysArr = $redis->scan($it, $pattern, $count);
- if ($keysArr)
- {
- foreach ($keysArr as $key)
- {
- echo $key . "\n";
- }
- }
- } while ($it > 0); //每次調用 Scan會自動改變 $it 值,當$it = 0時 這次遍歷結束 退出循環
- echo ‘---------------------------------------------------------------------------------‘ . "\n";
- /* 設置擴展在一次scan沒有查找出記錄時 進行重復的scan 直到查詢出結果或者遍歷結束為止 */
- $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
- $it = NULL;
- $pattern = ‘*‘;
- $count = 50; // 每次遍歷50條,註意是遍歷50條,遍歷出來的50條key還要去匹配你的模式,所以並不等於就能夠取出50條key
- //這種用法下我們只需要簡單判斷返回結果是否為空即可, 如果為空說明遍歷結束
- while ($keysArr = $redis->scan($it, $pattern, $count))
- {
- foreach ($keysArr as $key)
- {
- echo $key . "\n";
- }
- }
- [root@localhost php]# /usr/local/php/bin/php scan.php
- bm
- bm2
- h1
- name
- bit
- bm1
- places
- cities
- hhl
- ---------------------------------------------------------------------------------
- bm
- bm2
- h1
- name
- bit
- bm1
- places
- cities
- hhl
Redis遍歷所有key的兩個命令 -- KEYS 和 SCAN