使用Azure Cache for Redis时Jedis客户端ensureFill方法性能缓慢问题
我们使用1GB标准层的Azure Cache for Redis,在400并发高负载场景下,Redis写入操作变慢,排查发现Jedis客户端的redis.clients.jedis.util.RedisInputStream.ensureFill()方法存在性能瓶颈。
缓存使用流程为:从连接池获取连接 → 读写操作 → 关闭连接,但ensureFill()在这三个步骤中均被调用,不符合预期。从调用栈可见,缓存操作的三个环节(从池取资源、读写、关闭)最终都会调用该方法,且它始终是耗时最长的方法。
疑问点:
ensureFill()方法的作用是什么?- 为何在从连接池获取连接后仅做简单读写的情况下,三个步骤都要调用它?甚至关闭连接时也会触发?
以下是连接池配置代码:
private static GenericObjectPoolConfig<Jedis> buildPoolConfig() { final JedisPoolConfig poolConfig = new JedisPoolConfig(); poolConfig.setMaxTotal(200); poolConfig.setMaxIdle(100); poolConfig.setMinIdle(20); poolConfig.setTestOnBorrow(true); poolConfig.setTestOnReturn(true); poolConfig.setTestWhileIdle(true); poolConfig.setMinEvictableIdleTimeMillis(Duration.ofSeconds(100).toMillis()); poolConfig.setTimeBetweenEvictionRunsMillis(Duration.ofSeconds(100).toMillis()); poolConfig.setNumTestsPerEvictionRun(3); poolConfig.setBlockWhenExhausted(true); return poolConfig; }
1. ensureFill()方法的作用
ensureFill()是Jedis底层处理Redis响应流的核心方法,核心职责是保证RedisInputStream中有足够的字节数据供后续读取:
- 当客户端需要读取Redis响应时,如果当前输入流的缓存字节量不足,该方法会主动从Socket连接中读取数据填充到缓存;
- 同时负责流的边界校验,确保读取操作不会超出有效数据范围。
2. 三个环节均调用ensureFill()的原因
结合你的连接池配置和Jedis的工作机制,各环节调用的原因如下:
(1)从连接池获取连接时
你配置了TestOnBorrow=true,意味着每次从连接池拿连接时,Jedis会自动执行PING命令测试连接有效性。执行PING后需要读取Redis返回的PONG响应,这个读取过程会触发ensureFill()来拉取响应流数据。
(2)读写操作时
Redis读写逻辑本身就依赖输入流读取响应:
- 写入操作后需要读取Redis返回的确认(如
OK); - 读取操作更是直接需要从流中获取目标数据,这些操作都会触发
ensureFill()来填充或读取缓存字节。
(3)关闭连接时
你配置了TestOnReturn=true,归还连接到池时,Jedis同样会执行PING命令校验连接是否可用,读取PONG响应的过程再次触发ensureFill()。
另外,TestWhileIdle=true会在后台定时检测空闲连接时执行PING,这也是额外的ensureFill()调用来源,但不会直接体现在你提到的三个操作环节中。
性能优化参考
既然ensureFill()成为瓶颈,结合你的场景可尝试:
- 调整连接池测试配置:若网络环境稳定,可关闭
TestOnBorrow和TestOnReturn,仅保留TestWhileIdle,减少不必要的PING和响应读取操作; - 调整连接池参数:当前
MaxTotal=200,但并发量为400,可能存在连接等待,可适当调高MaxTotal(注意Azure Redis的连接数上限); - 检查Redis实例负载:1GB标准层Redis在400并发下可能达到性能瓶颈,可考虑升级实例规格,或优化业务逻辑(如批量操作减少请求次数)。
内容的提问来源于stack exchange,提问作者AlwaysDeveloper

