目录java
Redis是一种基于客户端-服务端模型(C/S模型)
以及请求/响应协议
的TCP服务。git
这意味着一般状况下一个请求会遵循如下步骤:github
客户端向服务端发送一个查询请求,并监听Socket返回,一般是以阻塞模式,等待服务端响应。redis
服务端处理命令,并将结果返回给客户端。spring
这就是普通请求模型。springboot
所谓RTT(Round-Trip Time),就是往返时延,在计算机网络中它是一个重要的性能指标,表示从发送端发送数据开始,到发送端收到来自接收端的确认(接收端收到数据后便当即发送确认),总共经历的时延。bash
通常认为,单向时延 = 传输时延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提供了redis-benchmark
工具测试性能,我在本身的电脑上经过cmd打开命令行,不使用管道,进行了一百万次set和get操做,效果以下:
$ redis-benchmark -n 1000000 -t set,get -q
SET: 42971.94 requests per second
GET: 46737.71 requests per second
复制代码
平均每秒处理4万屡次操做请求。
经过-P
命令使用管道,效果以下:
$ redis-benchmark -n 1000000 -t set,get -P 16 –q
SET: 198098.27 requests per second
GET: 351988.72 requests per second
复制代码
使用管道之后,set和get的速度变成了每秒将近20万次和35万次。
而后我在服务器上,测试了使用SpringDataRedis进行rpop
出队2000次的性能。
分别使用单线程出队、32个线程并发出队和单线程管道出队。下面是测试的结果:
从统计结果来看,出队2000次,在单线程下大约须要6秒;32个线程并发请求大约须要2秒;而单线程下使用管道只须要70毫秒左右。
当你要进行频繁的Redis请求的时候,为了达到最佳性能,下降RTT,你应该使用管道技术。
但若是经过管道发送了太多请求,也会形成Redis的CPU使用率太高。
下面是经过循环向Redis发送出队指令来监听队列的CUP使用状况:
当管道中累计了大量请求之后,CUP使用率迅速升到了100%,这是很是危险的操做。
对于监听队列的场景,一个简单的作法是当发现队列返回的内容为空的时候,就让线程休眠几秒钟,等队列中累积了必定量数据之后再经过管道去取,这样就既能享受管道带来的高性能,又避免了CPU使用率太高的风险。
Thread.currentThread().sleep(10 * 1000);
复制代码