基于JRebel开发的MySQL Explain插件
前言
探索
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
<scope>runtime</scope>
</dependency>
/**
* Actually execute the prepared statement. This is here so server-side
* PreparedStatements can re-use most of the code from this class.
*
* @param maxRowsToRetrieve the max number of rows to return
* @param sendPacket SQL语句packet
* @param createStreamingResultSet should a 'streaming' result set be created?
* @param queryIsSelectOnly is this query doing a SELECT?
*/
protected ResultSetInternalMethods executeInternal(int maxRowsToRetrieve, Buffer sendPacket, boolean createStreamingResultSet, boolean queryIsSelectOnly,
Field[] metadataFromCache, boolean isBatch) throws SQLException {}
实现
- javaagent -- 相对复杂一点,但是很通用,无特殊依赖
- jrebel自定义插件 -- 需要安装jrebel插件,idea和eclipse均支持
1. 新建一个maven项目
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<sdk.version>7.0.0</sdk.version>
</properties> <repositories>
<repository>
<id>zt-public</id>
<url>https://repos.zeroturnaround.com/nexus/content/groups/zt-public</url>
</repository>
</repositories> <dependencies>
<dependency>
<groupId>org.zeroturnaround</groupId>
<artifactId>jr-sdk</artifactId>
<version>${sdk.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.zeroturnaround</groupId>
<artifactId>jr-utils</artifactId>
<version>${sdk.version}</version>
<scope>provided</scope>
</dependency> <dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
<scope>provided</scope>
</dependency>
</dependencies>
2. 编写jrebel CBP
package com.example.plugin.mysql.cbp;
public class MySQLExplainCBP extends JavassistClassBytecodeProcessor {
@Override
public void process(ClassPool cp, ClassLoader cl, CtClass ctClass) throws Exception {
LoggerFactory.getInstance().echo("MySQLExplainCBP...");
// 自定义处理类所在包
cp.importPackage("com.example.plugin.mysql.explain");
// 找到方法:com.mysql.jdbc.PreparedStatement#executeInternal
// 在该方法第一行之前插入自定义逻辑
CtMethod m = ctClass.getDeclaredMethod("executeInternal");
m.insertBefore(
"MySQLExplain.explainSql(this.connection, sendPacket);"
);
}
}
3. 自定义explain逻辑
package com.example.plugin.mysql.explain;
public final class MySQLExplain {
private static final Logger logger = LoggerFactory.getInstance();
/**
* 调用者:MySQLExplainCBP#process
*/
public static void explainSql(com.mysql.jdbc.MySQLConnection conn, com.mysql.jdbc.Buffer sendPacket) {
byte[] bytes = new byte[sendPacket.getPosition()];
System.arraycopy(sendPacket.getByteBuffer(), 0, bytes, 0, bytes.length);
String sql = new String(bytes, 5, bytes.length - 5);
// 前置检查,检查是否执行计划
if (!processBefore(sql)) {
return;
}
try {
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("EXPLAIN " + sql);
List<ExplainResultVo> explainResultList = new ArrayList<>();
while (rs.next()) {
ExplainResultVo explainResultVo = new ExplainResultVo();
explainResultVo.setId(rs.getString("id"));
explainResultVo.setSelectType(rs.getString("select_type"));
explainResultVo.setTable(rs.getString("table"));
explainResultVo.setPartitions(rs.getString("partitions"));
explainResultVo.setType(rs.getString("type"));
explainResultVo.setPossibleKeys(rs.getString("possible_keys"));
explainResultVo.setKey(rs.getString("key"));
explainResultVo.setKeyLen(rs.getString("key_len"));
explainResultVo.setRef(rs.getString("ref"));
explainResultVo.setRows(rs.getString("rows"));
explainResultVo.setFiltered(rs.getString("filtered"));
explainResultVo.setExtra(rs.getString("Extra"));
explainResultList.add(explainResultVo);
}
rs.close();
// 打印结果
analyzeResult(sql, explainResultList);
} catch (Exception se) {
logger.errorEcho("EXPLAIN SQL异常", se);
}
}
/**
* 前置检查,检查是否执行计划,自定添加逻辑
*/
private static boolean processBefore(String sql) {
return true;
}
/**
* 分析结果,打印执行计划,可自定添加过滤条件
*/
private static void analyzeResult(String sql, List<ExplainResultVo> explainResultList) {
ExplainHelper.printExplainResult(sql, explainResultList, showSQL);
}
}
4. 编写jrebel插件入口类
package com.example.plugin.mysql; /**
* 插件注册,需在pom中配置该类
*/
public class MySQLExplainPlugin implements Plugin {
private static final Logger logger = LoggerFactory.getInstance(); @Override
public void preinit() {
Integration integration = IntegrationFactory.getInstance();
ClassLoader cl = getClass().getClassLoader(); logger.echo("MySQL执行计划插件启用, 配置详情: \n" + Config.getMySQLConfTable());
integration.addIntegrationProcessor(
cl,
"com.mysql.jdbc.PreparedStatement",
new MySQLExplainCBP());
} // 指定依赖条件,依赖存在时才启用本插件
@Override
public boolean checkDependencies(ClassLoader cl, ClassResourceSource crs) {
return crs.getClassResource("com.mysql.jdbc.PreparedStatement") != null;
} @Override
public String getId() {
return "mysql-explain-jr-plugin";
} // 省略其余方法,默认返回null即可
}
5. 在pom中指定插件入口类
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<configuration>
<archive>
<manifestEntries>
<JavaRebel-Plugin>com.example.plugin.mysql.MySQLExplainPlugin</JavaRebel-Plugin>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
6. 打包
mvn clean package -Dmaven.test.skip=true
7. 使用该插件
-Drebel.plugins=/your/path/mysql-explain-jr-plugin.jar
8 演示效果
- 启动时输出MySQL执行计划配置:

- 执行计划打印示例:

总结
项目地址
参考
基于JRebel开发的MySQL Explain插件的更多相关文章
- 基于JRebel开发的MybatisPlus热加载插件
前言 前天项目中使用了mybatis-plus,但是搭配Jrebel开发项目时,发现修改mapper的xml,或者mapper方法中的注解,Jrebel并没有能够reload mapper.于是就有了 ...
- 手把手教你基于C#开发WinCC语音报警插件「附源代码」
写在前面 众所周知,WinCC本身是可以利用C脚本或者VBS脚本来做语音报警,但是这种方式的本质是调用已存在的音频文件,想要实现实时播报报警信息是不行的,灵活性还不够,本文主要介绍基于C#/.NET开 ...
- 一些基于jQuery开发的控件
基于jQuery开发,非常简单的水平方向折叠控件.主页:http://letmehaveblog.blogspot.com/2007/10/haccordion-simple-horizontal-a ...
- 「完整案例」基于Socket开发TCP传输客户端
1 程序界面设计 TCP客户端在上位机开发中应用很广,大多数情况下,上位机软件都是作为一个TCP客户端来与PLC或其他服务器进行通信的.TCP客户端的主要功能就是连接服务器.发送数据.接收数据.断开 ...
- mac 下基于firebreath 开发多浏览器支持的浏览器插件
mac 下基于firebreath 开发多浏览器支持的浏览器插件 首先要区分什么是浏览器扩展和浏览器插件;插件可以像本地程序一样做的更多 一. 关于 firebreath http://www.fir ...
- Mysql Explain 解读(基于MySQL 5.6.36)
Mysql Explain 解读(基于MySQL 5.6.36) 1.语法 explain < table_name > #例子 explain select * from t3 wher ...
- Dropdown.js基于jQuery开发的轻量级下拉框插件
Dropdown.js 前言 在SPA(Single Page Application)盛行的时代,jQuery插件的轮子正在减少,由于我厂有需求而开发了这个插件.如果觉得本文对您有帮助,请给个赞,以 ...
- PostgreSQL 优势,MySQL 数据库自身的特性并不十分丰富,触发器和存储过程的支持较弱,Greenplum、AWS 的 Redshift 等都是基于 PostgreSQL 开发的
PostgreSQL 优势 2016-10-20 21:36 686人阅读 评论(0) 收藏 举报 分类: MYSQL数据库(5) PostgreSQL 是一个自由的对象-关系数据库服务器(数据库 ...
- 基于gin的golang web开发:mysql增删改查
Go语言访问mysql数据库需要用到标准库database/sql和mysql的驱动.标准库的Api使用比较繁琐这里再引入另一个库github.com/jmoiron/sqlx. go get git ...
随机推荐
- php laravel5.6引入geetest 行为验证
php laravel5.6引入geetest 行为验证 使用必要性 网站和APP,在所有可能被机器行为攻击的场景,例如但不限于注册.登录.短信接口.查询接口.营销活动.发帖评论等等,都可以部署使用「 ...
- MySQL8.0 zip压缩包版本 Windows下安装
MySQL zip压缩包版本 Windows下安装 Download MySQL Community Server 解压到相应的目录 我的解压目录:D:\Program Files\mysql-8.0 ...
- 【Mac】nsurlsessiond 后台下载问题的解决方法
最近在使用 Mac 系统的时候,经常发现 nsurlsessiond 这个进程,一直在后台下载,非常占用网速.解决方案如下: 通过终端执行下面的语句可以停止后台的自动更新: #!/bin/sh lau ...
- Flink 从0到1学习—— 分享四本 Flink 国外的书和二十多篇 Paper 论文
前言 之前也分享了不少自己的文章,但是对于 Flink 来说,还是有不少新入门的朋友,这里给大家分享点 Flink 相关的资料(国外数据 pdf 和流处理相关的 Paper),期望可以帮你更好的理解 ...
- .NET----错误和异常处理机制
前言 错误的出现并不总是编写程序的人的原因,有时应用程序会因为应用程序的最终用户引发的动作或运行代码的环境发生错误.无论如何,我们都应预测应用程序中出现的错误,并相应的进行编码. .Net改进了处理错 ...
- UEM“探针”技术及用户体验管理
随着互联网产品越来越多,用户群体越来越庞大以及用户品位的多样性增加,我们会发现这样的一个规律,就是相同类型的产品,比如播放器中的QQ影音和暴风影音,再比如小游戏平台中的腾讯游戏和联众等等,他们的功能是 ...
- oracle 断电启动失败:ORA-00600: internal error code, arguments
转载地址: http://www.2cto.com/database/201312/261602.html 由于服务器断电,启动 oracle 时报 ORA-00600 错误 查看 oracle tr ...
- 【Java例题】5.5 两个字符串中最长公共子串
5. 查找两个字符串中含有的最长字符数的公共子串. package chapter5; import java.util.Scanner; public class demo5 { public st ...
- LeetCode 85. 冗余连接 II
题目: 在本问题中,有根树指满足以下条件的有向图.该树只有一个根节点,所有其他节点都是该根节点的后继.每一个节点只有一个父节点,除了根节点没有父节点. 输入一个有向图,该图由一个有着N个节点 (节点值 ...
- apicloud 开发环境搭建
之前做过appcan 手机应用的开发,工作需要切换的apicloud , 开发环境的的搭建是开发的第一步,let's go 1新建应用 step1 注册账号 注册apicloud 账号:https ...