前几天出现了一个redis线上问题,其中涉及到了redis客户端的testOnBorrow配置,今天通过我们的线上问题,来说说redis客户端testOnBorrow配置的作用
redis线上问题
redis流量过大造成报警
我们一个服务连接的redis服务突然报警了,报警内容为:
- 访问redis服务的qps超过十万
看监控发现redis的qps超过十万
查询平台监控发现qps超过了十万

造成redis查询性能降低
- redis的qps达到了12万,我们的redis是副本形式的,极限的qps是10万以下,超过这个值,就会影响到性能
-
当时我们的redis服务受到影响查询时间已经到500ms
redis响应时间达到了500ms
分析问题
1.先查看流量分布

- 查看了redis的命令流量分布,主要用到的流量是hmget命令,qps达到3万6,是真实的服务流量,但是有7万2的qps,感觉不是很正常,看代码逻辑
2.看代码逻辑
- 1.我们的服务中redis是用作缓存,主要使用hash结构来缓存数据,提高接口查询速度
- 2.当redis查询不到、或者redis查询超时时候会从底层服务查询,来保证服务可靠性
- 3.该服务代码中使用了我们同学自己封装的redis客户端
- 4.redis配置中testOnBorrow属性设置为了true
- 5.testOnBorrow配置的作用为,在从池中取出连接前是否进行检验,如果检验失败,则从池中去除连接并尝试取出另一个。true为检查,false为不检查。设置为true时,当连接池中的连接全部被占后,导致后续操作,获取不到数据,这些程序会等待,直到超时或者获取到有效连接为主
- 6.连接校验的方式就是向服务发送ping命令,源码如下:
boolean validate = false;
Throwable validationThrowable = null;
try {
validate = this.factory.validateObject(p);
} catch (Throwable var15) {
PoolUtils.checkRethrow(var15);
validationThrowable = var15;
}
if (!validate) {
try {
this.destroy(p);
this.destroyedByBorrowValidationCount.incrementAndGet();
} catch (Exception var14) {
}
p = null;
if (create) {
NoSuchElementException nsee = new NoSuchElementException("Unable to validate object");
nsee.initCause(validationThrowable);
throw nsee;
}
}
}
校验的逻辑为发送ping命令
return hostAndPort.getHost().equals(connectionHost) && hostAndPort.getPort() == connectionPort && jedis.isConnected() && jedis.ping().equals("PONG");
- 7.这样的配置,理论上ping和正常流量是1比1,但是现在ping是2比1
- 8.查看我们同学封装redis客户端源码发现,这个客户端主要是为了集群模式redis准备的,他封装了一个JedisClient对象,每个JedisClient对应链接到一个redis集群节点上,获取redis连接过程为,先获取jedisClient对象,在用JedisClient对象获取redis链接,JedisClient对象获取连接时,也会使用jedis发送一次ping命令
String ping = jedis == null ? "" : jedis.ping();
- 9.所以当我们testOnBorrow属性设置为了true时,获取一次jedis连接时,会发送2次ping命令,造成了ping命令的流量为正常流量的两倍
- 10.实验验证:把我们同学封装的redis客户端替换成了jedis包后,把testOnBorrow属性设置为了true,测试接口为 ping流量和正常流量比例1比1
解决方式
1.把我们同学自己封装的redis包换成了jedis3.2版本,解决了每次获取连接ping的问题
2.由于我们redis只是用作缓存,查询不到时,会调用底层服务查询,所以每次查询不是强依赖与redis,所以我们把testOnBorrow属性设置为true,当连接池中获取不到连接时,查询走底层服务,保证服务的可靠性
3.目前现状
-
流量高峰时,redis的qps稳定在5万以下
image.png
-
服务响应时间也稳定在10ms左右
网友评论