假设集群实例的数据分片数量为N,当其中1个数据分片节点发生故障时,该故障节点会启动主备切换,从故障发生至完成切换大约需要几秒到几十秒。在该过程中,访问该数据分片节点的请求均会失败,假设请求分布均匀,理论上该实例会有 1/N 的请求失败率。
但存在以下原因,可能导致该过程中实际失败的请求数量高于理论值。本文以代理(Proxy)模式、2个数据分片节点的集群举例说明。
若实例为直连模式集群,则仅会存在使用多Key命令这一情况。
使用多Key命令
代理模式:Proxy会将多Key命令拆分为多个子命令按照路由表分发给对应的数据分片节点。
直连模式:客户端会将对应子命令发送给对应数据分片节点。
当多Key命令涉及节点故障时,该请求都会失败,如下图所示。
优化方案:减少单个命令中Key的数量,降低数据分片节点故障时多Key命令的失败概率。
客户端使用单连接
部分客户端会采用单连接异步发送请求,例如Lettuce。例如下图中,2个GET请求先后通过一个连接发送给Proxy,由于Redis RESP协议要求请求和回复的顺序一致,当
GET key2
请求因为节点故障无法返回时,GET key2
之后的请求即使已成功执行,客户端也无法收到其响应。优化方案:使用Jedis等支持连接池模型的客户端。
客户端连接池资源耗尽
对于连接池模型的客户端,存在最大连接数的配置,当连接数达到最大且没有空闲连接时,新请求会失败或阻塞。
下图以Jedis客户端为例,如果配置
maxTotal = 3, timeout = 2000
, 在2秒内发起3个GET key2
请求,此时连接数已满(连接池中所有连接都处于使用状态),但由于某个节点故障无法返回,新的请求会根据blockWhenExhausted配置直接失败或阻塞,从而导致用户端请求失败或超时。优化方案:合理配置JedisPool资源池大小,适当降低timeout配置,例如200-300(单位ms,Jedis默认为2000),实现快速失败,避免故障时大量连接处于阻塞状态。