redis scan 命令指南
1. 模糊查詢鍵值
redis 中模糊查詢key
有 keys
,scan
等,一下是一些具體用法。
-- 命令用法:keys [pattern]
keys name* -- 查詢以name開始的key
-- 命令用法:scan cursor [match pattern] [COUNT count]
scan 0 match name*
更多命令請參考:http://doc.redisfans.com/key/scan.html
2. keys 注意事項
雖然 keys
的速度非常快,但是在一個大的數據庫中,使用它還是可能造成性能問題,如果你需要從一個數據集中查找特定的key,你最好還是用 Redis 集合結構(set)來代替。
也就是說,keys 命令在生產環境不可以隨便用,因為keys 會鎖住 redis,並增加redis 的cpu 占用,所以很多公司的redis都禁用了這個命令。
而scan
就不會,因為它每次執行只返回少量的元素,所以這個命令可以用於生產環境,而不會像keys
,smembers
命令一樣,當數據庫很大時,可能會鎖住數秒,這對10000Qps的redis來說是毀滅性的傷害。
3. scan 使用方式
這里使用redisTemplate
來執行Redis命令,具體例子如下:
-- 1.單次查詢
(ScanPageResult) redisTemplate.execute((RedisCallback<ScanPageResult>) conn -> {
MultiKeyCommands commands = (MultiKeyCommands) conn.getNativeConnection();
ScanParams scanParams = new ScanParams();
scanParams.count(CommonConst.BATCH_SIZE_200);
scanParams.match(pattern);
ScanPageResult result = new ScanPageResult();
ScanResult<String> scanResult = commands.scan(cursor, scanParams);
Set<String> keys = Sets.newHashSet();
if (scanResult.getStringCursor() != null) {
keys.addAll(scanResult.getResult());
if (!"0".equals(scanResult.getStringCursor())) {
result.setNextCursor(scanResult.getStringCursor());
}
}
result.setKeys(keys);
return result;
});
-- ScanPageResult 是自己構造的對象,存儲返回的keys和cursor
-- 2. 查詢所有
do {
result = CacheUtil.scanForPage(pattern, result.getNextCursor());
if (result == null) {
break;
}
Set<String> keys = result.getKeys();
doSomething(keys);
} while (!"0".equals(result.getNextCursor()));
稍微解釋一下,因為scan
命令只會返回少量數據,而不是所有數據,所以它還需要返回一個:記錄上次查詢到的位置標識,這個在redis
里被稱為cursor(游標)。
所以下次再次查詢的時候需要傳入上一次返回的cursor
繼續查詢,直到cursor=0
為止,標識迭代結束,查詢完畢。
一般第一次查詢傳入的cursor=0
,作為初始查詢,然后根據結果判斷是否進行下一次查詢。
3.1 scan 命令的保證
因為是增量式迭代查詢,以保證查詢所有的結果,所以,在查詢間隔中新增的key,不一定會被返回。
另外,因為新增或刪除key都會改變redis key的索引,所以,多次查詢也會有重復的元素出現,所以使用scan命令,一定需要保證業務處理可重復執行。
然而因為增量式命令僅僅使用游標來記錄迭代狀態, 所以這些命令帶有以下缺點:
同一個元素可能會被返回多次。 處理重復元素的工作交由應用程序負責, 比如說, 可以考慮將迭代返回的元素僅僅用於可以安全地重復執行多次的操作上。
如果一個元素是在迭代過程中被添加到數據集的, 又或者是在迭代過程中從數據集中被刪除的, 那么這個元素可能會被返回, 也可能不會, 這是未定義的(undefined)。
3.2 並發執行多個迭代
在同一時間, 可以有任意多個客戶端對同一數據集進行迭代, 客戶端每次執行迭代都需要傳入一個游標, 並在迭代執行之后獲得一個新的游標, 而這個游標就包含了迭代的所有狀態, 因此, 服務器無須為迭代記錄任何狀態。
3.3 使用錯誤的游標進行增量式迭代
使用間斷的(broken)、負數、超出范圍或者其他非正常的游標來執行增量式迭代並不會造成服務器崩潰, 但可能會讓命令產生未定義的行為。
未定義行為指的是, 增量式命令對返回值所做的保證可能會不再為真。
只有兩種游標是合法的:
- 在開始一個新的迭代時, 游標必須為 0 。
- 增量式迭代命令在執行之后返回的, 用於延續(continue)迭代過程的游標。