最近接到一個需求,後台管理頁面中,提供一個簡單的redis操作介面(其實rdm本身很不錯,https://github.com/uglide/RedisDesktopManager/releases/ 為啥還要搞這個,rdm應該只是營運人員使用呢,涉及帳號密碼等許可權的事情),不管,看看準系統如何?。
第一個需求,列出redis的key,以及能按照pattern查詢出key列表。
redis本身提供了info,dbsize,keys *
info可以看到所有庫的key數量
dbsize則是當前庫key的數量
keys *這種資料量小還可以,大的時候可以直接搞死生產環境。
dbsize和keys *統計的key數可能是不一樣的,如果沒記錯的話,keys *統計的是當前db有效key,而dbsize統計的是所有未被銷毀的key(有效和未被銷毀是不一樣的,具體可以瞭解redis的到期策略)
跟現在需求實現還是有點距離。
想要取指定pattern的key的數量。這個功能Redis沒有,以後也不會有。
有人在github上向antirez提過這個要求,結果被否決了,理由是:
Hi, KEYS is only intended for debugging since it is O(N) and performs a full keyspace scan. COUNT has the same problem but without the excuse of being useful for debugging... (since you can simply use redis-cli keys ... | grep ...). So feature not accepted. Thanks for your interest.
崩潰ing。。。。
翻閱文檔,看到有一個scan命令(增量式迭代命令)。好像能實現功能,先走一把。
public List<String> findKeysForPage(String patternKey, int pageNum, int pageSize) { ScanOptions options = ScanOptions.scanOptions().match(patternKey).build(); RedisConnectionFactory factory = stringRedisTemplate.getConnectionFactory(); RedisConnection rc = factory.getConnection(); Cursor<byte[]> cursor = rc.scan(options); List<String> result = new ArrayList<String>(pageSize); int tmpIndex = 0; int startIndex = (pageNum - 1) * pageSize; int end = pageNum * pageSize; while (cursor.hasNext()) { if (tmpIndex >= startIndex && tmpIndex < end) { result.add(new String(cursor.next())); tmpIndex++; continue; } // 擷取到滿足條件的資料後,就可以退出了 if(tmpIndex >=end) { break; } tmpIndex++; cursor.next(); } try { // cursor.close(); } catch (Exception e) { e.printStackTrace(); } try { RedisConnectionUtils.releaseConnection(rc, factory); } catch (Exception e) { e.printStackTrace(); } return result; }
如上代碼:
1.能分頁擷取key的列表
2.如果需要擷取總數,去掉上面方法中,
if(tmpIndex >=end) { break; }
那麼最終tmpIndex的值就是總數。
另外:
在spring-redis-data的1.6.0版本之前可能會出現,
java.util.NoSuchElementException] with root cause
java.util.NoSuchElementException
at java.util.Collections$EmptyIterator.next(Collections.java:4189)
at org.springframework.data.redis.core.ScanCursor.moveNext(ScanCursor.java:215)
at org.springframework.data.redis.core.ScanCursor.next(ScanCursor.java:202)
這裡有說明:
https://jira.spring.io/browse/DATAREDIS-417
1.6.1之後解決了。
但是,ohmygod,總是有但是:
1.如何輸入的patternKey內容返回的結果很多,而且需要得到總數,那麼就會遍曆幾乎所有的key,一般我們線上key的數量在千萬層級,那麼跑起來的速度很嚇人,佔用線上資源
2.scan命令本身,因為增量式命令僅僅使用遊標來記錄迭代狀態,同一個元素可能會被返回多次。 處理重複元素的工作交由應用程式負責。比如說, 可以考慮將迭代返回的元素僅僅用於可以安全地重複執行多次的操作上。如果一個元素是在迭代過程中被添加到資料集的, 又或者是在迭代過程中從資料集中被刪除的, 那麼這個元素可能會被返回, 也可能不會, 這是未定義的(undefined)。
結論:
1.在生產環境使用,要謹慎,最好只是開發人員許可權的功能
2.最好不要使用總數,這樣相對來說分頁擷取值,一般不會擷取很多頁後面的內容