[Interprétation du code source de druid] - qu'est-ce qu'une requête sql expérimente dans druid ?

Cet article a participé à l'événement "Newcomer Creation Ceremony" pour commencer la route de la création de pépites d'or.

druid - qu'est-ce qu'une requête sql expérimente dans druid?

La configuration du pool de connexions de Druid a la configuration de PreparedStatementCache, qui résout le problème que les instructions SQL peuvent être précompilées et stockées dans l'objet PreparedStatement, et cet objet est stocké dans PreparedStatementCache, qui peut contourner la compilation de la base de données pour oracle.L'amélioration, mais pour mysql, ce n'est pas si évident.

Cet article interprète la méthode executeQuery dans la classe DruidPooledPreparedStatement, en essayant de comprendre comment effectuer le prétraitement, comment exécuter SQL et comment surveiller comment obtenir des données pendant l'exécution SQL. La classe DruidPooledPreparedStatement implémente la méthode executeQuery. La chose la plus importante dans cette méthode est la phrase ResultSet rs = stmt.executeQuery(). stmt est l'objet de classe de la classe PreparedStatementProxyImpl.

Diagramme de classes DruidPooledPreparedStatement

insérez la description de l'image ici

Analyse du code source

//构造方法 核心是:连接池和预处理的持有者
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;
}
复制代码

Exécuter la requête executeQuery diagramme de séquence

insérez la description de l'image ici

Exécuter la requête exécuter le code source de la requête

@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();
    }
}
复制代码

préparéStatement_executeQuery

L'implémentation de la méthode executeQuery par la classe PreparedStatementProxyImpl, qui appelle la méthode createChain() de la classe mère 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

La valeur de retour de cette méthode est un objet de classe de chaîne de filtres FilterChainImpl, la classe FilterChainImpl

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

    return chain;
}
复制代码

Diagramme de classes FilterEventAdapter

insérez la description de l'image ici

Code source de FilterEventAdapter

Lorsque la méthode prepareStatement_executeQuery de la classe FilterChainImpl est exécutée, cette méthode de la classe de filtre nextFilter est exécutée en premier.

@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 classe de filtre (FilterEventAdapter) de la surveillance SQL enregistre les données de surveillance lors de l'exécution SQL. Décrit la source des données de surveillance des druides.

//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;
    }
}
复制代码

Résumer

Aujourd'hui, je me concentre principalement sur la façon dont le sql de la requête est exécuté dans druid, et s'il est surveillé, comment est-il enregistré. Grâce à l'étude d'il y a quelques jours et d'aujourd'hui, je comprends le vrai sens de "druide est né pour la surveillance". La surveillance tout au long de la conception passe par tous les traitements tels que les pics, le nombre de connexions, le temps d'exécution sql, etc. Lors de l'exécution spécifique de sql, les données pertinentes de la surveillance des enregistrements sont interceptées au moyen de Filter. Demain, je prévois d'interpréter le code source de StatFilter pour un monitoring spécifique.

Je suppose que tu aimes

Origine juejin.im/post/7147298686094016520
conseillé
Classement