针对数据库连接池到DRDS连接探活的优化

针对数据库连接池到DRDS连接探活的优化

技术教程gslnedu2025-03-09 16:51:134A+A-

简介: 针对数据库连接池到DRDS连接探活的优化


1. 问题背景

近期在给某专有云客户进行云产品应用性能优化分析时,发现了一个有趣的关于DRDS使用层面的问题,这里给大家分享一下。
使用过DRDS产品的同学都知道在DRDS中,未分库分表的数据表会存储在“0号库”上,对于这些表操作的SQL会被分发到“0号库”上执行。所以一般情况下,0号库所在实例的压力会比其它实例的压力稍大一些。近期分析该客户的数据库性能时,发现客户使用的DRDS下0号库所在的RDS实例的压力明显比其它RDS实例高出许多。

图1:SQL语句平均每秒执行次数及事务数


2. 原因分析

通过查看0号库所在的RDS实例的执行SQL发现,有大量的 SELECT 'x' 的查询语句。检查应用侧代码后发现,这个查询语句是应用侧连接池配置的连接探活SQL,所有的连接池实现几乎都有这个功能,可以通过探活SQL检测连接当前是否可用。
那么问题来了:

  1. 为什么只有0号库所在RDS上会有大量此类的语句?
    DRDS中不带表名的(比如 SELECT 'x')SQL和show命令都会被下发到0号库执行。
  2. 对于客户端来说这种连接检测是否有用?

答案一定是有用的,因为如果因网络闪断或其它原因导致的连接状态不可用,即使获取到了连接对象,也不能进行数据访问操作。所以这个检测是有必要的,但对于使用DRDS作为数据源的场景来说,目前配置的检测方式是存在问题的。
对于传统的数据库使用方式,客户端是直接连接到底层数据库的,如下图。探活SQL是直接发到连接的数据库执行,这种场景下使用 SELECT 'x' 检测客户端到数据库的连接是没有问题的。

图2:客户端连接到数据库

而对于使用DRDS作为数据源的场景来说,探活语句在发送到DRDS服务后,会被转发到0号库执行,这就意味着这个探活SQL实际上检测的是客户端-->DRDS-->0号库的链路是否正常。

图3:客户端通过DRDS连接到数据库


这一点可以从DRDS上看 SELECT 'x' 的执行计划得到证实,如下:

图4:执行结果1


实际上,这样的数据源连接检测是没有意义的。因为:

  • 第一,数据源后端实际上只检测了DRDS到0号库的连接状态,DRDS到其它分库的连接状态并未检测。但真正执行SQL时,DRDS是有可能将解析后的SQL下发到其它分库上执行的。
  • 第二,客户端探活SQL的作用主要是为了保证客户端连接池与数据源之间的连接是可用的。对于数据源背后的情况应该由数据源本身维护,即由DRDS本身到RDS的连接池保障连接可用性,而不应该通过客户端的探活功能来保证。
  • 3. 解决方法

    明白以上内容后,我们解决问题的方案就比较清楚了,实际上我们只需要让客户端连接池检测客户端到DRDS的连接状态即可。那有没有这样的检测方法呢?
    答案当然是有的,经过与DRDS研发同学确认,将探活SQL修改为 SELECT 'x' FROM dual 即可。
    修改后,再次在DRDS查看执行计划,如下:

    图5:执行结果2


    在应用侧修改连接池的探活SQL配置后,从0号库所在实例上看,已经看不到探活SQL的执行记录,而且从修改前和修改后0号库所在实例的压力来看,效果也比较明显,0号库的压力相比之前下降了大概80%左右。

    图6:SQL语句平均每秒执行次数及事务数2


    4. 连接池参数配置

    至此,0号库压力过高的问题解决了,下面我们聊聊为什么会有大量的探活语句出现。
    探活机制实际上是数据源连接池通用的一种检测机制,可以检测连接池内的连接对象是否真的可用。拿Druid连接池举例,探活SQL是通过数据源的 validationQuery 属性配置的。与之相关的配置属性还有:testOnBorrow testWhileIdle testOnReturn
    timeBetweenEvictionRunsMillis
    minEvictableIdleTimeMillis。

    官方解释如下:

  • testOnBorrow:申请连接时执行 validationQuery 配置的探活语句检测连接是否有效。
  • testWhileIdle:申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis ,执行 validationQuery 检测连接是否有效。
  • testOnReturn:归还连接时执行 validationQuery 检测连接是否有效。
  • timeBetweenEvictionRunsMillis:有两个含义 1)Destroy线程检测连接的间隔时间,如果连接空闲时间大于等于 minEvictableIdleTimeMillis 则关闭物理连接。
    2)testWhileIdle 的判断依据,详细看 testWhileIdle 属性的说明。
  • minEvictableIdleTimeMillis:连接保持空闲而不被驱逐的最小时间。
  • 文章前面描述的出现大量探活SQL的情况是因为应用将连接池的testOnBorrow设置成了true,所以在每次应用获取连接时,都会执行 validationQuery 配置的探活语句检测连接是否有效。虽然通过前面的优化步骤,已经降低了0号库的压力,使探活语句不下发到0号库执行。

    但探活语句仍会在DRDS实例上执行,DRDS实例的压力并未减轻。通过上面对Druid数据源属性配置的说明可以了解到,如果将 testOnBorrow 或 testOnReturn 打开,会对系统性能有一定的影响,因为每次都会在获取连接时多执行一次查询来检测连接是否可用。因此推荐使用如下的配置:

  • testWhileIdle=true【如果获得的连接为“空闲连接”,则会进行探活检测,如果检测失败,会将此连接从连接池移除,尝试重新从连接池获取连接】
  • timeBetweenEvictionRunsMillis=60000【Destroy线程每隔1分钟对连接池内部的空闲时间>= minEvictableIdleTimeMillis的连接进行探活检测,如果检测失败,会将连接从连接池移除】
  • minEvictableIdleTimeMillis=60000【如果连接闲置1分钟,则认为此连接为“空闲连接“】
  • 这样设置完成后,只有在获取到“空闲连接”时,才会进行探活检测,大大降低了业务高峰时段的探活频率。同时,也可通过适当缩短
    minEvictableIdleTimeMillis 的值,兼顾由于网络闪断或其它原因导致的连接不可用的情况,减少业务出错的概率,在系统性能和可用性之间找到一个平衡点。

    作者:刘维

    本文为阿里云原创内容,未经允许不得转载

    点击这里复制本文地址 以上内容由朽木教程网整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!
    qrcode

    朽木教程网 © All Rights Reserved.  蜀ICP备2024111239号-8