redis常用操作(管道(pipeline)實現批量操作,Redis模糊匹配等)

試了很多種錯誤的方法,現將自己測試成功redis管道pipeline批量操作的方法和redis常用操作以及一些關於springboot+redis的概念分享給大家

開發環境準備:

spring boot 2.x 

使用RedisTemplate 操作

springboot項目pom引入redis依賴:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency> 
        <!-- Redis-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-redis</artifactId>
            <version>1.4.7.RELEASE</version>
        </dependency>

Jedis是Redis官方推薦的面向Java的操作Redis的客戶端,而RedisTemplate是SpringDataRedis中對JedisApi的高度封裝。
SpringDataRedis相對於Jedis來說可以方便地更換Redis的Java客戶端,比Jedis多了自動管理連接池的特性,方便與其他Spring框架進行搭配使用.

 

 redisTemplate

1. redisTemplate

  1. redisTemplate默認使用的是JDK序列化,但是可以主動設置
  2. redisTemplate執行兩條命令其實是在兩個連接裏完成的,因爲redisTemplate執行完一個命令就會對其關閉,但是redisTemplate額外爲什麼提供了RedisCallback和SessionCallBack兩個接口

 StringRedisTemplate

  1. StringRedisTemplate繼承RedisTemplate,只是提供字符串的操作,複雜的Java對象還要自行處理

RedisCallback和SessionCallBack:

  1. 作用: 讓RedisTemplate進行回調,通過他們可以在同一條連接中執行多個redis命令
  2. SessionCalback提供了良好的封裝,優先使用它,redisCallback使用起來有點複雜(很多工作需要我們自己來完成)還是優先選擇SessionCalback

 

redis 基礎操作

 redisTemplate模糊匹配刪除

String key = "userRole:*";
            redisTemplate.delete(key);

 Redis模糊查詢

可以通過Redis中keys命令進行獲取key值,具體命令格式:keys pattern

文中提到redis中允許模糊查詢的有3個通配符,分別是:*,?,[]

其中:

*:通配任意多個字符

?:通配單個字符

[]:通配括號內的某一個字符

  使用通配符拿到keys

Set<String> keysUserRole = redisTemplate.keys("userRole:" + "*");

 批量查詢

Set<String> keysList = stringRedisTemplate.keys(keys);
List<String> strings = stringRedisTemplate.opsForValue().multiGet(keysList);

Redis管道(pipeline)流操作

總的來說Redis的管道可以在大量數據需要一次性操作完成的時候,使用Pipeline進行批處理,將多次操作合併成一次操作,可以減少鏈路層的時間消耗。

流水線:

  redis的讀寫速度十分快,所以系統的瓶頸往往是在網絡通信中的延遲。

  redis可能會在很多時候處於空閒狀態而等待命令的到達。

  爲了解決這個問題,可以使用redis的流水線,流水線是一種通訊協議,類似一個隊列批量執行一組命令。

 

  redis的管道 pipeline批量set

 //耗時:309;

 @RequestMapping(value = "/redisPipeline", method = RequestMethod.POST)
    @ApiOperation(value = "redis的管道 pipeline 添加數據測試")
    public void  redistest(){
        log.info("redistest開始");
        // 開始時間
        long start = System.currentTimeMillis();
        RedisSerializer stringSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringSerializer);
        redisTemplate.setValueSerializer(stringSerializer);
        List<String> result = redisTemplate.executePipelined(new SessionCallback() {
            //執行流水線
            @Override
            public Object execute(RedisOperations operations) throws DataAccessException {
                //批量處理的內容
                for (int i = 0; i < 10000; i++) {
                    operations.opsForValue().set("redistest:" + "k" + i, "v" + i);
                }
                //注意這裏一定要返回null,最終pipeline的執行結果,纔會返回給最外層
                return null;
            }
        });
        // 結束時間
        long end = System.currentTimeMillis();
        log.info("運行時間:"+(end-start));
        }

此處與未使用管道流水線操作做對比後續其他操作就不一一對比了

           未使用流水線處理10000次請求:

           //耗時:5692;

public class RedisTest {
    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    public void test(){
        // 開始時間
        long start = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
            redisTemplate.opsForValue().set("k"+i,"v"+i);
        }
        // 結束時間
        long end = System.currentTimeMillis();
        System.out.println(end-start);
    }
}

  redis的管道 pipeline批量 delete

@RequestMapping(value = "/redisPipeline", method = RequestMethod.DELETE)
    @ApiOperation(value = "redis的管道 pipeline刪除測試")
    public void  redisDeletetest(){
        log.info("redistest開始");
        // 開始時間
        long start = System.currentTimeMillis();
        RedisSerializer stringSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringSerializer);
        redisTemplate.setValueSerializer(stringSerializer);
        redisTemplate.executePipelined(new SessionCallback() {
            //執行流水線
            @Override
            public Object execute(RedisOperations operations) throws DataAccessException {
                //批量處理的內容
                for (int i = 0; i < 20000; i++) {
                    //operations.opsForValue().set("redistest:"+"k"+i,"v"+i);
                    operations.delete("redistest:"+"k"+i);
                    System.out.println(i);
                }
                return null;
            }
        });
        // 結束時間
        long end = System.currentTimeMillis();
        log.info("運行時間:"+(end-start));
    }

  redis的管道 pipeline批量 GET

  /**
     * redis 批量操作其中一種方式
     * redis pipeline 管道技術
     */
    @RequestMapping(value = "/redisPipeline", method = RequestMethod.GET)
    @ApiOperation(value = "redis的管道 pipeline GET測試")
    public void redisPipeline(){
        RedisSerializer stringSerializer = new StringRedisSerializer();
        stringRedisTemplate.setKeySerializer(stringSerializer);
        stringRedisTemplate.setValueSerializer(stringSerializer);
        List<String>  keys=new ArrayList();
        for (int i = 0; i < 200; i++) {
            keys.add("redistest:"+"k"+i);
        }
        //調用 通道批量獲取
        Map<String, Object> stringObjectMap = batchQueryByKeys(keys, true);
        System.out.println(stringObjectMap.size());
    }

    /**
     *
     * @param keys
     * @param useParallel  是否使用並行平行流
     * @return
     */
    public Map<String,Object> batchQueryByKeys(List<String> keys,Boolean useParallel){
        if(null == keys || keys.size() == 0 ){
            return null;
        }
        if(null == useParallel){
            useParallel = true;
        }
        List<Object> results = stringRedisTemplate.executePipelined(
                new RedisCallback<Object>() {
                    @Override
                    public Object doInRedis(RedisConnection connection) throws DataAccessException {
                        StringRedisConnection stringRedisConn = (StringRedisConnection)connection;
                        for(String key:keys) {
                            stringRedisConn.get(key);
                        }
                        return null;
                    }
                });
        if(null == results || results.size() == 0 ){return null;}

        Map<String,Object> resultMap  =  null;

        if(useParallel){

            Map<String,Object> resultMapOne  = Collections.synchronizedMap(new HashMap<String,Object>());

            keys.parallelStream().forEach(t -> {
                resultMapOne.put(t,results.get(keys.indexOf(t)));
            });

            resultMap = resultMapOne;

        }else{

            Map<String,Object> resultMapTwo  = new HashMap<>();

            for(String t:keys){
                resultMapTwo.put(t,results.get(keys.indexOf(t)));
            }

            resultMap = resultMapTwo;
        }

        return  resultMap;

    }

在這裏要說明下我實現的管道pipeline批量獲取使用的是RedisCallback對象實現的,原因是我使用SessionCalback對象來實現時調用get方法總是獲取null最後也沒找到原因所以使用了RedisCallback對象來實現的批量獲取,如果有哪位大神瞭解SessionCalback對象的實現方法求指點一二  哈哈。。。。

 

批量操作multi和pipeline效率的比較

multi和pipeline的區別在於multi會將操作都即刻的發送至redis服務端queued起來,每條指令queued的操作都有一次通信開銷,執行exec時redis服務端再一口氣執行queued隊列裏的指令,pipeline則是在客戶端本地queued起來,執行exec時一次性的發送給redis服務端,這樣只有一次通信開銷。比如我有5個incr操作,multi的話這5個incr會有5次通信開銷,但pipeline只有一次。

所以在批量操作使用pipeline效率會更高。

 

關於文章提到的並行執行的流的說明請移步文章:

地址:https://blog.csdn.net/u011001723/article/details/52794455/

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章