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秒。