redis開門之批次插入pipeLine

有点儿意思發表於2024-12-04

Redis開門之批次插入Pipeline

下發資料同步到Redis中,資料少的話幾千條,多則達百萬級。其中一個場景是把下發的資料同步到Redis中,資料同步完成後,把資料寫入到檔案中,下發給客戶,客戶呼叫。某天......

產品經理:小A,我發現我們這個資料整體下發的流程耗時有點長啊...從拉取資料到處理下發將近一個小時的時間,每天處理的XX數量也不是很多,看下是哪塊耗時比較久,最佳化下

小A:嗯...我們現在的處理邏輯是所有的任務完成後 才會統一進行同步Redis的操作,而且目前的程式碼並沒有批次處理資料到Redis,而是直接一條一條的處理,就很慢....

產品經理:一條一條?百萬的資料 那大概處理多長時間啊?最佳化下吧...

小A:嗯,我看下怎麼處理吧

看了之前同步Redis程式碼的邏輯(之前不是小A寫的),非同步處理、執行緒池、分批次、應有盡有但就是很慢。給出小A 最佳化前的大概框架。

try {
            redisTemplate.executePipelined(new SessionCallback<Object>() {
                @Override
                public <K, V> Object execute(RedisOperations<K, V> redisOperations) throws DataAccessException {
                    for (JSONObject redisSynchro : json) {
                        // 同步資料到redis
                        redisTemplate.opsForList().rightPush("XXX")
                        // 設定過期時間
                        redisTemplate.expire()
                    }
                    return null;
                }
            });
        } catch (Exception e) {
            log.info("同步redis發生意外異常 :" + e.getMessage());
        } finally {
            countDownLatch.countDown();
        }

小A查了下Redis批次處理資料的方法 網上給出的部落格就是pipeline操作啊,程式碼裡用的就是executePipelined 執行管道操作。嗯... 不對再查查。executePipelined的執行原理是什麼?

  • 管道(Pipeline) 是 Redis 提供的一種最佳化手段,允許客戶端將多個命令一次性傳送到 Redis 伺服器,而不是每次傳送一個命令並等待響應。這樣可以減少網路延遲,提高操作效率。

  • executePipelined() 方法的本意是將多個 Redis 操作封裝為一個管道批次執行,返回所有操作的結果。

  • 客戶端批次傳送命令 → 伺服器批次執行命令 → 客戶端一次性接收所有結果。


為什麼第一次的執行沒有透過管道執行呢?

封裝層次過高: executePipelined 中使用了 redisTemplate 的操作方法,而不是直接透過 RedisConnection 來執行 Redis 的底層命令。

分散的命令: 每個 rightPush和 expire 方法都各自獨立呼叫,即使這些操作在同一個 executePipelined 方法中,也無法透過 Redis 的原生管道機制最佳化。

所以如果要想使用Redis的管道操作,應該避免使用高層次封裝的redisTemplat。於是乎,小A改變了上面的執行方式

redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
    StringRedisSerializer serializer = new StringRedisSerializer();
    for (ResultInfo result : tmps) {
        // 組裝資料 key/value
        // 檢查元素是否已存在
        if (檢查條件) {
            // 如果不存在,則插入
            connection.rPush(serializer.serialize(key), serializer.serialize(value));
        }
        // 設定過期時間
        connection.expire(serializer.serialize(key), 30 * 24 * 3600L);   
    }
    return null;
});

使用了 StringRedisSerializer 對鍵和值進行序列化。序列化方式直接處理 Redis 底層連線的操作,通常適用於高效能的場景。

經過實測13W級資料插入耗時3秒,9W資料耗時1秒。

相關文章