Redis 4.x系列(八):Redis Pipeline(管道技术)

  Redis 管道是客户端将多个命令打包一次性发送给服务器,不用等到单独命令的执行结果返回;而 Redis 管理需要服务器在执行所有的命令后返回结果,所有命令发送和接收只发生一次,大大减少了多个命令单独在网络的耗时延迟。Redis 管道官方文档

管道(pipeline)详解

Redis 是一个使用客户端/服务器模型,遵循请求/响应协议TCP服务器,这意味着完成请求(通信过程)需要经过以下步骤:

  1. 客户端向服务发送一个命令。
  2. 服务器收到命令并将其放入执行队列(阻塞方式,因为 Redis 是单线程执行模型)。
  3. 命令被执行。
  4. 服务器将命令执行结果返回给客户端。

客户端和服务器通常需要通过网络连接,数据包从客户端经网络传输到服务器,服务器返回结果到客户端,这发送和返回的时间称为**往返时延(RTT)**。当客户端需要发送多个命令时(例如,将多个元素加到同一列表等),很容易看出是影响性能的。
通信过程的第1步 和 第4步 耗费的时间完全取决于客户端和服务器之间的网络延迟。若执行多条个命令,服务器执行命令的时间非常短,而网络传输可能花费更多的时间。

Redis 管道是客户端将多个命令打包一次性发送给服务器,不用等到单独命令的执行结果返回;而 Redis 管理需要服务器在执行所有的命令后返回结果,所有命令发送和接收只发生一次,大大减少了多个命令单个在网络的耗时。

管道技术是广泛使用的技术。使如,许多 POP3 协议实现已经支持此功能,大大加快了从服务器下载电子邮件的过程。Redis 从行早就开始支持管道技术,不管运行的什么版本,可理解为使用管道技术可以不考虑版本是否支持的情况。

管道技术不仅仅可以减少由于往返时间而导致的延迟成功,还大大提高了在给定 Redis 服务中每秒可执行的总操作量。这是因为,在不使用管道技术的情况下,为每个命令提供服务,需要调用系统 read()write(),意味着从用户态内核态,在多命令情况下,频繁的上下文切换需要消耗巨大的系统性能。使用管道技术,通常调用一次read()可以读取多个命令,并调用一次write()返回多个执行结果。使用管道技术最多可以提高10倍的性能。

示例操作:

  1. 创建多个 Redis 命令操作的文件

    1
    2
    3
    4
    5
    6
    7
    8
    $ touch pipeline.txt
    $ vim pipeline.txt

    set key1 value1
    sadd key2 value21 value22
    get key1
    scard key2
    cat pipeline.txt
  2. 转换行尾结束符

    1
    unix2dos pipeline.txt
  3. 使用 redis-cli--pipe 选项,通过管道发送命令

    1
    2
    3
    4
    5
    $ cat pipeline.txt | /usr/local/redis/bin/redis-cli -a 123456 - -pipe
    Warning: Using a password with '-a' option on the command line interface may not be safe.
    All data transferred. Waiting for the last reply...
    Last reply received from server.
    errors: 0, replies: 4

Java 中使用Redis管道

Spring Boot 集成 Redis,使用 RedisTemplate 执行 Pipeline 操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
* @name: SaveByPipeLine
* @desc: 使用管道技术批量发送数据
**/
@RunWith(SpringRunner.class)
@SpringBootTest
public class SendByPipeLine {

@Autowired
private RedisTemplate<Object, Object> redisTemplate;

/**
* 使用管道技术
*/
@Test
public void saveByPipeLine(){

RedisCallback<Object> redisCallback = new RedisCallback<Object>() {

//doInRedis中的redis操作不会立刻执行
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException {
connection.openPipeline();

//执行业务,打包多条命令
for (int i = 0; i < 10000; i++) {
String key = "key:" + i;
String value = "value" + i;
connection.set(key.getBytes(), value.getBytes());
// connection.get(key.getBytes());
}

//所有redis操作会在connection.closePipeline()之后一并提交到redis并执行,这是pipeline方式的优势
// List<Object> resultList = connection.closePipeline();
return null;
}
};

//所有操作的执行结果为executePipelined()的返回值
List<Object> resultList = redisTemplate.executePipelined(redisCallback, redisTemplate.getStringSerializer());
System.out.println(JSON.toJSONString(resultList));
}
}

参考:
RedisTemplate使用PipeLine的总结

Redis 4.x系列(八):Redis Pipeline(管道技术)

http://blog.gxitsky.com/2018/10/13/Redis-8-pipeline/

作者

光星

发布于

2018-10-13

更新于

2022-08-14

许可协议

评论