druid sql监控流程源码分析

·  阅读 266
druid sql监控流程源码分析
简介:

druid号称为监控而生的数据库连接池。 详情请介绍参考官方链接:(github.com/alibaba/dru…) 本次文章只做监控流程的源码分析。初始化等流程一笔带过,不做详解。

流程

首先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的执行结果可以通过获取该对象来获得。

附录流程图

druid监控数据.png

分类:
后端
分类:
后端
收藏成功!
已添加到「」, 点击更改