关于数据库连接验证以及探活请求的优化

背景

最近有个业务反馈在请求量较大时,即使是select 1这种连接验证请求,也会有比较大的耗时。正好之前了解jdbc驱动,有相关方式可以解决,于是提供给业务让修改验证后上线,这里做个过程记录。

分析

业务使用连接池为dbcp , select 1是验证数据库连接可用时候的请求。实际中我们接入的中间件也会将这个SQL转发到MySQL,这是可能增加耗时的原因,毕竟整个执行链路因此变长了,如果其他SQL执行变慢,也会导致这个验证SQL发生争抢和等待,进而增加整体的耗时。。于是想如果能避免类似请求或者避免转发到MySQL,应该可以减少耗时。

正好之前调研已知在jdbc驱动中会将/* ping */开头的SQL转成ping包,正巧我们的中间件收到ping包是直接返回的,就不用再将select 1转发到MySQL执行,可以减少请求消耗。而且,这里由于接入了中间件,业务探活也是针对中间件Proxy这层的,也没有转发到MySQL实例的必要。

jdbc驱动的原理如下:

mysql-connect-java 版本 5.1.45

    protected static final String PING_MARKER = "/* ping */";


 /**
     * Execute a SQL statement that returns a single ResultSet
     * 
     * @param sql
     *            typically a static SQL SELECT statement
     * 
     * @return a ResulSet that contains the data produced by the query
     * 
     * @exception SQLException
     *                if a database access error occurs
     */
    public java.sql.ResultSet executeQuery(String sql) throws SQLException {
        synchronized (checkClosed().getConnectionMutex()) {
            MySQLConnection locallyScopedConn = this.connection;

            this.retrieveGeneratedKeys = false;

            checkNullOrEmptyQuery(sql);

            resetCancelledState();

            implicitlyCloseAllOpenResults();

            if (sql.charAt(0) == '/') {
                if (sql.startsWith(PING_MARKER)) {
                    doPingInstead();

                    return this.results;
                }
            }

            setupStreamingTimeout(locallyScopedConn);

在中间件这层,Proxy收到的包,执行类型就从3(query)变成了14(ping),因此就直接返回OK的包,完成连接的探测。

解决

当然总体的解决方案可以有如下几种,其中第二种就是最后所采用的。

  1. 在中间层添加过滤,不转发给MySQL
  2. jdbc 会把 /* ping */ select 1 的请求,修改成ping包调用,而中间层针对ping包是直接返回,不会转发到MySQL的,可以减少耗时,同时减少到MySQL的请求。
  3. 建议业务取消select 1的验证。

总结

最近接触到好几个优化问题都和jdbc驱动有关系,可以看到,在jdbc驱动其实已经有很多较为方便好用的优化功能,可以通过简单地修改配置达到想要的效果,避免了许多的开发成本。之后如果有机会可以全部了解下jdbc驱动的源码,做一个集合,以做分享。

推荐阅读更多精彩内容