@這是小豪的第十二篇文章
上篇文章 釋出後,在公眾號下方的留言中看到了這麼一句話
"keys 操作有點恐怖哈"
,當時就在想為啥恐怖,有點不知所以然,也沒過多的去管他,直到今天在社群文章中又看到了人家的提醒"keys 操作是不行的"
.... 哈哈,需要去了解為什麼啦!
keys 是什麼
- keys - KEYS PATTERN
用於查詢所有符合給定模式 pattern 的 key
為什麼會用到 keys
$redis->keys('login:201903*')
$redis->bitop('AND', 'monthActivities'', $redis->keys('login:201903*'));
echo "連續一個月簽到使用者數量:" . $redis->bitCount('monthActivities');
這是上篇文章中用到 keys
的地方,當時是為了統計連續一個月簽到使用者數量,通過 keys('login:201903') 獲取到三月所有 key
,然後加以聚合統計。
keys 使用會造成的後果
大家知道 Redis 是單執行緒程式,是按照順序執行指令的,如果說我們現在正在執行 keys
命令,那麼其它指令必須等到當前的 keys
指令執行完了才可以繼續,再加上 keys
操作是遍歷演算法,複雜度是 O(n),乍一想就知道問題所在了,當例項中資料量過大的時候,Redis
服務可能會卡頓,其餘指令可能會延時甚至超時報錯....
再者 keys
中沒有 offset、limit 引數,如果說滿足查詢條件的 keys
特別多,那就有點尷尬了,哈哈。
所以說官方的建議是:生產環境遮蔽掉 keys
命令。
替代方案 scan
說了那麼多,這也不行那也不好的,究竟怎麼辦呢,Redis 為了解決這個問題,它在 2.8 版本中加入了指令:scan。
好,那我們現在就來看一下這個命令:
- scan - cursor [MATCH pattern] [COUNT count]
用於迭代當前資料庫中的資料庫鍵
相比 keys
,我們來看一下 scan
的特點:
- 複雜度雖然也是 O(n),但是它是通過遊標分步進行的,不會阻塞執行緒;
- 提供 limit 引數,可以控制每次返回結果的最大條數,limit 只是對增量式迭代命令的一種提示 (hint),返回的結果可多可少;
- 同 keys 一樣,它也提供模式匹配功能;
- 伺服器不需要為遊標儲存狀態,遊標的唯一狀態就是 scan 返回給客戶端的遊標整數;
- 返回的結果可能會有重複,需要客戶端去重複,這點非常重要;
- 遍歷的過程中如果有資料修改,改動後的資料能不能遍歷到是不確定的;
- 單次返回的結果是空的並不意味著遍歷結束,而要看返回的遊標值是否為零
現在我們來實踐一下:
> keys 201903*
1) "login:20190311"
2) "login:20190312"
3) "login:20190313"
> scan 0 match login:201903*
1) "0"
2) 1) "login:20190313"
2) "login:20190311"
3) "login:20190312"
> scan 0 match login:201903* count 2
1) "5"
2) 1) "login:20190313"
2) "login:20190311"
看到這裡估計有點蒙圈,scan 0
是個啥意思,為啥下面的結果中第一個資料有的為 0 ,有的為 5。
其實是這樣的,當我們第一次遍歷查詢時,cursor 值為 0,如果說資料全部查詢完畢,那麼返回結果的第一個資料就為 0 表示查詢完畢,如果說返回的不為 0 ,那麼就需要將這個資料作為下一次遍歷的 cursor,也就是現在的 scan 5
,一直遍歷到返回的第一個資料為 0 為止。第二個引數一目瞭然哈,就是控制返回數量,那究竟是不是這樣呢,我們來看一下:
> scan 0
1) "0"
2) 1) "age"
2) "login:20190313"
3) "names"
4) "login:20190311"
5) "name"
6) "login:20190312"
7) "sex"
8) "ages"
> scan 0 match login:201903* count 2
1) "1"
2) 1) "login:20190313"
現在問題就來了,不是 count 2 嗎。怎麼查詢出來的資料只有 1 條,是不是壞了?不是滴,哈哈。這是因為這個 count 不是限定返回結果的數量,而是限定伺服器單次遍歷的字典槽位數量(約等於),所以就明白了吧。它查出來是空的也不要擔心,只要結果的第一個資料不為 0 就繼續遍歷迴圈下去。
說到這裡,大家應該對,scan
有了大致的瞭解了吧,那我們就回歸到之前的問題,統計該如何修改呢,大家來看一下基本的查詢程式碼:
\dd($redis->scan(0, 'match', 'login:201903*', 'count', 1000));
來看一下結果:
就這樣就 ok 啦,至於統計程式碼該如何改,我就沒寫出來噠,大家可以自己思考一下怎麼去寫,然後貼在評論區,哈哈。
結束語
至此 keys
和 scan
的講解就結束噠,有什麼不明白的或者有錯誤的地方,還望大家在評論區留言。
相關連結:
- 【Redis SCAN的使用】http://jinguoxing.github.io/redis/2018/09/...
- 【SCAN】http://doc.redisfans.com/key/scan.html