1. Redis 的pipeline
1.1 什麼是pipeline
Redis 中的pipeline 管道命令類似於批處理執行命令。 普通執行一次Redis 命令會進行一次TCP連接的創建與銷燬, 執行多條Redis 命令就會進行多次TCP連接的創建與銷燬, 而TCP 連接的創建與銷燬時比較耗時。 採用pipeline技術, 是先將多條命令添加到隊列中,然後創建一次TCP連接,將每次命令的執行結果(返回值爲非void的命令)依次添加到list中,然後一次性返回,最後關閉TCP連接。
1.2 pipeline 特點
- 一次pipeline 操作只創建和銷燬一次TCP連接
- pipeline 中的多條命令存儲在一個隊列中,所以命令執行是有順序的
- pipeline 中每條返回值不是void的命令都會返回一個結果,然後存儲在List中. 縱使返回值爲null
- pipeline 中返回值爲void的命令, 不會返回結果, 也就是說list中不會有此對象的返回值.
- pipeline 中允許執行不同的命令
1.3 SDR API
SDR 提供了兩中API來執行pipeline命令,區別在於對命令返回的結果進行反向序列化時,使用redisTemplate自身的valueSerializer,還是使用自定義的valueSerializer. 通常使用第一種方式即可.
API 簽名 | 方法描述 |
---|---|
public List executePipelined(RedisCallback<?> action) | 執行pipeline命令,使用redisTemplate的valueSerializer對結果進行解析 |
public List executePipelined(RedisCallback<?> action, @Nullable RedisSerializer<?> resultSerializer) | 執行pipleLine命令, 使用自定義的serializer 對結果集進行解析 |
2. API 測試
筆者構造四條命令來測試pipeline 的執行機制:
- 命令一返回爲A, 所以list中添加第一個元素
- 命令二返回爲void,所以list中不添加
- 命令三返回爲null, 所以list中添加第二個元素,只不過值爲null
- 命令四返回爲B,所以list中添加第三個元素
2.1 默認序列化方式執行pipeline
- 筆者使用stringRedisTemplate 開啓pipeline , 所以pipeline 中value序列化爲stringRedisTemplate的序列化方式, 即StringRedisSerializer.
- 每個命令返回值都會使用StringRedisSerializer進行反向序列化。
@Test
public void test() {
// 初始化數據10條數據
ListOperations<String, String> ops = stringRedisTemplate.opsForList();
ops.leftPush("queue", "{\"@class\":\"org.zongf.learn.sdr.l01.UserPO\",\"username\":\"zhangsan\",\"password\":\"123456\",\"age\":20}");
ops.leftPush("queue", "{\"@class\":\"org.zongf.learn.sdr.l01.UserPO\",\"username\":\"zhangsan\",\"password\":\"123456\",\"age\":20}\n");
// 管道符獲取命令
List<Object> list = stringRedisTemplate.executePipelined(new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException {
// 使用StringRedisTemplate, 可以將RedisConnection 轉換爲StringRedisConnection
StringRedisConnection stringRedisConnection = (StringRedisConnection) connection;
// 返回值爲string
stringRedisConnection.rPop("queue");
// 返回值爲void
stringRedisConnection.slaveOfNoOne();
// 返回值爲null
stringRedisConnection.rPop("no-exsists-queue");
// 返回值爲string
stringRedisConnection.rPop("queue");
return null;
}
});
for (Object object : list) {
String clz = object != null ? object.getClass().toString() : null;
System.out.println(clz + "--> " + object);
}
}
輸出結果:
class java.lang.String--> {"@class":"org.zongf.learn.sdr.l01.UserPO","username":"zhangsan","password":"123456","age":20}
null--> null
class java.lang.String--> {"@class":"org.zongf.learn.sdr.l01.UserPO","username":"zhangsan","password":"123456","age":20}
2.2 自定義序列化方式
- 筆者使用json 序列化工具反向解析每個命令的執行結果, 所以返回結果可以直接是List
- 使用指定Serializer 方式API時, RedisConnection 不能再轉換爲StringRedisConnection
@Test
public void test2() {
// 初始化數據10條數據
ListOperations<String, String> ops = stringRedisTemplate.opsForList();
ops.leftPush("queue", "{\"@class\":\"org.zongf.learn.sdr.l01.UserPO\",\"username\":\"zhangsan\",\"password\":\"123456\",\"age\":20}");
ops.leftPush("queue", "{\"@class\":\"org.zongf.learn.sdr.l01.UserPO\",\"username\":\"zhangsan\",\"password\":\"123456\",\"age\":20}\n");
// 管道符獲取命令
List<UserPO> list = redisTemplate.executePipelined(new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException {
// 返回值爲string
connection.rPop("queue".getBytes());
// 返回值爲void
connection.slaveOfNoOne();
// 返回值爲null
connection.rPop("no-exsists-queue".getBytes());
// 返回值爲string
connection.rPop("queue".getBytes());
return null;
}
}, new GenericJackson2JsonRedisSerializer());
System.out.println("result:" + list);
}
輸出結果:
class org.zongf.learn.sdr.l01.UserPO--> UserPO{username='zhangsan', password='123456', age=20}
null--> null
class org.zongf.learn.sdr.l01.UserPO--> UserPO{username='zhangsan', password='123456', age=20}