[Interpretación del código fuente de druida]: ¿qué experimenta una consulta sql en druida?

Este artículo ha participado en el evento "Ceremonia de creación de recién llegados" para iniciar el camino de la creación de pepitas de oro.

druida: ¿qué experimenta una consulta sql en druida?

La configuración del grupo de conexiones de Druid tiene la configuración de PreparedStatementCache, que resuelve el problema de que las declaraciones SQL se pueden precompilar y almacenar en el objeto PreparedStatement, y este objeto se almacena en PreparedStatementCache, que puede omitir la compilación de la base de datos para Oracle. La mejora, pero para mysql, no es tan obvio.

Este artículo interpreta el método executeQuery en la clase DruidPooledPreparedStatement, tratando de comprender cómo hacer el preprocesamiento, cómo ejecutar SQL y cómo monitorear cómo obtener datos durante la ejecución de SQL. La clase DruidPooledPreparedStatement implementa el método executeQuery. Lo más importante de este método es la sentencia ResultSet rs = stmt.executeQuery(). stmt es el objeto de clase de la clase PreparedStatementProxyImpl.

Diagrama de clase DruidPooledPreparedStatement

inserte la descripción de la imagen aquí

Análisis de código fuente

//构造方法 核心是:连接池和预处理的持有者
public DruidPooledPreparedStatement(DruidPooledConnection conn, PreparedStatementHolder holder) throws SQLException{
    super(conn, holder.statement);
    this.stmt = holder.statement;
    this.holder = holder;
    this.sql = holder.key.sql;
		// 配置项中是否打开属性poolPreparedStatements
    pooled = conn.getConnectionHolder().isPoolPreparedStatements();
    // Remember the defaults

    if (pooled) {
      //如果打开了这个属性
        try {
          	//获取最大字段大小
            defaultMaxFieldSize = stmt.getMaxFieldSize();
        } catch (SQLException e) {
            LOG.error("getMaxFieldSize error", e);
        }

        try {
          //获取最大行
            defaultMaxRows = stmt.getMaxRows();
        } catch (SQLException e) {
            LOG.error("getMaxRows error", e);
        }

        try {
          //获取查询超时时间
            defaultQueryTimeout = stmt.getQueryTimeout();
        } catch (SQLException e) {
            LOG.error("getMaxRows error", e);
        }

        try {
          //取数方向
            defaultFetchDirection = stmt.getFetchDirection();
        } catch (SQLException e) {
            LOG.error("getFetchDirection error", e);
        }

        try {
          //取数大小
            defaultFetchSize = stmt.getFetchSize();
        } catch (SQLException e) {
            LOG.error("getFetchSize error", e);
        }
    }

    currentMaxFieldSize = defaultMaxFieldSize;
    currentMaxRows = defaultMaxRows;
    currentQueryTimeout = defaultQueryTimeout;
    currentFetchDirection = defaultFetchDirection;
    currentFetchSize = defaultFetchSize;
}
复制代码

Ejecutar consulta ejecutarQuery diagrama de secuencia

inserte la descripción de la imagen aquí

Ejecutar consulta ejecutar código fuente de consulta

@Override
public ResultSet executeQuery() throws SQLException {
  //check 连接
    checkOpen();
		//执行查询的次数++
    incrementExecuteQueryCount();
   //sql 事务记录
    transactionRecord(sql);
   //oracle设置行预取
    oracleSetRowPrefetch();
	// 执行前 running状态变更
    conn.beforeExecute();
    try {
      //实际执行 PreparedStatementProxyImpl 的查询代码详解见下面
        ResultSet rs = stmt.executeQuery();

        if (rs == null) {
            return null;
        }
				//连接池返回结果封装
        DruidPooledResultSet poolableResultSet = new DruidPooledResultSet(this, rs);
      //添加结果集跟踪 用于监控
        addResultSetTrace(poolableResultSet);

        return poolableResultSet;
    } catch (Throwable t) {
        errorCheck(t);

        throw checkException(t);
    } finally {
      //更新连接的running状态
        conn.afterExecute();
    }
}
复制代码

preparadoStatement_executeQuery

La implementación del método executeQuery por la clase PreparedStatementProxyImpl, que llama al método createChain() de la clase padre StatementProxyImpl, prepareStatement_executeQuery

@Override
public ResultSet executeQuery() throws SQLException {
    firstResultSet = true;

    updateCount = null;
    lastExecuteSql = sql;
    lastExecuteType = StatementExecuteType.ExecuteQuery;
    lastExecuteStartNano = -1L;
    lastExecuteTimeNano = -1L;
		// 调用父类createChain 返回FilterChainImpl对象内容,执行FilterChain的preparedStatement_executeQuery方法
    return createChain().preparedStatement_executeQuery(this);
}
复制代码

FilterChainImpl

El valor de retorno de este método es un objeto de clase FilterChainImpl de clase de cadena de filtro, la clase FilterChainImpl

public FilterChainImpl createChain() {
  //获取FilterChainImpl对象
    FilterChainImpl chain = this.filterChain;
    if (chain == null) {
        chain = new FilterChainImpl(this.getConnectionProxy().getDirectDataSource());
    } else {
        this.filterChain = null;
    }

    return chain;
}
复制代码

Diagrama de clases FilterEventAdapter

inserte la descripción de la imagen aquí

Código fuente de FilterEventAdapter

Cuando se ejecuta el método prepareStatement_executeQuery de la clase FilterChainImpl, este método de la clase de filtro nextFilter se ejecuta primero.

@Override
public ResultSetProxy preparedStatement_executeQuery(PreparedStatementProxy statement) throws SQLException {
    if (this.pos < filterSize) {
      // 执行过滤器的方法 SQL监控的过滤器类(FilterEventAdapter)
        return nextFilter().preparedStatement_executeQuery(this, statement);
    }

    ResultSet resultSet = statement.getRawObject().executeQuery();
    if (resultSet == null) {
        return null;
    }
    return new ResultSetProxyImpl(statement, resultSet, dataSource.createResultSetId(),
            statement.getLastExecuteSql());
}
复制代码

La clase de filtro (FilterEventAdapter) de la supervisión de SQL guarda los datos de supervisión durante la ejecución de SQL. Describe la fuente de los datos de seguimiento del druida.

//FilterEventAdapter
//这个类的很巧妙的之处就是采用了设计模式中的模版方法,FilterEventAdapter作为父类实现通用的处理,子类继承这个实现具体的个性话的业务,很适合在实际业务场景中进行业务抽象模型的时候使用这种设计思路
@Override
public ResultSetProxy preparedStatement_executeQuery(FilterChain chain, PreparedStatementProxy statement)
                                                                                                         throws SQLException {
    try {
        //sql实际执行之前 调用的是 如果子类是Log Filter的时候:组装sql执行的日志  如果是Stat Filter则记录对应的监控参数
        statementExecuteQueryBefore(statement, statement.getSql());

        ResultSetProxy resultSet = chain.preparedStatement_executeQuery(statement);

        if (resultSet != null) {
            //子类中Log Filter的方法组装sql执行的日志 or Stat Filter则记录对应的监控参数
            statementExecuteQueryAfter(statement, statement.getSql(), resultSet);
            //子类中Log Filter的方法组装sql执行的日志 or Stat Filter则记录对应的监控参数
            resultSetOpenAfter(resultSet);
        }

        return resultSet;
    } catch (SQLException error) {
        statement_executeErrorAfter(statement, statement.getSql(), error);
        throw error;
    } catch (RuntimeException error) {
        statement_executeErrorAfter(statement, statement.getSql(), error);
        throw error;
    } catch (Error error) {
        statement_executeErrorAfter(statement, statement.getSql(), error);
        throw error;
    }
}
复制代码

Resumir

Hoy, me enfoco principalmente en cómo se ejecuta el sql de la consulta en druid, y si se monitorea, cómo se registra. A través del estudio de hace unos días y hoy, entiendo el verdadero significado de "nace druida para monitorear". El monitoreo a lo largo del diseño se ejecuta a través de todo el procesamiento, como picos, número de conexiones, tiempo de ejecución de sql, etc. Al ejecutar sql específicamente, los datos relevantes del monitoreo de registros son interceptados por medio de Filter. Mañana planeo interpretar el código fuente de StatFilter para un monitoreo específico.

Supongo que te gusta

Origin juejin.im/post/7147298686094016520
Recomendado
Clasificación