老生常談,keys不安全,因為
- keys的操作會導致數據庫暫時被鎖住,其他的請求都會被堵塞;業務量大的時候會出問題
Spring RedisTemplate實現scan
1. hscan sscan zscan
- 例子中的"field"是值redis的key,即從key為"field"中的hash中查找
- redisTemplate的opsForHash,opsForSet,opsForZSet 可以 分別對應 sscan、hscan、zscan
- 也可以使用
(JedisCommands) connection.getNativeConnection()
的 hscan、sscan、zscan 方法實現cursor遍歷,參照下文2.2章節
try { Cursor<Map.Entry<Object,Object>> cursor = redisTemplate.opsForHash().scan("field", ScanOptions.scanOptions().match("*").count(1000).build()); while (cursor.hasNext()) { Map.Entry<Object,Object> entry = cursor.next(); Object key = entry.getKey(); Object valueSet = entry.getValue(); } //關閉cursor cursor.close(); } catch (IOException e) { e.printStackTrace(); }
- cursor.close(); 游標一定要關閉,不然連接會一直增長;可以使用
client lists
info clients
info stats
命令查看客戶端連接狀態,會發現scan操作一直存在 - 我們平時使用的redisTemplate.execute 是會主動釋放連接的,可以查看源碼確認
- 代碼雖然只是調用一次scan方法,但是spring-data-redis已經對scan做了封裝,這個scan結合cursor.hasNext會多次redis scan,最終拿到所有match的結果
2. scan
2.1 使用spring-data-redis封裝好的scan方法
public Set<String> scan(String matchKey) { Set<String> keys = redisTemplate.execute((RedisCallback<Set<String>>) connection -> { Set<String> keysTmp = new HashSet<>(); Cursor<byte[]> cursor = connection.scan(new ScanOptions.ScanOptionsBuilder()
.match("*" + matchKey + "*").count(1000).build()); while (cursor.hasNext()) { keysTmp.add(new String(cursor.next())); } return keysTmp; }); return keys; }
項目中我用的就是這種方法來代替
Set keys = redisTemplate.keys(uploadTag + entity.getCaseNo() + ":*");