Redis-Jedis使用Scan實現redis前綴key查找


  我們都知道Redis是單線程的,因此我們在使用一些命令(例如keys)的時候需要非常謹慎,可能一不小心就會阻塞進程,進而引起雪崩,這時候 scan命令就有了比較明顯的優勢;

scan命令簡介:

  SCAN 命令(在Redis2.8版本引入)是一個基於游標的迭代器(cursor based iterator): SCAN 命令每次被調用之后, 都會向用戶返回一個新的游標, 用戶在下次迭代時需要使用這個新游標作為 SCAN 命令的游標參數, 以此來延續之前的迭代過程,當 SCAN 命令的游標參數被設置為 0 時, 服務器將開始一次新的迭代, 而當服務器向用戶返回值為 0 的游標時, 表示迭代已結束。

  命令格式如下:

SCAN cursor [MATCH pattern] [COUNT count]

  其中cursor為游標,MATCH和COUNT為可選參數;

迭代過程示例如下(查詢以PLFX-ZZSFP-為前綴的key):

127.0.0.1:6379> scan 0 match PLFX-ZZSFP-* count 5
1) "1536"
2) (empty list or set)
127.0.0.1:6379> scan 1536 match PLFX-ZZSFP-* count 5
1) "1280"
2) (empty list or set)
127.0.0.1:6379> scan 1280 match PLFX-ZZSFP-* count 5
1) "1664"
2) (empty list or set)
127.0.0.1:6379> scan 1664 match PLFX-ZZSFP-* count 10
1) "1216"
2) (empty list or set)
127.0.0.1:6379> scan 1216 match PLFX-ZZSFP-* count 10
1) "32"
2) (empty list or set)
127.0.0.1:6379> scan 32 match PLFX-ZZSFP-* count 10
1) "1184"
2) (empty list or set)
127.0.0.1:6379> scan 1184 match PLFX-ZZSFP-* count 10
1) "1888"
2) 1) "PLFX-ZZSFP-AYHZ-10112301000032441693"
127.0.0.1:6379> scan 1888 match PLFX-ZZSFP-* count 10
1) "2016"
2) (empty list or set)
......

127.0.0.1:6379> scan 3 MATCH * COUNT 1
1) "0"
2) (empty list or set)

從上面迭代過程可以看到增量式迭代命令並不保證每次執行都返回某個給定數量的元素。增量式命令甚至可能會返回零個元素, 但只要命令返回的游標不是 0 , 應用程序就不應該將迭代視作結束;

與SCAN 命令相關的命令:

 與SCAN 命令相關的命令還有 SSCAN 命令、 HSCAN 命令和 ZSCAN 命令,都是用於增量地迭代(incrementally iterate)一集元素(a collection of elements),區別在於:

  1、SCAN 命令用於迭代當前數據庫中的數據庫鍵,返回的每個元素都是一個數據庫鍵;

  2、SSCAN 命令用於迭代集合鍵中的元素,返回的每個元素都是一個集合成員;

  3、HSCAN 命令用於迭代哈希鍵中的鍵值對,返回的每個元素都是一個鍵值對,一個鍵值對由一個鍵和一個值組成。

  4、ZSCAN 命令用於迭代有序集合中的元素(包括元素成員和元素分值),返回的每個元素都是一個有序集合元素,一個有序集合元素由一個成員(member)和一個分值(score)組成。

Jedis實現SCAN命令:

 SCAN 命令對應java的 Jedis 中的操作示例如下:

public static void testScan(Jedis jedis){
        // 游標初始值為0
        String cursor = ScanParams.SCAN_POINTER_START;
        String key = "PLFX-ZZSFP-*";
        ScanParams scanParams = new ScanParams();
        scanParams.match(key);// 匹配以 PLFX-ZZSFP-* 為前綴的 key
        scanParams.count(1000);
        while (true){
            //使用scan命令獲取數據,使用cursor游標記錄位置,下次循環使用
            ScanResult<String> scanResult = jedis.scan(cursor, scanParams);
            cursor = scanResult.getStringCursor();// 返回0 說明遍歷完成
            List<String> list = scanResult.getResult();
            long t1 = System.currentTimeMillis();
            for(int m = 0;m < list.size();m++){
                String mapentry = list.get(m);
                System.out.println(mapentry);
                //jedis.del(key, mapentry);
                
            }
            long t2 = System.currentTimeMillis();
            System.out.println("獲取" + list.size()
                + "條數據,耗時: " + (t2-t1) + "毫秒,cursor:" + cursor);
            if ("0".equals(cursor)){
                break;
            }
        }
}

 SSCAN 命令對應的 Jedis 中的操作如下:

public static void testSscan(Jedis jedis) {
        // 游標初始值為0
        String cursor = ScanParams.SCAN_POINTER_START;
        ScanParams scanParams = new ScanParams();
        scanParams.count(1000);
        String key = "PLFX-ZZSFP";
        while (true) {
            //使用sscan命令獲取數據,使用cursor游標記錄位置,下次循環使用
            ScanResult<String> sscanResult = jedis.sscan(key, cursor, scanParams);
            cursor = sscanResult.getStringCursor();// 返回0 說明遍歷完成
            List<String> scanResult = sscanResult.getResult();
            long t1 = System.currentTimeMillis();
            for (int m = 0; m < scanResult.size(); m++) {
                String mapentry = scanResult.get(m);
                System.out.println(mapentry);
                jedis.srem(key, mapentry);//處理過程,可以換成其他的
            }
            long t2 = System.currentTimeMillis();
            System.out.println("處理" + scanResult.size() + "條數據,耗時: " + (t2 - t1) + "毫秒,cursor:" + cursor);
            if ("0".equals(cursor)) {
                break;
            }
        }
    }

 HSCAN 命令對應的 Jedis 中的操作如下:

public static void testHcan(Jedis jedis) {
        // 游標初始值為0
        String cursor = ScanParams.SCAN_POINTER_START;
        ScanParams scanParams = new ScanParams();
        scanParams.count(1000);
        String key = "PLFX-ZZSFP";
        while (true) {
            //使用hscan命令獲取數據,使用cursor游標記錄位置,下次循環使用
            ScanResult<Map.Entry<String, String>> hscanResult = jedis.hscan(key, cursor, scanParams);
            cursor = hscanResult.getStringCursor();// 返回0 說明遍歷完成
            List<Map.Entry<String, String>> scanResult = hscanResult.getResult();
            long t1 = System.currentTimeMillis();
            for (int m = 0; m < scanResult.size(); m++) {
                Map.Entry<String, String> mapentry = scanResult.get(m);
                System.out.println(mapentry);
                jedis.hdel(key, mapentry.getKey());//處理過程,可以換成其他的
            }
            long t2 = System.currentTimeMillis();
            System.out.println("處理" + scanResult.size() + "條數據,耗時: " + (t2 - t1) + "毫秒,cursor:" + cursor);
            if ("0".equals(cursor)) {
                break;
            }
        }
    }

 ZSCAN 命令對應的 Jedis 中的操作如下:

public static void testZscan(Jedis jedis) {
        // 游標初始值為0
        String cursor = ScanParams.SCAN_POINTER_START;
        String key = "PLFX-ZZSFP-*";
        ScanParams scanParams = new ScanParams();
        scanParams.match(key);// 匹配以 PLFX-ZZSFP-* 為前綴的 key
        scanParams.count(1000);
        while (true) {
            //使用 zscan 命令獲取數據,使用cursor游標記錄位置,下次循環使用
            ScanResult<Tuple> scanResult = jedis.zscan(key, cursor, scanParams);
            cursor = scanResult.getStringCursor();// 返回0 說明遍歷完成
            List<Tuple> list = scanResult.getResult();
            long t1 = System.currentTimeMillis();
            for (int m = 0; m < list.size(); m++) {
                Tuple tuple = list.get(m);
                System.out.println("Element:" + tuple.getElement() + ",Score:" + tuple.getScore());
            }
            long t2 = System.currentTimeMillis();
            System.out.println("處理" + list.size() + "條數據,耗時: " + (t2 - t1) + "毫秒,cursor:" + cursor);
            if ("0".equals(cursor)) {
                break;
            }
        }
    }

 

本文只是簡單介紹了一下RedisScan命令,及在java中使用Jedis實現Scan命令的方式;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM