简介:
druid号称为监控而生的数据库连接池。
详情请介绍参考官方链接:(https://github.com/alibaba/druid/wiki/Druid%E8%BF%9E%E6%8E%A5%E6%B1%A0%E4%BB%8B%E7%BB%8D)
本次文章只做监控流程的源码分析。初始化等流程一笔带过,不做详解。
流程
首先druid初始化会创建一个DruidDataSource数据源对象。以一次查询为例。当我们请求接口查询用户信息时,会执行DruidDataSource
的getConnection()方法。该方法会返回一个DruidPooledConnection对象。拿到连接对象后会执行该对象的prepareStatement(String sql)方法。接下来我们从DruidPooledConnection的prepareStatement(String sql)开始进行sql监控的源码流程分析。
one
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
//检查状态
checkState();
//生成一个预编译对象
PreparedStatementHolder stmtHolder = null;
PreparedStatementKey key = new PreparedStatementKey(sql, getCatalog(), MethodType.M1);
boolean poolPreparedStatements = holder.isPoolPreparedStatements();
//是否使用池中缓存预编译对象
if (poolPreparedStatements) {
stmtHolder = holder.getStatementPool().get(key);
}
//当预编译对象为空时。conn.prepareStatement(sql)会创建jdbcSqlStat。保存sql执行基本信息
if (stmtHolder == null) {
try {
stmtHolder = new PreparedStatementHolder(key, conn.prepareStatement(sql));
holder.getDataSource().incrementPreparedStatementCount();
} catch (SQLException ex) {
handleException(ex, sql);
}
}
//初始化Statement。会设置查询时间等。
initStatement(stmtHolder);
//创建一个DruidPooledPreparedStatement预编译对象返回给调用方。执行后续流程。
DruidPooledPreparedStatement rtnVal = new DruidPooledPreparedStatement(this, stmtHolder);
holder.addTrace(rtnVal);
return rtnVal;
}
two
拿到DruidPooledConnection的prepareStatement方法返回的DruidPooledPreparedStatement预编译对象后。会执行DruidPooledPreparedStatement对象的execute()方法。
@Override
public boolean execute() throws SQLException {
//检查连接是否开启
checkOpen();
//增加执行次数
incrementExecuteCount();
transactionRecord(sql);
conn.beforeExecute();
try {
//该方法会调用Filter责任链执行preparedStatement_execute方法
return stmt.execute();
} catch (Throwable t) {
errorCheck(t);
throw checkException(t);
} finally {
conn.afterExecute();
}
}
@Override
public boolean preparedStatement_execute(FilterChain chain, PreparedStatementProxy statement) throws SQLException {
//这里会执行对应Filter的statementExecuteBefore方法。监控的Filter为StatFilter。该方法内部实现为StatFilter.internalBeforeStatementExecute(StatementProxy statement, String sql);
statementExecuteBefore(statement, statement.getSql());
//获取执行结果
boolean firstResult = chain.preparedStatement_execute(statement);
//同理执行对应Filter对应方法。此方法对应StatFilter的internalAfterStatementExecute方法
this.statementExecuteAfter(statement, statement.getSql(), firstResult);
//返回执行结果
return firstResult;
}
three
StatFilter的internalBeforeStatementExecute方法跟internalAfterStatementExecute方法会记录sql的执行情况并保存这些结果到JdbcDataSourceStat中。后续查看sql的执行结果可以通过获取该对象来获得。
//执行前
private final void internalBeforeStatementExecute(StatementProxy statement, String sql) {
JdbcDataSourceStat dataSourceStat = statement.getConnectionProxy().getDirectDataSource().getDataSourceStat();
dataSourceStat.getStatementStat().beforeExecute();
final ConnectionProxy connection = statement.getConnectionProxy();
final JdbcConnectionStat.Entry connectionCounter = getConnectionInfo(connection);
statement.setLastExecuteStartNano();
connectionCounter.setLastSql(sql);
JdbcStatContext statContext = JdbcStatManager.getInstance().getStatContext();
boolean inTransaction = false;
try {
inTransaction = !statement.getConnectionProxy().getAutoCommit();
} catch (SQLException e) {
LOG.error("getAutoCommit error", e);
}
if (sqlStat != null) {
sqlStat.setExecuteLastStartTime(System.currentTimeMillis());
sqlStat.incrementRunningCount();
if (inTransaction) {
sqlStat.incrementInTransactionCount();
}
}
StatFilterContext.getInstance().executeBefore(sql, inTransaction);
}
//执行后
private final void internalAfterStatementExecute(StatementProxy statement, boolean firstResult,
int... updateCountArray) {
final long nowNano = System.nanoTime();
final long nanos = nowNano - statement.getLastExecuteStartNano();
JdbcDataSourceStat dataSourceStat = statement.getConnectionProxy().getDirectDataSource().getDataSourceStat();
dataSourceStat.getStatementStat().afterExecute(nanos);
final JdbcSqlStat sqlStat = statement.getSqlStat();
if (sqlStat != null) {
sqlStat.incrementExecuteSuccessCount();
sqlStat.decrementRunningCount();
sqlStat.addExecuteTime(statement.getLastExecuteType(), firstResult, nanos);
statement.setLastExecuteTimeNano(nanos);
if ((!firstResult) && statement.getLastExecuteType() == StatementExecuteType.Execute) {
try {
int updateCount = statement.getUpdateCount();
sqlStat.addUpdateCount(updateCount);
} catch (SQLException e) {
LOG.error("getUpdateCount error", e);
}
} else {
for (int updateCount : updateCountArray) {
sqlStat.addUpdateCount(updateCount);
sqlStat.addFetchRowCount(0);
StatFilterContext.getInstance().addUpdateCount(updateCount);
}
}
long millis = nanos / (1000 * 1000);
if (millis >= slowSqlMillis) {
String slowParameters = buildSlowParameters(statement);
sqlStat.setLastSlowParameters(slowParameters);
String lastExecSql = statement.getLastExecuteSql();
if (logSlowSql) {
LOG.error("slow sql " + millis + " millis. " + lastExecSql + "" + slowParameters);
}
handleSlowSql(statement);
}
}
String sql = statement.getLastExecuteSql();
StatFilterContext.getInstance().executeAfter(sql, nanos, null);
Profiler.release(nanos);
}
总结
初始化数据源会创建DruidDataSource对象。获取连接的时候会调用DruidDataSource.getConnection()方法,该方法会返回DruidPooledConnection连接对象。接着会调用该对象的prepareStatement(String sql)方法。prepareStatement方法会判断是否使用池中缓存预编译对象构建DruidPooledPreparedStatement
对象并返回。接着会调用DruidPooledPreparedStatement的execute()方法执行本次sql。execute()方法会调用
FilterEventAdapter的preparedStatement_execute方法。该方法主要调用对应的Filter实现执行前、执行后操作数据记录等。监控的过滤器实现为StatFilter。执行前对应internalBeforeStatementExecute方法。执行后对呀internalAfterStatementExecute方法。会记录sql的执行情况并保存这些结果到JdbcDataSourceStat中。后续查看sql的执行结果可以通过获取该对象来获得。
附录流程图: