需求
假設你需要從 Redis 實例成千上萬的 key 中找出特定前綴的 key 列表來手動處理數據,可能是修改它的值,也可能是刪除 key。那該如何從海量的 key 中找出滿足特定前綴的 key 列表來?
我們可以用 keys 來列出所有滿足特定正則字符串規則的 key .
192.168.18.131:8001> set artisan 1
OK
192.168.18.131:8001> set artisan2 2
-> Redirected to slot [6066] located at 192.168.18.132:8002
OK
192.168.18.132:8002> set artisan3 3
-> Redirected to slot [1939] located at 192.168.18.131:8001
OK
192.168.18.131:8001> set artisan4 4
-> Redirected to slot [14196] located at 192.168.18.132:8005
OK
192.168.18.132:8005> set artisan5 5
-> Redirected to slot [10069] located at 192.168.18.132:8002
OK
192.168.18.132:8002>
192.168.18.132:8002> keys *
1) "artisanKey"
2) "clusterArtisan"
3) "artisan2"
4) "ar"
5) "test"
6) "artisan5"
192.168.18.132:8002> keys artisan*
1) "artisanKey"
2) "artisan2"
3) "artisan5"
192.168.18.132:8002>
我這個是集羣環境,有幾個key被分到了其他的slot上去了,所以看到的數據僅僅是當前slot的數據。
keys 優點呢 ,使用簡單
當然了,也有缺點
- 一次性列出所有滿足條件的 key. keys 算法是遍歷算法,複雜度是 O(n) ,如果數據量很大,會導致 Redis 服務卡頓,所有讀寫 Redis 的其它的指令都會被延後甚至會超時報錯,因爲Redis 是單線程程序,順序執行所有指令,其它指令必須等到當前的 keys 指令執行完了纔可以繼續。
咋辦呢? ---------------> Redis在 2.8 版本中加入了scan指令.
scan
scan 相比keys 具備有以下特點:
- 複雜度雖然也是 O(n),但是它是通過遊標分步進行的,不會阻塞線程;
- 提供 limit 參數,可以控制每次返回結果的最大條數,limit 只是一個 hint,返回的結果可多可少;
- 同 keys 一樣,它也提供模式匹配功能;
- 服務器不需要爲遊標保存狀態,遊標的唯一狀態就是 scan 返回給客戶端的遊標整數;
- 返回的結果可能會有重複,需要客戶端去重複,這點非常重要;
- 遍歷的過程中如果有數據修改,改動後的數據能不能遍歷到是不確定的;
- 單次返回的結果是空的並不意味着遍歷結束,而要看返回的遊標值是否爲零
scan基本使用
批量寫入一批模擬數據
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPoolConfig;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
public class JedisClusterDemo {
public static void main(String[] args) throws IOException {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(20);
config.setMaxIdle(10);
config.setMinIdle(5);
Set<HostAndPort> jedisClusterNode = new HashSet<HostAndPort>();
jedisClusterNode.add(new HostAndPort("192.168.18.131", 8001));
jedisClusterNode.add(new HostAndPort("192.168.18.131", 8004));
jedisClusterNode.add(new HostAndPort("192.168.18.132", 8002));
jedisClusterNode.add(new HostAndPort("192.168.18.132", 8005));
jedisClusterNode.add(new HostAndPort("192.168.18.133", 8003));
jedisClusterNode.add(new HostAndPort("192.168.18.133", 8006));
JedisCluster jedisCluster = null;
try {
//connectionTimeout:指的是連接一個url的連接等待時間
//soTimeout:指的是連接上一個url,獲取response的返回等待時間
jedisCluster = new JedisCluster(jedisClusterNode, 6000, 5000, 10, "artisan", config);
for (int i = 0; i < 10000; i++) {
jedisCluster.set("{art}:clusterArtisan:" + i, "artisanValue:" + i);
}
System.out.println("DONE");
} catch (Exception e) {
e.printStackTrace();
} finally {
if (jedisCluster != null)
jedisCluster.close();
}
}
}