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
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
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
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.