背景
最近有个业务反馈在请求量较大时,即使是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的包,完成连接的探测。
解决
当然总体的解决方案可以有如下几种,其中第二种就是最后所采用的。
- 在中间层添加过滤,不转发给MySQL
- jdbc 会把
/* ping */ select 1
的请求,修改成ping包调用,而中间层针对ping包是直接返回,不会转发到MySQL的,可以减少耗时,同时减少到MySQL的请求。 - 建议业务取消
select 1
的验证。
总结
最近接触到好几个优化问题都和jdbc驱动有关系,可以看到,在jdbc驱动其实已经有很多较为方便好用的优化功能,可以通过简单地修改配置达到想要的效果,避免了许多的开发成本。之后如果有机会可以全部了解下jdbc驱动的源码,做一个集合,以做分享。
网友评论