Maison > base de données > tutoriel mysql > Comment Java utilise-t-il l'intercepteur de pilote MySQL pour implémenter des calculs SQL fastidieux ?

Comment Java utilise-t-il l'intercepteur de pilote MySQL pour implémenter des calculs SQL fastidieux ?

WBOY
Libérer: 2023-05-27 13:10:06
avant
1132 Les gens l'ont consulté

Contexte

L'une des exigences de l'entreprise est que le composant de journal de suivi des liens existant de l'entreprise doit prendre en charge l'impression du temps d'exécution SQL de MySQL. La méthode courante pour implémenter le suivi des liens consiste à implémenter l'interface d'intercepteur ou le filtre fourni par un framework ou un outil tiers. . L'interface d'interception ne fait pas exception pour MySQL. En fait, elle implémente simplement l'interface d'intercepteur pilotée par MySQL.

Implémentation spécifique

Les canaux MySQL ont des versions différentes et les interfaces d'intercepteur des différentes versions sont différentes, vous devez donc implémenter l'intercepteur de réponse en fonction des différentes versions du pilote MySQL que vous utilisez. Ensuite, nous présenterons MySQL. canaux séparément. Méthodes d’implémentation des versions 5, 6 et 8.

MySQL5

Ici, nous prenons la version 5.1.18 du canal MySQL comme exemple pour implémenter l'interface StatementInterceptorV2. La principale logique d'implémentation se trouve dans preProcess et postProcess. . , ces deux méthodes sont des méthodes à exécuter avant et après l'exécution de SQL. Le framework que j'utilise est le MDC qui est utilisé ici pour enregistrer un horodatage avant l'exécution de SQL. postProcess MDC .put("sql_exec_time", start);, vous pouvez également utiliser ThreadLocal pour l'implémenter, puis utiliser MDC.get("sql_exec_time") dans le code de la méthode <code>postProcess>, supprimez le temps enregistré avant l'exécution de SQL et soustrayez enfin le temps avant l'exécution de SQL de l'horodatage actuel pour calculer le temps d'exécution de SQL. StatementInterceptorV2接口,主要实现逻辑在preProcesspostProcess方法,这两个方法是sql执行前后要执行的方法,我所使用的框架是logback,这里使用MDC来记录sql执行前的一个时间戳,代码在postProcess方法MDC.put("sql_exec_time", start);,自己也可以使用ThreadLocal等来实现,然后在postProcess方法中使用MDC.get("sql_exec_time")将记录的sql执行前的时间取出来,最后再用当前时间戳减去sql执行前的时间,就算出了sql执行的时间。

import static net.logstash.logback.marker.Markers.append;

import com.mysql.jdbc.Connection;
import com.mysql.jdbc.ResultSetInternalMethods;
import com.mysql.jdbc.Statement;
import com.mysql.jdbc.StatementInterceptorV2;
import com.redick.util.LogUtil;
import java.sql.SQLException;
import java.util.Properties;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;

/**
 * @author Redick01
 */
@Slf4j
public class Mysql5StatementInterceptor implements StatementInterceptorV2 {

    @Override
    public void init(Connection connection, Properties properties) throws SQLException {

    }

    @Override
    public ResultSetInternalMethods preProcess(String s, Statement statement, Connection connection)
            throws SQLException {
        String start = String.valueOf(System.currentTimeMillis());
        MDC.put("sql_exec_time", start);
        log.info(LogUtil.customizeMarker(LogUtil.kLOG_KEY_TRACE_TAG, "sql_exec_before"), "开始执行sql");
        return null;
    }

    @Override
    public boolean executeTopLevelOnly() {
        return false;
    }

    @Override
    public void destroy() {

    }

    @Override
    public ResultSetInternalMethods postProcess(String s, Statement statement,
            ResultSetInternalMethods resultSetInternalMethods, Connection connection, int i,
            boolean b, boolean b1, SQLException e) throws SQLException {
        long start = Long.parseLong(MDC.get("sql_exec_time"));
        long end = System.currentTimeMillis();
        log.info(LogUtil.customizeMarker(LogUtil.kLOG_KEY_TRACE_TAG, "sql_exec_after")
                .and(append(LogUtil.kLOG_KEY_SQL_EXEC_DURATION, end - start)), "结束执行sql");
        return null;
    }
}
Copier après la connexion

MySQL6

MySQL6和MySQL5基本一样,只是接口不是同一个,直接放代码

import static net.logstash.logback.marker.Markers.append;

import com.mysql.cj.api.MysqlConnection;
import com.mysql.cj.api.jdbc.Statement;
import com.mysql.cj.api.jdbc.interceptors.StatementInterceptor;
import com.mysql.cj.api.log.Log;
import com.mysql.cj.api.mysqla.result.Resultset;
import com.redick.util.LogUtil;
import java.sql.SQLException;
import java.util.Properties;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;

/**
 * @author Redick01
 */
@Slf4j
public class Mysql6StatementInterceptor implements StatementInterceptor {

    @Override
    public StatementInterceptor init(MysqlConnection mysqlConnection, Properties properties,
            Log log) {
        return null;
    }

    @Override
    public <T extends Resultset> T preProcess(String s, Statement statement) throws SQLException {
        String start = String.valueOf(System.currentTimeMillis());
        MDC.put("sql_exec_time", start);
        log.info(LogUtil.customizeMarker(LogUtil.kLOG_KEY_TRACE_TAG, "sql_exec_before"), "开始执行sql");
        return null;
    }

    @Override
    public boolean executeTopLevelOnly() {
        return false;
    }

    @Override
    public void destroy() {

    }

    @Override
    public <T extends Resultset> T postProcess(String s, Statement statement, T t, int i, boolean b,
            boolean b1, Exception e) throws SQLException {
        long start = Long.parseLong(MDC.get("sql_exec_time"));
        long end = System.currentTimeMillis();
        log.info(LogUtil.customizeMarker(LogUtil.kLOG_KEY_TRACE_TAG, "sql_exec_after")
                .and(append(LogUtil.kLOG_KEY_SQL_EXEC_DURATION, end - start)), "结束执行sql");
        return null;
    }
}
Copier après la connexion

MySQL8

MySQL8和MySQL5/6的拦截器接口又不一样了,MySQL8的拦截器接口是com.mysql.cj.interceptors.QueryInterceptor,统计sql执行时间的方式还是一样的,代码如下:

import static net.logstash.logback.marker.Markers.append;

import com.mysql.cj.MysqlConnection;
import com.mysql.cj.Query;
import com.mysql.cj.interceptors.QueryInterceptor;
import com.mysql.cj.log.Log;
import com.mysql.cj.protocol.Resultset;
import com.mysql.cj.protocol.ServerSession;
import com.redick.util.LogUtil;
import java.util.Properties;
import java.util.function.Supplier;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;

/**
 * @author Redick01
 */
@Slf4j
public class Mysql8QueryInterceptor implements QueryInterceptor {

    @Override
    public QueryInterceptor init(MysqlConnection mysqlConnection, Properties properties, Log log) {
        return null;
    }

    @Override
    public <T extends Resultset> T preProcess(Supplier<String> supplier, Query query) {
        String start = String.valueOf(System.currentTimeMillis());
        MDC.put("sql_exec_time", start);
        log.info(LogUtil.customizeMarker(LogUtil.kLOG_KEY_TRACE_TAG, "sql_exec_before"), "开始执行sql");
        return null;
    }

    @Override
    public boolean executeTopLevelOnly() {
        return false;
    }

    @Override
    public void destroy() {

    }

    @Override
    public <T extends Resultset> T postProcess(Supplier<String> supplier, Query query, T t,
            ServerSession serverSession) {
        long start = Long.parseLong(MDC.get("sql_exec_time"));
        long end = System.currentTimeMillis();
        log.info(LogUtil.customizeMarker(LogUtil.kLOG_KEY_TRACE_TAG, "sql_exec_after")
                .and(append(LogUtil.kLOG_KEY_SQL_EXEC_DURATION, end - start)), "结束执行sql");
        return null;
    }
}
Copier après la connexion

使用方法

MySQL5和6的使用方式一样,在数据库链接的url中增加如下statementInterceptors参数,例如:

 url: jdbc:mysql://127.0.0.1:3316/log-helper?useUnicode=true&characterEncoding=UTF8&statementInterceptors=com.redick.support.mysql.Mysql5StatementInterceptor&serverTimezone=CST
Copier après la connexion

MySQL8则是在url中增加queryInterceptors

url: jdbc:mysql://127.0.0.1:3316/log-helper?useUnicode=true&characterEncoding=UTF8&queryInterceptors=com.redick.support.mysql.Mysql8QueryInterceptor&serverTimezone=CST
Copier après la connexion

MySQL6

MySQL6 et MySQL5 sont fondamentalement les mêmes, mais l'interface n'est pas la même, il suffit de mettre le code directement

{"@timestamp":"2023-02-28T17:16:29.234+08:00","@version":"0.0.1","message":"开始执行sql","logger_name":"com.redick.support.mysql.Mysql5StatementInterceptor","thread_name":"http-nio-3321-exec-4","level":"INFO","level_value":20000,"traceId":"9ed930dc-4cc6-4719-bf33-9fcb618fd65b","spanId":"1","request_type":"getName","parentId":"0","trace_tag":"sql_exec_before"}
Copier après la connexion

MySQL8

L'interface d'intercepteur de MySQL8 et MySQL5/6 est encore différente, l'interface d'intercepteur de MySQL8 est com.mysql.cj.interceptors.QueryInterceptor, la méthode de comptage du temps d'exécution SQL est toujours la même, le code est le suivant : 🎜
{"@timestamp":"2023-02-28T17:16:29.237+08:00","@version":"0.0.1","message":"结束执行sql","logger_name":"com.redick.support.mysql.Mysql5StatementInterceptor","thread_name":"http-nio-3321-exec-4","level":"INFO","level_value":20000,"traceId":"9ed930dc-4cc6-4719-bf33-9fcb618fd65b","spanId":"1","request_type":"getName","parentId":"0","trace_tag":"sql_exec_after","sql_duration":3}
Copier après la connexion
🎜Méthode d'utilisation🎜🎜MySQL5 et 6 sont utilisés dans de la même manière, ajoutez ce qui suit à l'url des paramètres statementInterceptors du lien de base de données, par exemple : 🎜rrreee🎜MySQL8 ajoute les paramètres queryInterceptors dans l'url, par exemple : 🎜rrreee 🎜Résultats des tests🎜🎜journal de pré-exécution SQL🎜rrreee🎜exécution SQL Après le journal, l'indicateur sql_duration prend 3 ms pour exécuter sql🎜rrreee

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:yisu.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal