本文来说下Redis的管道pipeline
Redis提供了一个称为管道(Pipeline) 的机制将一组Redis命令进行组装,通过一次 RTT 传输给 Redis,再将这些 Redis 命令的执行结果按顺序传递给客户端。即使用pipeline执行了n次命令,整个过程就只需要一次 RTT。底层避免了用户态切换到内核态。
正常情况下,每个请求命令发出后client通常会阻塞并等待redis服务端处理,redis服务端处理完后将结果返回给client。当client使用pipeline发送命令时,redis server必须部分请求放到队列中(使用内存)执行完毕后一次性发送结果。在一定程度上,可以较大的提升性能,性能提升的原因主要是TCP链接中较少了“交互往返”的时间。
Redis是一种基于客户端-服务端模型(C/S模型)以及请求/响应协议的TCP服务。这意味着通常情况下一个请求会遵循以下步骤:
这就是普通请求模型。
所谓RTT(Round-Trip Time),就是往返时延,在计算机网络中它是一个重要的性能指标,表示从发送端发送数据开始,到发送端收到来自接收端的确认(接收端收到数据后便立即发送确认),总共经历的时延。
一般认为,单向时延 = 传输时延t1 + 传播时延t2 + 排队时延t3
为了解决这个问题,Redis支持通过管道,来达到减少RTT的目的。
SpringDataRedis提供了executePipelined方法对管道进行支持。下面是一个Redis队列的操作,放到了管道中进行操作。
package net.ijiangtao.tech.framework.spring.ispringboot.redis.pipelining;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;
import java.time.Duration;
import java.time.Instant;
/**
* Redis Pipelining
*
* @author ijiangtao
* @create 2019-04-13 22:32
**/
@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class RedisPipeliningTests {
@Autowired
private RedisTemplate<String, String> redisTemplate;
private static final String RLIST = "test_redis_list";
@Test
public void test() {
Instant beginTime2 = Instant.now();
redisTemplate.executePipelined(new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException {
for (int i = 0; i < (10 * 10000); i++) {
connection.lPush(RLIST.getBytes(), (i + "").getBytes());
}
for (int i = 0; i < (10 * 10000); i++) {
connection.rPop(RLIST.getBytes());
}
return null;
}
});
log.info(" ***************** pipeling time duration : {}", Duration.between(beginTime2, Instant.now()).getSeconds());
}
}
注意executePipelined中的doInRedis方法返回总为null。
当你要进行频繁的Redis请求的时候,为了达到最佳性能,降低RTT,你应该使用管道技术。
但如果通过管道发送了太多请求,也会造成Redis的CPU使用率过高。
下面是通过循环向Redis发送出队指令来监听队列的CUP使用情况:
当管道中累计了大量请求以后,CUP使用率迅速升到了100%,这是非常危险的操作。
对于监听队列的场景,一个简单的做法是当发现队列返回的内容为空的时候,就让线程休眠几秒钟,等队列中累积了一定量数据以后再通过管道去取,这样就既能享受管道带来的高性能,又避免了CPU使用率过高的风险。
Thread.currentThread().sleep(10 * 1000);
本文详细介绍了redis中有关管道相关的知识与内容。