├── .gitattributes ├── .gitignore ├── 1.png ├── 2.png ├── README.md ├── arch.png ├── ddl.sql ├── gravity-agent.jar ├── sample data ├── demo_table_202210222335.csv ├── quartz_202210222335.csv ├── sql_app_configs_202210222335.csv ├── sql_explain_config_rules_202210222335.csv ├── sql_explain_config_rules_script_202210222335.csv ├── sql_explain_info_202210222335.csv ├── sql_explain_stars_202210222335.csv ├── sql_explain_statistics_202210222335.csv ├── sql_explain_table_fingerprint_202210222335.csv └── sql_process_log_202210222335.csv ├── sql-capture-plugin ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── tangym │ └── sql │ └── plugin │ └── v5 │ ├── MybatisMapperProxyAdvice.java │ ├── MybatisPluginDefine.java │ ├── Mysql5xPluginDefine.java │ ├── PreparedStatement5xAdvice.java │ ├── StartedPostProcessor.java │ ├── model │ └── SqlExplainInfo.java │ └── util │ ├── DataPool.java │ ├── HttpUtil.java │ ├── PostUtil.java │ ├── SqlFormatUtil.java │ └── SqlPostTask.java ├── sql-detect-h5 ├── .env ├── .env.development ├── Dockerfile ├── README.md ├── babel.config.js ├── jest.config.js ├── nginx.conf ├── package-lock.json ├── package.json ├── public │ ├── favicon.png │ └── index.html ├── src │ ├── App.vue │ ├── assets │ │ ├── images │ │ │ └── gitlab.png │ │ └── styles │ │ │ ├── antd.less │ │ │ ├── app.less │ │ │ ├── index.less │ │ │ ├── theme.less │ │ │ └── theme │ │ │ ├── fonts │ │ │ ├── element-icons.ttf │ │ │ └── element-icons.woff │ │ │ └── index.css │ ├── components │ │ ├── Footer.vue │ │ ├── LeftSider.vue │ │ └── SubMenu.vue │ ├── main.js │ ├── requests │ │ ├── quartz.js │ │ └── sqlexplain.js │ ├── router.js │ ├── stores │ │ ├── actions.js │ │ ├── getters.js │ │ ├── index.js │ │ ├── modules │ │ │ └── perftest.js │ │ └── mutations.js │ ├── utils │ │ ├── helper.js │ │ └── http.js │ └── views │ │ ├── 404 │ │ └── index.vue │ │ ├── home.vue │ │ ├── sqlananysis │ │ ├── dashboard.vue │ │ ├── index.vue │ │ ├── rules.vue │ │ ├── services.vue │ │ ├── sqls.vue │ │ ├── sqlsDetail.vue │ │ └── tables.vue │ │ └── tool │ │ ├── index.vue │ │ └── tasks.vue └── vue.config.js ├── sql-detect-server ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── tangym │ │ └── sql │ │ └── server │ │ ├── Application.java │ │ ├── config │ │ ├── AfterServiceStarted.java │ │ ├── DataSourceConfig.java │ │ └── SqlExplainDingConfig.java │ │ ├── constant │ │ └── SqlExplainRules.java │ │ ├── controller │ │ ├── QuartzController.java │ │ ├── SqlAppConfigsController.java │ │ ├── SqlCheckConfigRuleScriptController.java │ │ ├── SqlCheckConfigRulesController.java │ │ ├── SqlCheckController.java │ │ ├── SqlProcessLogController.java │ │ ├── SqlStarsController.java │ │ ├── SqlStatisticsController.java │ │ └── SqlTableFingerPrintController.java │ │ ├── dto │ │ ├── request │ │ │ ├── PageRequestDTO.java │ │ │ ├── SqlAppConfigsPageRequest.java │ │ │ ├── SqlDetailListPageRequest.java │ │ │ └── SqlTableHashPageRequest.java │ │ └── response │ │ │ ├── ApiResponse.java │ │ │ ├── ExplainResult.java │ │ │ └── QuartzResponse.java │ │ ├── entity │ │ ├── Quartz.java │ │ ├── SqlAppConfigs.java │ │ ├── SqlExplainConfigRules.java │ │ ├── SqlExplainConfigRulesScript.java │ │ ├── SqlExplainInfo.java │ │ ├── SqlExplainStars.java │ │ ├── SqlExplainStatistics.java │ │ ├── SqlExplainTableFingerprint.java │ │ └── SqlProcessLog.java │ │ ├── mapper │ │ ├── QuartzMapper.java │ │ ├── SqlAppConfigsMapper.java │ │ ├── SqlExplainConfigRulesMapper.java │ │ ├── SqlExplainConfigRulesScriptMapper.java │ │ ├── SqlExplainInfoMapper.java │ │ ├── SqlExplainStarsMapper.java │ │ ├── SqlExplainStatisticsMapper.java │ │ ├── SqlExplainTableFingerprintMapper.java │ │ └── SqlProcessLogMapper.java │ │ ├── scheduler │ │ ├── SqlExplainClearDataTask.java │ │ ├── SqlExplainDingTalkAlertTask.java │ │ ├── SqlExplainFailScheduleTask.java │ │ ├── SqlExplainScheduleTask.java │ │ ├── SqlExplainStatisticsTask.java │ │ ├── SqlExplainTableHash.java │ │ └── SqlExplainTaskCommon.java │ │ └── util │ │ └── QuartzUtils.java │ └── resources │ ├── application.yml │ └── mapper │ ├── QuartzMapper.xml │ ├── SqlAppConfigsMapper.xml │ ├── SqlExplainConfigRulesMapper.xml │ ├── SqlExplainConfigRulesScriptMapper.xml │ ├── SqlExplainInfoMapper.xml │ ├── SqlExplainStarsMapper.xml │ ├── SqlExplainStatisticsMapper.xml │ ├── SqlExplainTableFingerprintMapper.xml │ └── SqlProcessLogMapper.xml ├── sql-service-demo ├── .gitignore ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── tangym │ │ │ └── sqlservice │ │ │ └── demo │ │ │ ├── SqlServiceDemoApplication.java │ │ │ ├── config │ │ │ └── DataSourceConfig.java │ │ │ ├── controller │ │ │ └── DemoController.java │ │ │ ├── domain │ │ │ └── DemoTable.java │ │ │ └── mapper │ │ │ └── DemoTableMapper.java │ └── resources │ │ ├── application.properties │ │ └── mapper │ │ └── DemoTableMapper.xml │ └── test │ └── java │ └── com │ └── tangym │ └── sqlservice │ └── demo │ └── SqlServiceDemoApplicationTests.java ├── sql-spring-plugin ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── tangym │ └── sql │ └── spring │ └── plugin │ ├── ArgumentPrintAspect.java │ ├── BeanDefinitionRegistryProcessor.java │ ├── RegisterBeanAdvice.java │ └── SpringPluginDefine.java └── wechat.png /.gitattributes: -------------------------------------------------------------------------------- 1 | *.css linguist-language=java 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .settings 3 | .idea 4 | rebel.xml 5 | *.iml 6 | target/ 7 | .DS_Store 8 | node_modules/ 9 | -------------------------------------------------------------------------------- /1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangyiming/sql-detect/657febab417d30ca0863f40a0d50279673506c6b/1.png -------------------------------------------------------------------------------- /2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangyiming/sql-detect/657febab417d30ca0863f40a0d50279673506c6b/2.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 慢SQL检测 2 | 3 | > *B站启动与使用演示视频*:https://www.bilibili.com/video/BV1EG4y1H7ov/ 4 | 5 | 这个平台我是阅读参考了Tester Home上文章 [使用插桩技术解决慢查询测试问题](https://testerhome.com/topics/29228) 后实现的,之前在公司内部使用,现在改造开源了出来。由于时间仓促,改造中如果导致了Bug,希望各位提Issue告诉我,或者你帮助我修改也可以。 6 | 7 | ## 代码介绍 8 | 项目主要分四个部分: 9 | - sql-capture-plugin 10 | 11 | 一个sql语句的捕获插件,基于 [Gravity](https://github.com/ymm-tech/gravity) 的Java Agent开发,这是运满满开源的一款简洁优秀的字节码增强的AOP框架,得益于Gravity的开源,本项目才能够脱离我前公司环境构建并开源。 12 | 13 | - sql-spring-plugin 14 | 15 | 一个Spring插件,用于应用加载capture插件后,可以先去调用服务端接口获取一下捕获规则 16 | 17 | - sql-detect-server 18 | 19 | 我们的平台服务端 20 | 21 | - sql-detect-h5 22 | 23 | 我们的平台前端 24 | 25 | - sql-service-demo 26 | 27 | 一个演示应用,可以用它来调试代码,查看捕获效果 28 | 29 | ## 准备工作 30 | 1. 根据DDL文件里的语句,建数据库和数据表 31 | 2. 导入sample data文件夹中的数据到响应库表中 32 | 3. clone [Gravity](https://github.com/ymm-tech/gravity) 项目,并本地编译打包好agent包,以及后续所需的依赖gravity-plugin-api包 33 | 4. Maven 打包好我们的插件 sql-capture-plugin 和 sql-spring-plugin 34 | 5. 启动平台前后端 35 | 6. 启动演示demo应用,启动前修改启动配置:-javaagent:/YOUR-PATH/gravity-agent.jar=appName=sql-service-demo,localDebug=true 36 | 7. 第一次启动后,用户根目录会创建好.gravity的目录,将我们的插件拷贝到.gravity/sql-service-demo/agent/下,重新启动应用,其他应用同理拷贝到对应目录下。 37 | 38 | 这样,你就可以把玩应用,发起sql调用,观察捕获和数据分析了。 39 | 40 | ## 重要 41 | 这个平台的核心在于规则,使用什么样的规则来认定是慢查询,又需要忽略哪些语句,这需要大量的语句分析后经验的积累与优化了,我内置的规则只是最粗暴的一种,你需要自己结合实践去精细化他们。 42 | 43 | ## 优化,留给你自己来做了 44 | - 数据上报使用消息中间件 45 | - 定时任务管理使用XXL-JOB 46 | - 智能化与问题相似度分析 47 | - sql快慢等级 48 | 49 | ## 截图(部分功能未在截图内体现) 50 |  51 | 52 |  53 | 54 | ## 架构图 55 |  56 | 57 | ## 关于 Explain 58 | Explain从Mysql 5.6开始并不会执行子查询,所以应用接入后不用担心影响原本DB性能,同时在5.6之后Explain开始在SELECT基础上增加支持 UPDATE/INSERT/DELETE/REPLACE等语句的执行计划解释(内部做了近似转换为Select语句)。 59 | Explain命令执行不依赖数据量进行分析,所以在线下来说是个相对客观的分析,但是他只是一个脱离业务场景的纯执行解释的近似分析,最终判断,需要研发人员自己处理定夺。 60 | 61 | ## SQL语句捕获插件 62 | 63 | ### 1、拦截点 64 | 基于Java agent能力,开发插件,用于捕获应用发起的SQL调用信息。暂时只考虑针对mysql 5.x的版本进行插件实现。 65 | 为了简化拦截,可以一次性拿到全部底层的sql连接与语句信息,选择在jdbc驱动层对mysql-connector-java进行拦截,而不是直接在mybatis层拦截,只有mybaits的sql id需要从mybatis层拦截获取。 66 | 这样我们可以拦截到sql的mybatis的xml文件中的id,以及sql的原始语句,参数化拼接后的语句,与db连接所需的一切信息。获取到信息后进行数据上报存储。 67 | 68 | ### 2、数据上报策略 69 | 采用Http协议,调用服务端接口上报信息,同时使用ArraryBlockingQueue进行定时批量上报,减少连接数,直接在上报前也可以过滤掉很多重复SQL,优化性能。当然了也可以采用Websocket连接上报信息,或是引用MQ中间件临时存储需要分析的SQL语句,不过作为agent插件来说,引入这些有点重了。 70 | 71 | ## 服务端设计 72 | 73 | ### 1、数据上报开关 74 | 应用可以通过开关实时控制SQL上报记录分析与否,因为在服务端判断,所以agent端不会改变上报策略的停止与否。如果想在agent端进行控制,那么可以选择使用线程定时任务去拉最新的开关状态,也可以内置httpserver,通过外部接口调用来实时改变开关状态。 75 | ### 2、SQL过滤与保存 76 | 考虑到执行SQL的数量多,重复率高,时效性强,分析次数多等特性,需要对SQL语句设置必要的过滤策略: 77 | 同应用24H(未来考虑增加应用级别的过滤时间段可配置化)内相同的原始sql,无论有无分析过,只保存一条; 78 | 识别分库分表的相同语句进行重复过滤,对分库分表的表结构进行去重存储; 79 | 服务端应用慢查相关表语句过滤,避免循环调用,或者关闭服务端的插件; 80 | 过滤掉SELECT count(0) FROM 这种 PageHelper 插件分页查询时先执行的语句; 81 | 自定义需要过滤的sql语句; 82 | ### 3、SQL Explain分析的执行 83 | 通过jdbc直连DB进行执行explain分析,这样可以避免需要修改服务端配置来做db连接; 84 | 丢弃超过上报时段24小时,仍然一直分析执行失败的sql语句; 85 | 对于分析失败的语句分析时间间隔不应太短; 86 | ### 4、定时任务 87 | 通过Quartz定时任务来消费待分析的SQL。 88 | 定时任务:慢SQL检测 89 | 批量获取未做explain的语句进行分析执行;采用串行任务,防止任务执行时间过长,多任务拖死服务。 90 | 定时任务:慢SQL失败任务重检测 91 | 批量获取24H内执行分析失败的任务,重试分析。 92 | 定时任务:慢SQL定时告警 93 | 定时汇总应用的分析结果为慢查询的SQL语句数量,钉钉发送预警给应用负责人。 94 | 定时任务:DB表结构Hash值计算 95 | 定时的为落库的表结构进行Hash计算并存储值与计算时间。 96 | ### 5、慢查询的判断规则 97 | 对于explain结果key值为Null的,或者type值为 ALL,index,range其一的,将标记为慢查。 98 | 具体explain结果解释可参考官方文档:https://dev.mysql.com/doc/refman/5.7/en/explain-output.html#explain-join-types,也可以参考《高性能MySQL 第3版》附录D Explain章节。 99 | ### 6、慢查预警后的处理 100 | 你可以进行以下三种选择,确认非慢查忽略后的SQL,再DB表发生变更前,将不会再进行预警。 101 | - 确认非慢查-忽略 102 | - 确认慢查-已优化 103 | - 确认慢查-待处理 104 | 需要注意的是,你的每一次操作,都会被记录,如果发生线上问题,可以作为原因回溯。 105 | ### 7、汇总指标 106 | 分析总数:应用所有上报已执行分析数 107 | 108 | 慢查总数:应用所有标记为慢查的数量,已处理为非慢查的不算入其中 109 | 110 | 慢查占比 = 慢查总数 / 分析总数 (慢查为零则占比为 0%) 111 | 112 | 近7日新增慢查SQL数 = 慢查未处理数 + 慢查已处理数 (7日内) 113 | 114 | 慢查占比与健康度关系 115 | - <= 0.1% 优秀 116 | - <= 1.0% 良 117 | - <= 2.0% 中 118 | - 其他 差 119 | 120 | ## 慢查询判断过滤逻辑 121 | 122 | explain 时,如果本条数据为慢查,查找相同original sql的上一条数据: 123 | 124 | 如果上一条为慢查,直接标记为慢查; 125 | 126 | 如果上一条为非慢(无论explain的结果还是人工修改的),且没有指纹(即没有更新时间),则跳过比较,直接标记为非慢。 127 | 128 | 如果上一条为非慢(无论explain的结果还是人工修改的),且表hash的更新时间早于上一条数据的创建时间,那么认为表结构无变更,标记为非慢; 129 | 130 | 如果上一条为非慢(无论explain的结果还是人工修改的),且表hash的更新时间晚于上一条数据的创建时间,那么认为表结构有变更,标记为慢查; 131 | 132 | -------------------------------------------------------------------------------- /arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangyiming/sql-detect/657febab417d30ca0863f40a0d50279673506c6b/arch.png -------------------------------------------------------------------------------- /ddl.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE `sql_detect` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci */ 2 | 3 | create table demo_table 4 | ( 5 | id int auto_increment 6 | primary key, 7 | user varchar(32) null, 8 | age int null 9 | ); 10 | 11 | create table quartz 12 | ( 13 | id int auto_increment comment 'ID', 14 | job_name varchar(32) not null comment '任务名称', 15 | job_class varchar(64) not null comment '任务执行类', 16 | cron_expression varchar(64) not null comment '任务运行时间表达式', 17 | run_after_start tinyint(1) default 0 not null comment '是否在应用启动后运行 1是 0否', 18 | constraint quartz_id_uindex 19 | unique (id) 20 | ) 21 | comment '定时任务' charset=utf8mb4; 22 | 23 | alter table quartz 24 | add primary key (id); 25 | 26 | create table sql_app_configs 27 | ( 28 | id int auto_increment, 29 | service varchar(32) not null comment '应用名', 30 | explain_switch tinyint(1) default 1 null comment '应用级别explain检查开关 1开启 0关闭', 31 | users json null comment '应用负责人列表,通知到SQL分析群里哪些人', 32 | ding_token varchar(70) null comment '钉群机器人token', 33 | constraint sql_app_configs_id_uindex 34 | unique (id), 35 | constraint sql_app_configs_service_uindex 36 | unique (service) 37 | ) 38 | comment '慢查应用通知配置信息' charset=utf8mb4; 39 | 40 | alter table sql_app_configs 41 | add primary key (id); 42 | 43 | create table sql_explain_config_rules 44 | ( 45 | id int auto_increment comment '主键ID', 46 | rule_name varchar(32) null comment '规则名称', 47 | rule_detail varchar(100) not null comment '规则内容', 48 | rule_key varchar(16) null comment '唯一键,识别规则所属', 49 | constraint sql_explain_config_rules_id_uindex 50 | unique (id), 51 | constraint sql_explain_config_rules_name_uindex 52 | unique (rule_name), 53 | constraint sql_explain_config_rules_rule_key_uindex 54 | unique (rule_key) 55 | ) 56 | comment '慢sql动态规则配置' charset=utf8mb4; 57 | 58 | alter table sql_explain_config_rules 59 | add primary key (id); 60 | 61 | create table sql_explain_config_rules_script 62 | ( 63 | id int auto_increment, 64 | script varchar(256) null comment '规则脚本', 65 | remark varchar(256) null comment '规则备注信息', 66 | constraint sql_explain_config_rules_script_id_uindex 67 | unique (id) 68 | ) 69 | comment '慢查忽略规则表' charset=utf8mb4; 70 | 71 | alter table sql_explain_config_rules_script 72 | add primary key (id); 73 | 74 | create table sql_explain_info 75 | ( 76 | id int auto_increment, 77 | service_name varchar(32) null comment '服务名称/应用名称', 78 | original_sql text null comment '原始不填参数的sql语句', 79 | parameterized_sql text null comment '最终可以执行的参数化后的sql', 80 | server_version int null comment 'mysql server 版本 5 or 8', 81 | explain_res json null comment 'explain结果', 82 | is_slow tinyint(1) null comment '是否是慢查sql', 83 | is_prod_fault tinyint(1) null comment '线上是否出慢查故障', 84 | db_host varchar(64) null comment 'host', 85 | db_port varchar(4) null comment '端口号', 86 | db_name varchar(64) null comment '数据库名', 87 | db_user varchar(32) null comment '用户名', 88 | db_pwd varchar(16) null comment '密码', 89 | is_failed int default 0 null comment '执行explain是否失败 0否 1是', 90 | is_alert tinyint(1) null comment '是否已告警', 91 | remark varchar(100) null comment '备注信息', 92 | create_time timestamp null comment '创建时间', 93 | update_time timestamp null comment '更新时间', 94 | tb_fingerprints json null comment '关联hash签名表id列表', 95 | original_sql_hash varchar(32) null comment '原始语句的sql hash值', 96 | sql_id varchar(256) null comment 'sql mybatis id 全路径', 97 | constraint sql_explain_info_id_uindex 98 | unique (id) 99 | ) 100 | comment '慢查sql信息表' charset=utf8mb4; 101 | 102 | create index sql_explain_info_is_alert_index 103 | on sql_explain_info (is_alert); 104 | 105 | create index sql_explain_info_is_failed_index 106 | on sql_explain_info (is_failed); 107 | 108 | create index sql_explain_info_is_slow_index 109 | on sql_explain_info (is_slow); 110 | 111 | create index sql_explain_info_original_sql_hash_index 112 | on sql_explain_info (original_sql_hash); 113 | 114 | create index sql_explain_info_service_name_index 115 | on sql_explain_info (service_name); 116 | 117 | alter table sql_explain_info 118 | add primary key (id); 119 | 120 | create table sql_explain_stars 121 | ( 122 | id int auto_increment comment '主键ID', 123 | job_number varchar(8) not null comment '工号', 124 | service_name varchar(32) not null comment '关注应用', 125 | constraint sql_explain_stars_id_uindex 126 | unique (id) 127 | ) 128 | comment '关注应用信息表' charset=utf8mb4; 129 | 130 | alter table sql_explain_stars 131 | add primary key (id); 132 | 133 | create table sql_explain_statistics 134 | ( 135 | id int auto_increment, 136 | service_name varchar(32) null comment '应用名', 137 | health varchar(16) null comment '健康度', 138 | slow_percent varchar(8) null comment '慢查占比', 139 | latest_slow_in_seven int null comment '近七日新增慢查数', 140 | slow_total int null comment '慢查总数', 141 | explain_total int null comment '分析总数', 142 | calc_time timestamp null comment '汇总时间点,以后从此时间点进行数据统计,做累加', 143 | constraint sql_explain_statistics_id_uindex 144 | unique (id), 145 | constraint sql_explain_statistics_service_name_uindex 146 | unique (service_name) 147 | ) 148 | comment '汇总数据表' charset=utf8mb4; 149 | 150 | alter table sql_explain_statistics 151 | add primary key (id); 152 | 153 | create table sql_explain_table_fingerprint 154 | ( 155 | id int auto_increment, 156 | db_name varchar(64) not null comment '数据库名', 157 | db_host varchar(64) null comment 'host', 158 | db_port varchar(4) null comment 'port', 159 | db_user varchar(32) null comment '用户名', 160 | db_pwd varchar(16) null comment '密码', 161 | tb_name varchar(64) not null comment '表名', 162 | fingerprint varchar(32) null comment 'md5 hash值', 163 | create_time timestamp null comment '创建时间', 164 | update_time timestamp null comment '更新时间', 165 | constraint sql_explain_table_fingerprint_id_uindex 166 | unique (id) 167 | ) 168 | comment '数据表指纹' charset=utf8mb4; 169 | 170 | create index sql_explain_table_fingerprint_db_name_index 171 | on sql_explain_table_fingerprint (db_name); 172 | 173 | create index sql_explain_table_fingerprint_tb_name_index 174 | on sql_explain_table_fingerprint (tb_name); 175 | 176 | alter table sql_explain_table_fingerprint 177 | add primary key (id); 178 | 179 | create table sql_process_log 180 | ( 181 | id int auto_increment, 182 | explain_info_id int null comment '记录关联sql数据id', 183 | user_id varchar(8) null comment '操作人工号', 184 | user_name varchar(8) null comment '操作人姓名', 185 | type varchar(16) null comment '操作类型', 186 | remark varchar(100) null comment '备注', 187 | create_time timestamp null comment '记录创建时间', 188 | update_time timestamp null comment '记录修改时间', 189 | constraint sql_process_log_id_uindex 190 | unique (id) 191 | ) 192 | comment '慢查sql数据操作记录' charset=utf8mb4; 193 | 194 | create index sql_process_log_explain_info_id_index 195 | on sql_process_log (explain_info_id); 196 | 197 | alter table sql_process_log 198 | add primary key (id); 199 | 200 | -------------------------------------------------------------------------------- /gravity-agent.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangyiming/sql-detect/657febab417d30ca0863f40a0d50279673506c6b/gravity-agent.jar -------------------------------------------------------------------------------- /sample data/demo_table_202210222335.csv: -------------------------------------------------------------------------------- 1 | "id","user","age" 2 | 11,tangyiming,20 3 | -------------------------------------------------------------------------------- /sample data/quartz_202210222335.csv: -------------------------------------------------------------------------------- 1 | "id","job_name","job_class","cron_expression","run_after_start" 2 | 1,慢SQL检测,com.tangym.sql.server.scheduler.SqlExplainScheduleTask,*/30 * * * * ?,1 3 | 2,慢SQL失败任务重检测,com.tangym.sql.server.scheduler.SqlExplainFailScheduleTask,"15 0 * * * ?",1 4 | 3,慢SQL定时告警,com.tangym.sql.server.scheduler.SqlExplainDingTalkAlertTask,"20 0 10-20/2 ? * MON,TUE,WED,THU,FRI *",1 5 | 4,DB表结构Hash值计算,com.tangym.sql.server.scheduler.SqlExplainTableHash,"50 0 3 * * ?",1 6 | 5,应用汇总信息计算,com.tangym.sql.server.scheduler.SqlExplainStatisticsTask,"40 */30 * ? * *",1 7 | 6,上报SQL清理(保留最近半个月数据),com.tangym.sql.server.scheduler.SqlExplainClearDataTask,"0 0 0 ? * FRI *",1 8 | -------------------------------------------------------------------------------- /sample data/sql_app_configs_202210222335.csv: -------------------------------------------------------------------------------- 1 | "id","service","explain_switch","users","ding_token" 2 | 2,sql-service-demo,1,"[{""tel"": ""13770888888"", ""name"": ""eamon""}]", 3 | -------------------------------------------------------------------------------- /sample data/sql_explain_config_rules_202210222335.csv: -------------------------------------------------------------------------------- 1 | "id","rule_name","rule_detail","rule_key" 2 | 1,SQL拦截语句规则,"{""capture"":[""select""]}",capture 3 | 2,慢SQL判断规则,"{""keySet"":[""NULL""],""typeSet"":[""index"",""ALL""]}",criteria 4 | -------------------------------------------------------------------------------- /sample data/sql_explain_config_rules_script_202210222335.csv: -------------------------------------------------------------------------------- 1 | "id","script","remark" 2 | 3,"null != explainResult.getExtra() && explainResult.getExtra().equalsIgnoreCase(""Impossible WHERE noticed after reading const tables"")","""extra"": ""Impossible WHERE noticed after reading const tables"" 这种结果判断改为非慢查" 3 | 4,"null != explainResult.getSelectType() && explainResult.getSelectType().equalsIgnoreCase(""UNION RESULT"")","""selectType"": ""UNION RESULT"" 这种结果判断改为非慢查" 4 | -------------------------------------------------------------------------------- /sample data/sql_explain_info_202210222335.csv: -------------------------------------------------------------------------------- 1 | "id","service_name","original_sql","parameterized_sql","server_version","explain_res","is_slow","is_prod_fault","db_host","db_port","db_name","db_user","db_pwd","is_failed","is_alert","remark","create_time","update_time","tb_fingerprints","original_sql_hash","sql_id" 2 | 3,sql-service-demo,"select id,user,age from demo_table where id = ?","select id,user,age from demo_table where id = 11",5,"[{""key"": ""PRIMARY"", ""ref"": ""const"", ""rows"": 1, ""type"": ""const"", ""table"": ""demo_table"", ""keyLen"": 4, ""filtered"": 100, ""selectType"": ""SIMPLE"", ""possibleKeys"": ""PRIMARY""}]",0,,localhost,"3306",sql_detect,root,root,,,,"2022-10-22 23:06:01","2022-10-22 23:10:53","[3]","0009D933A8EB03EC67C0CAE14669C012",com.tangym.sqlservice.demo.mapper.DemoTableMapper#selectByPrimaryKey 3 | -------------------------------------------------------------------------------- /sample data/sql_explain_stars_202210222335.csv: -------------------------------------------------------------------------------- 1 | "id","job_number","service_name" 2 | -------------------------------------------------------------------------------- /sample data/sql_explain_statistics_202210222335.csv: -------------------------------------------------------------------------------- 1 | "id","service_name","health","slow_percent","latest_slow_in_seven","slow_total","explain_total","calc_time" 2 | 2,sql-service-demo,excellent,"0.00",0,0,1,"2022-10-22 23:30:40" 3 | -------------------------------------------------------------------------------- /sample data/sql_explain_table_fingerprint_202210222335.csv: -------------------------------------------------------------------------------- 1 | "id","db_name","db_host","db_port","db_user","db_pwd","tb_name","fingerprint","create_time","update_time" 2 | 3,sql_detect,localhost,"3306",root,root,demo_table,CD0C36336E6CDE12789E2AFCC20DEC68,"2022-10-22 23:06:01","2022-10-22 23:10:17" 3 | -------------------------------------------------------------------------------- /sample data/sql_process_log_202210222335.csv: -------------------------------------------------------------------------------- 1 | "id","explain_info_id","user_id","user_name","type","remark","create_time","update_time" 2 | -------------------------------------------------------------------------------- /sql-capture-plugin/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.tangym 8 | sql-capture-plugin 9 | 0.0.1-SNAPSHOT 10 | sql-capture-plugin 11 | sql-capture-plugin 12 | 13 | 14 | agent 15 | UTF-8 16 | 1.8 17 | ${java.version} 18 | ${java.version} 19 | 20 | 21 | 22 | 23 | io.manbang 24 | gravity-plugin-api 25 | 1.0.0 26 | provided 27 | 28 | 29 | mysql 30 | mysql-connector-java 31 | 5.1.49 32 | provided 33 | 34 | 35 | com.google.auto.service 36 | auto-service 37 | 1.0-rc7 38 | true 39 | provided 40 | 41 | 42 | org.apache.logging.log4j 43 | log4j-slf4j-impl 44 | 2.7 45 | provided 46 | 47 | 48 | org.projectlombok 49 | lombok 50 | 1.18.12 51 | provided 52 | 53 | 54 | com.squareup.okhttp3 55 | okhttp 56 | 3.14.9 57 | provided 58 | 59 | 60 | com.alibaba 61 | fastjson 62 | 1.2.75 63 | provided 64 | 65 | 66 | com.tangym 67 | sql-spring-plugin 68 | 0.0.1-SNAPSHOT 69 | provided 70 | 71 | 72 | 73 | 74 | 75 | 76 | org.apache.maven.plugins 77 | maven-jar-plugin 78 | 79 | ${jar-outputDirectory} 80 | 81 | 82 | ${jar.type} 83 | ${project.groupId} 84 | ${project.artifactId} 85 | ${project.version} 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | local 96 | 97 | false 98 | 99 | 100 | ${user.home}/.gravity/${jar.type} 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /sql-capture-plugin/src/main/java/com/tangym/sql/plugin/v5/MybatisMapperProxyAdvice.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.plugin.v5; 2 | 3 | import com.tangym.sql.plugin.v5.util.DataPool; 4 | import io.manbang.gravity.plugin.Advice; 5 | import io.manbang.gravity.plugin.ExecuteContext; 6 | import lombok.extern.slf4j.Slf4j; 7 | 8 | import java.lang.reflect.Method; 9 | 10 | /** 11 | * @author : yiming.tang 12 | */ 13 | @Slf4j 14 | public class MybatisMapperProxyAdvice implements Advice { 15 | @Override 16 | public void enterMethod(ExecuteContext context) { 17 | Method method = context.getArgument(1); 18 | String mapper = method.getDeclaringClass().getName(); 19 | String sqlId = method.getName(); 20 | DataPool.sqlIdThreadLocal.set(mapper+"#"+sqlId); 21 | } 22 | 23 | @Override 24 | public void exitMethod(ExecuteContext context) { 25 | DataPool.sqlIdThreadLocal.remove(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /sql-capture-plugin/src/main/java/com/tangym/sql/plugin/v5/MybatisPluginDefine.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.plugin.v5; 2 | 3 | import com.google.auto.service.AutoService; 4 | import io.manbang.gravity.plugin.Plugin; 5 | import io.manbang.gravity.plugin.PluginDefine; 6 | import io.manbang.gravity.plugin.Witness; 7 | import net.bytebuddy.description.type.TypeDescription; 8 | import net.bytebuddy.matcher.ElementMatcher; 9 | 10 | import static net.bytebuddy.matcher.ElementMatchers.named; 11 | 12 | /** 13 | * 增强mybatis,获取sql id 14 | * @author : yiming.tang 15 | */ 16 | @AutoService(PluginDefine.class) 17 | public class MybatisPluginDefine implements PluginDefine { 18 | private static final String MAPPER_PROXY_CLASS_NAME = "org.apache.ibatis.binding.MapperProxy"; 19 | 20 | @Override 21 | public ElementMatcher getTypeMatcher() { 22 | return named(MAPPER_PROXY_CLASS_NAME); 23 | } 24 | 25 | @Override 26 | public Plugin[] getPlugins() { 27 | return new Plugin[]{ 28 | Plugin.advice(named("invoke"), 29 | MybatisMapperProxyAdvice.class.getName()) 30 | }; 31 | } 32 | 33 | @Override 34 | public Witness getWitness() { 35 | return Witness.classes(MAPPER_PROXY_CLASS_NAME); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sql-capture-plugin/src/main/java/com/tangym/sql/plugin/v5/Mysql5xPluginDefine.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.plugin.v5; 2 | 3 | import com.google.auto.service.AutoService; 4 | import io.manbang.gravity.plugin.Plugin; 5 | import io.manbang.gravity.plugin.PluginDefine; 6 | import io.manbang.gravity.plugin.Witness; 7 | import net.bytebuddy.description.type.TypeDescription; 8 | import net.bytebuddy.matcher.ElementMatcher; 9 | 10 | import static net.bytebuddy.matcher.ElementMatchers.named; 11 | 12 | /** 13 | * 增强jdbc驱动 获取sql信息 14 | * @author : yiming.tang 15 | */ 16 | @AutoService(PluginDefine.class) 17 | public class Mysql5xPluginDefine implements PluginDefine { 18 | private static final String PREPARED_STATEMENT_CLASS_NAME = "com.mysql.jdbc.PreparedStatement"; 19 | 20 | @Override 21 | public ElementMatcher getTypeMatcher() { 22 | return named(PREPARED_STATEMENT_CLASS_NAME); 23 | } 24 | 25 | @Override 26 | public Plugin[] getPlugins() { 27 | return new Plugin[]{ 28 | Plugin.advice(named("execute").or(named("executeQuery")) 29 | .or(named("executeUpdate")) 30 | .or(named("executeLargeUpdate")), 31 | PreparedStatement5xAdvice.class.getName()) 32 | }; 33 | } 34 | 35 | @Override 36 | public Witness getWitness() { 37 | return Witness.classes(PREPARED_STATEMENT_CLASS_NAME, "com.mysql.jdbc.MySQLConnection"); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /sql-capture-plugin/src/main/java/com/tangym/sql/plugin/v5/PreparedStatement5xAdvice.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.plugin.v5; 2 | 3 | import com.mysql.jdbc.MySQLConnection; 4 | import com.mysql.jdbc.PreparedStatement; 5 | import com.tangym.sql.plugin.v5.model.SqlExplainInfo; 6 | import com.tangym.sql.plugin.v5.util.DataPool; 7 | import io.manbang.gravity.plugin.Advice; 8 | import io.manbang.gravity.plugin.ExecuteContext; 9 | import lombok.SneakyThrows; 10 | import lombok.extern.slf4j.Slf4j; 11 | 12 | import java.util.Optional; 13 | import java.util.Properties; 14 | 15 | import static com.tangym.sql.plugin.v5.util.DataPool.linkedBlockingQueue; 16 | import static com.tangym.sql.plugin.v5.util.SqlFormatUtil.formatSql; 17 | 18 | /** 19 | * @author : yiming.tang 20 | */ 21 | @Slf4j 22 | public class PreparedStatement5xAdvice implements Advice { 23 | @SneakyThrows 24 | @Override 25 | public void enterMethod(ExecuteContext context) { 26 | Object target = context.getTarget(); 27 | if (target instanceof PreparedStatement) { 28 | String sqlId = DataPool.sqlIdThreadLocal.get(); 29 | PreparedStatement preparedStatement = (PreparedStatement) target; 30 | String ORIGINALSQL = formatSql(preparedStatement.getPreparedSql()); 31 | String SQL = formatSql(preparedStatement.asSql()); 32 | String start = ORIGINALSQL.split(" ")[0]; 33 | 34 | // 自动分库分表中间件带入参数处理,如逻辑不同请自我修改或删除 35 | if (start.startsWith("/*")) { 36 | start = start.split("\\/")[2]; 37 | ORIGINALSQL = ORIGINALSQL.split("\\/")[2]; 38 | SQL = SQL.split("\\/")[2]; 39 | } 40 | if (!DataPool.captureRules.contains(start.toLowerCase())) return; 41 | MySQLConnection connection = context.getFieldValue("connection"); 42 | Optional optional = Optional.ofNullable(connection).map(MySQLConnection::getProperties); 43 | if (!optional.isPresent()) { 44 | return; 45 | } 46 | Properties props = optional.get(); 47 | SqlExplainInfo sqlExplainInfo = SqlExplainInfo.builder().dbHost(String.valueOf(props.get("HOST"))).dbPort(String.valueOf(props.get("PORT"))).dbName(String.valueOf(props.get("DBNAME"))).dbUser(String.valueOf(props.get("user"))).dbPwd(String.valueOf(props.get("password"))).originalSql(ORIGINALSQL).parameterizedSql(SQL).sqlId(sqlId).serverVersion(5).serviceName(DataPool.appName).build(); 48 | if (!linkedBlockingQueue.offer(sqlExplainInfo)) log.info("sqls queue is full"); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /sql-capture-plugin/src/main/java/com/tangym/sql/plugin/v5/StartedPostProcessor.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.plugin.v5; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.JSONArray; 5 | import com.alibaba.fastjson.JSONObject; 6 | import com.google.auto.service.AutoService; 7 | import com.tangym.sql.plugin.v5.util.DataPool; 8 | import com.tangym.sql.plugin.v5.util.HttpUtil; 9 | import com.tangym.sql.plugin.v5.util.SqlPostTask; 10 | import com.tangym.sql.spring.plugin.BeanDefinitionRegistryProcessor; 11 | import io.manbang.gravity.plugin.AgentOptions; 12 | import lombok.extern.slf4j.Slf4j; 13 | import org.springframework.beans.factory.support.BeanDefinitionRegistry; 14 | 15 | /** 16 | * @author : yiming.tang 17 | */ 18 | @AutoService(BeanDefinitionRegistryProcessor.class) 19 | @Slf4j 20 | public class StartedPostProcessor implements BeanDefinitionRegistryProcessor { 21 | 22 | @Override 23 | public void process(BeanDefinitionRegistry registry) { 24 | // todo 根据生产情况调整域名配置 25 | String serverUrl = ""; 26 | if (AgentOptions.INSTANCE.isLocalDebug()) { 27 | serverUrl = "http://127.0.0.1:8080/detect"; 28 | } 29 | String detail = HttpUtil.get(String.format("%s/sql/config/rules/capture", serverUrl)); 30 | if (null != detail) { 31 | JSONObject jsonObject = JSON.parseObject(detail); 32 | Object capture = jsonObject.get("capture"); 33 | JSONArray ja = JSONArray.parseArray(capture.toString()); 34 | if (ja.size() > 0) DataPool.captureRules = ja.toJavaList(String.class); 35 | } 36 | 37 | SqlPostTask.start(); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /sql-capture-plugin/src/main/java/com/tangym/sql/plugin/v5/model/SqlExplainInfo.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.plugin.v5.model; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | 6 | import java.io.Serializable; 7 | import java.time.LocalDateTime; 8 | 9 | /** 10 | * @author yiming.tamng 11 | */ 12 | @Builder 13 | @Data 14 | public class SqlExplainInfo implements Serializable { 15 | private static final long serialVersionUID = 4094078424027038060L; 16 | 17 | private Integer id; 18 | 19 | /** 20 | * 服务名称/应用名称 21 | */ 22 | private String serviceName; 23 | 24 | /** 25 | * 原始不填参数的sql语句 26 | */ 27 | private String originalSql; 28 | 29 | /** 30 | * 最终可以执行的参数化后的sql 31 | */ 32 | private String parameterizedSql; 33 | 34 | /** 35 | * sql mybatis id 全路径 36 | */ 37 | private String sqlId; 38 | 39 | /** 40 | * host 41 | */ 42 | private String dbHost; 43 | 44 | /** 45 | * 端口号 46 | */ 47 | private String dbPort; 48 | 49 | /** 50 | * 数据库名 51 | */ 52 | private String dbName; 53 | 54 | /** 55 | * 用户名 56 | */ 57 | private String dbUser; 58 | 59 | /** 60 | * 密码 61 | */ 62 | private String dbPwd; 63 | 64 | /** 65 | * mysql server 版本 5 or 8 66 | */ 67 | private Integer serverVersion; 68 | 69 | /** 70 | * explain结果 71 | */ 72 | private String explainRes; 73 | 74 | /** 75 | * 是否是慢查sql 76 | */ 77 | private Boolean isSlow; 78 | 79 | /** 80 | * 是否已告警 81 | */ 82 | private Boolean isAlert; 83 | 84 | /** 85 | * 线上是否出慢查故障 86 | */ 87 | private Boolean isProdFault; 88 | 89 | /** 90 | * 备注信息 91 | */ 92 | private String remark; 93 | 94 | /** 95 | * 更新时间 96 | */ 97 | private LocalDateTime updateTime; 98 | 99 | /** 100 | * 创建时间 101 | */ 102 | private LocalDateTime createTime; 103 | 104 | } 105 | -------------------------------------------------------------------------------- /sql-capture-plugin/src/main/java/com/tangym/sql/plugin/v5/util/DataPool.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.plugin.v5.util; 2 | 3 | import com.tangym.sql.plugin.v5.model.SqlExplainInfo; 4 | import io.manbang.gravity.plugin.AgentOptions; 5 | 6 | import java.util.Arrays; 7 | import java.util.List; 8 | import java.util.concurrent.LinkedBlockingQueue; 9 | 10 | /** 11 | * @author : yiming.tang 12 | */ 13 | public class DataPool { 14 | 15 | public static String appName = AgentOptions.INSTANCE.getAppName(); 16 | 17 | public static List captureRules = Arrays.asList("select"); 18 | 19 | public static LinkedBlockingQueue linkedBlockingQueue = new LinkedBlockingQueue<>(); 20 | 21 | public static ThreadLocal sqlIdThreadLocal = new ThreadLocal<>(); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /sql-capture-plugin/src/main/java/com/tangym/sql/plugin/v5/util/HttpUtil.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.plugin.v5.util; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import okhttp3.*; 5 | 6 | import java.io.IOException; 7 | import java.util.concurrent.TimeUnit; 8 | 9 | /** 10 | * @author : yiming.tang 11 | */ 12 | @Slf4j 13 | public class HttpUtil { 14 | public static void post(String url, String json) { 15 | MediaType mediaType = MediaType.parse("application/json;charset=UTF-8"); 16 | OkHttpClient okhttp = new OkHttpClient(); 17 | okhttp.newBuilder().connectTimeout(20000L, TimeUnit.MILLISECONDS).readTimeout(20000, TimeUnit.MILLISECONDS).build(); 18 | RequestBody requestBody = RequestBody.create(mediaType, json); 19 | Request request = new Request.Builder().post(requestBody).url(url).build(); 20 | try { 21 | Response response = okhttp.newCall(request).execute(); 22 | if (!response.isSuccessful()) 23 | response.body().close(); 24 | } catch (IOException e) { 25 | e.printStackTrace(); 26 | } 27 | } 28 | 29 | public static String get(String url) { 30 | Request.Builder builder = new Request.Builder(); 31 | Request request = builder.get().url(url).build(); 32 | Response response; 33 | OkHttpClient HTTP_CLIENT = new OkHttpClient.Builder() 34 | .connectTimeout(20000L, TimeUnit.SECONDS) 35 | .readTimeout(20000L, TimeUnit.SECONDS) 36 | .build(); 37 | try { 38 | response = HTTP_CLIENT.newCall(request).execute(); 39 | if (response.code() == 200) { 40 | assert response.body() != null; 41 | String body = response.body().string(); 42 | response.body().close(); 43 | return body; 44 | } 45 | } catch (IOException e) { 46 | e.printStackTrace(); 47 | } 48 | return null; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /sql-capture-plugin/src/main/java/com/tangym/sql/plugin/v5/util/PostUtil.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.plugin.v5.util; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.tangym.sql.plugin.v5.model.SqlExplainInfo; 5 | import io.manbang.gravity.plugin.AgentOptions; 6 | import lombok.extern.slf4j.Slf4j; 7 | 8 | /** 9 | * @author : yiming.tang 10 | */ 11 | @Slf4j 12 | public class PostUtil { 13 | static void process(SqlExplainInfo info) { 14 | // todo 根据生产情况调整域名配置 15 | String serverUrl = ""; 16 | if (AgentOptions.INSTANCE.isLocalDebug()) { 17 | serverUrl = "http://127.0.0.1:8080/detect"; 18 | } 19 | HttpUtil.post(String.format("%s/sql/add", serverUrl), JSON.toJSONString(info)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /sql-capture-plugin/src/main/java/com/tangym/sql/plugin/v5/util/SqlFormatUtil.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.plugin.v5.util; 2 | 3 | /** 4 | * @author : yiming.tang 5 | */ 6 | public class SqlFormatUtil { 7 | public static String formatSql(String sql) { 8 | String res; 9 | if (null != sql) { 10 | res = sql.replaceAll("[\r\n]", " ").replaceAll("\\s+", " "); 11 | } else { 12 | return null; 13 | } 14 | return res; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /sql-capture-plugin/src/main/java/com/tangym/sql/plugin/v5/util/SqlPostTask.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.plugin.v5.util; 2 | 3 | import com.tangym.sql.plugin.v5.model.SqlExplainInfo; 4 | import lombok.extern.slf4j.Slf4j; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.concurrent.Executors; 9 | import java.util.concurrent.ScheduledExecutorService; 10 | import java.util.concurrent.TimeUnit; 11 | 12 | /** 13 | * @author : yiming.tang 14 | */ 15 | @Slf4j 16 | public class SqlPostTask { 17 | private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); 18 | 19 | public static void start() { 20 | SqlPostTask task = new SqlPostTask(); 21 | task.scheduledExecutorService.scheduleAtFixedRate(() -> { 22 | try { 23 | if (!DataPool.linkedBlockingQueue.isEmpty()) { 24 | List infoList = new ArrayList<>(); 25 | DataPool.linkedBlockingQueue.drainTo(infoList); 26 | if (infoList.size() > 0) { 27 | infoList.forEach(PostUtil::process); 28 | } 29 | } 30 | } catch (Exception e) { 31 | log.info("sql post failed"); 32 | } 33 | }, 60, 1, TimeUnit.SECONDS); 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /sql-detect-h5/.env: -------------------------------------------------------------------------------- 1 | NODE_ENV=production 2 | VUE_APP_API_BASE_URL=/detect 3 | -------------------------------------------------------------------------------- /sql-detect-h5/.env.development: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | VUE_APP_API_BASE_URL=/detect 3 | -------------------------------------------------------------------------------- /sql-detect-h5/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:latest as build-stage 2 | WORKDIR /app 3 | COPY package*.json ./ 4 | RUN npm install 5 | COPY ./ . 6 | RUN npm run build 7 | 8 | FROM nginx as production-stage 9 | RUN mkdir /app 10 | COPY --from=build-stage /app/dist /app 11 | COPY nginx.conf /etc/nginx/nginx.conf -------------------------------------------------------------------------------- /sql-detect-h5/README.md: -------------------------------------------------------------------------------- 1 | # test-platform-boss 2 | 3 | ## 技术栈 4 | 5 | vue.js/vuex/ant-design-vue/axios/vue-test-utils/jest/prettier 6 | 7 | ## 特点 8 | 9 | 本骨架工程采用了顶部菜单加侧边菜单分别渲染的方式,为了可以直接通过复制网址打开对应页面且能正确高亮顶部与侧边菜单,本工程并未使用浏览器本地存储数据的方式,而是对 url 进行了解析,所以对于地址命名与项目文件命名有着一定的规范。请阅读骨架工程代码学习约定规约。 10 | 11 | ## 使用 12 | 13 | - 请根据自己的需要修改登录认证与封装的 axios 请求拦截方法。 14 | 15 | ## 本地开发 16 | 17 | devServer 下修改 host: 'web.amh-group.com',本地 hosts 文件新增:机器 IP web.amh-group.com 18 | 19 | ## 部署 20 | 21 | ### 常规部署方式 22 | 23 | 拉取代码 24 | 25 | ```bash 26 | [root@localhost conf.d]# npm install 27 | [root@localhost conf.d]# npm run build 28 | ``` 29 | 30 | 配置 nginx server 项 31 | 32 | ```bash 33 | [root@localhost conf.d]# vi default.conf 34 | ``` 35 | 36 | > listen 8080; 37 | > 38 | > location / { 39 | > 40 | > root /opt/test-platform-h5/dist/; 41 | > 42 | > try_files $uri $uri/ /index.html = 404; 43 | > 44 | > } 45 | 46 | ```bash 47 | [root@localhost conf.d]# nginx -c /etc/nginx/nginx.conf 48 | [root@localhost conf.d]# nginx -s reload 49 | ``` 50 | 51 | 关闭防火墙,以防止外面无法访问 52 | 53 | ```bash 54 | [root@localhost conf.d]# systemctl stop firewalld.service 55 | ``` 56 | 57 | ### Docker 化部署方式(暂不用这种方式部署) 58 | 59 | 在项目中添加 docker 与 nginx 配置 60 | 给服务器生成 sshkey 添加到 gitlab 61 | 在服务器上拉取前端代码 62 | 进行 image 构建(tag 随版本变更),和容器运行 63 | 64 | ```bash 65 | [root@localhost opt]# docker build test-platform-h5/ -t test-platform-h5:1.0.0 66 | [root@localhost opt]# docker run -d -p 8080:80 test-platform-h5:1.0.0 67 | ``` 68 | 69 | 参考:https://cli.vuejs.org/guide/deployment.html#docker-nginx 70 | -------------------------------------------------------------------------------- /sql-detect-h5/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['@vue/app'], 3 | } 4 | -------------------------------------------------------------------------------- /sql-detect-h5/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: '@vue/cli-plugin-unit-jest', 3 | collectCoverage: true, 4 | collectCoverageFrom: ['**/*.{js,vue}', '!**/node_modules/**', '!**/dist/**'], //排除掉dist,否则生成报告报错 5 | coverageReporters: ['html', 'text-summary'], 6 | } 7 | -------------------------------------------------------------------------------- /sql-detect-h5/nginx.conf: -------------------------------------------------------------------------------- 1 | user nginx; 2 | worker_processes 1; 3 | error_log /var/log/nginx/error.log warn; 4 | pid /var/run/nginx.pid; 5 | events { 6 | worker_connections 1024; 7 | } 8 | http { 9 | include /etc/nginx/mime.types; 10 | default_type application/octet-stream; 11 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 12 | '$status $body_bytes_sent "$http_referer" ' 13 | '"$http_user_agent" "$http_x_forwarded_for"'; 14 | access_log /var/log/nginx/access.log main; 15 | sendfile on; 16 | keepalive_timeout 65; 17 | server { 18 | listen 80; 19 | server_name localhost; 20 | location / { 21 | root /app; 22 | index index.html; 23 | try_files $uri $uri/ /index.html; 24 | } 25 | error_page 500 502 503 504 /50x.html; 26 | location = /50x.html { 27 | root /usr/share/nginx/html; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /sql-detect-h5/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-platform-boss", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "qa": "vue-cli-service serve --open --mode qa", 8 | "build": "vue-cli-service build", 9 | "test:unit": "vue-cli-service test:unit", 10 | "lint": "vue-cli-service lint", 11 | "format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,vue,json,css,scss,less,md}\" " 12 | }, 13 | "dependencies": { 14 | "ant-design-vue": "^1.6.3", 15 | "axios": "^0.19.2", 16 | "core-js": "^2.6.11", 17 | "echarts": "^4.9.0", 18 | "ema-proxy": "1.0.1", 19 | "eslint-plugin-prettier": "^3.2.0", 20 | "file-saver": "^2.0.2", 21 | "html-loader": "^0.5.5", 22 | "js-cookie": "^2.2.1", 23 | "lodash": "^4.17.20", 24 | "markdown-loader": "^5.1.0", 25 | "moment": "^2.29.1", 26 | "prettier": "^2.2.1", 27 | "qrcodejs2": "0.0.2", 28 | "showdown": "^1.9.1", 29 | "sockjs-client": "^1.5.1", 30 | "stompjs": "^2.3.3", 31 | "v-jsoneditor": "^1.4.4", 32 | "vue": "^2.6.11", 33 | "vue-json-excel": "^0.3.0", 34 | "vue-router": "^3.3.4", 35 | "vue-template-compiler": "^2.6.11", 36 | "vuex": "^3.5.1", 37 | "xlsx": "^0.14.5" 38 | }, 39 | "devDependencies": { 40 | "@vue/cli-plugin-babel": "^3.12.1", 41 | "@vue/cli-plugin-eslint": "^4.4.6", 42 | "@vue/cli-plugin-unit-jest": "^4.5.4", 43 | "@vue/cli-service": "^4.4.6", 44 | "@vue/eslint-config-prettier": "^4.0.1", 45 | "@vue/test-utils": "^1.0.3", 46 | "babel-eslint": "^10.1.0", 47 | "eslint": "^5.16.0", 48 | "eslint-plugin-vue": "^5.2.3", 49 | "less": "^3.11.3", 50 | "less-loader": "^4.1.0", 51 | "vue-loader": "^15.9.3" 52 | }, 53 | "eslintConfig": { 54 | "root": true, 55 | "env": { 56 | "node": true 57 | }, 58 | "extends": [ 59 | "plugin:vue/essential", 60 | "@vue/prettier" 61 | ], 62 | "rules": { 63 | "no-unused-vars": 0, 64 | "no-console": 0, 65 | "no-debugger": "off", 66 | "no-extra-boolean-cast": 0 67 | }, 68 | "parserOptions": { 69 | "parser": "babel-eslint" 70 | }, 71 | "overrides": [ 72 | { 73 | "files": [ 74 | "**/__tests__/*.{j,t}s?(x)", 75 | "**/tests/unit/**/*.spec.{j,t}s?(x)" 76 | ], 77 | "env": { 78 | "jest": true 79 | } 80 | } 81 | ] 82 | }, 83 | "prettier": { 84 | "jsxBracketSameLine": false, 85 | "singleQuote": true, 86 | "semi": false, 87 | "tabWidth": 4, 88 | "printWidth": 200, 89 | "bracketSpacing": true, 90 | "vueIndentScriptAndStyle": false, 91 | "arrowParens": "avoid", 92 | "endOfLine": "lf" 93 | }, 94 | "postcss": { 95 | "plugins": { 96 | "autoprefixer": {} 97 | } 98 | }, 99 | "browserslist": [ 100 | "> 1%", 101 | "last 2 versions", 102 | "not ie <= 8" 103 | ] 104 | } 105 | -------------------------------------------------------------------------------- /sql-detect-h5/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangyiming/sql-detect/657febab417d30ca0863f40a0d50279673506c6b/sql-detect-h5/public/favicon.png -------------------------------------------------------------------------------- /sql-detect-h5/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | Firefly | 造数-Mock-测试平台 15 | 16 | 17 | 18 | We're sorry but artemis-fe doesn't work properly without JavaScript enabled. Please enable it to continue. 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /sql-detect-h5/src/assets/images/gitlab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangyiming/sql-detect/657febab417d30ca0863f40a0d50279673506c6b/sql-detect-h5/src/assets/images/gitlab.png -------------------------------------------------------------------------------- /sql-detect-h5/src/assets/styles/antd.less: -------------------------------------------------------------------------------- 1 | .ant-layout-header { 2 | height: 50px !important; 3 | } 4 | 5 | .ant-layout-footer { 6 | color: #586069 !important; 7 | font-size: 14px; 8 | text-align: center; 9 | padding: 14px 0px; 10 | } 11 | 12 | #components-layout-demo-top-side .top-menu[data-v-7ba5bd90] { 13 | line-height: 50px; 14 | position: absolute; 15 | left: 250px; 16 | } 17 | 18 | .ant-advanced-search-form { 19 | padding: 12px !important; 20 | background: @searchform-bgcolor; 21 | border: 1px solid #d9d9d9; 22 | border-radius: 6px; 23 | .ant-form-item { 24 | display: flex; 25 | } 26 | .ant-form-item-control-wrapper { 27 | flex: 1; 28 | } 29 | } 30 | 31 | #components-form-demo-advanced-search { 32 | .ant-form { 33 | max-width: none; 34 | } 35 | .search-result-list { 36 | margin-top: 16px; 37 | border: 1px dashed #e9e9e9; 38 | border-radius: 6px; 39 | background-color: #fafafa; 40 | min-height: 200px; 41 | text-align: center; 42 | padding-top: 80px; 43 | } 44 | } 45 | 46 | /*有固定行的ant-design-vue 表格滑动样式*/ 47 | .ant-table-fixed .ant-table-row-hover { 48 | background: #eef1f6 !important; 49 | } 50 | .ant-table-fixed .ant-table-row-hover > td { 51 | background: #eef1f6 !important; 52 | } 53 | /*没有固定行的表格个样式*/ 54 | .ant-table-tbody > tr:hover:not(.ant-table-expanded-row) > td { 55 | background-color: rgba(241, 248, 242, 0.945) !important; 56 | } 57 | .ant-table-body .ant-table-row-hover { 58 | background: #f1f6fd !important; 59 | } 60 | .ant-table-body .ant-table-row-hover > td { 61 | background: #f1f6fd !important; 62 | } 63 | 64 | .1 { 65 | background: #cdcbcb !important; 66 | } 67 | -------------------------------------------------------------------------------- /sql-detect-h5/src/assets/styles/app.less: -------------------------------------------------------------------------------- 1 | body { 2 | margin-right: auto; 3 | margin-left: auto; 4 | // 这个可以用在居中内容的博客中 5 | // max-width 680px 6 | word-wrap: break-word; 7 | word-break: break-all; 8 | font-family: 'Avenir', Helvetica, Arial, sans-serif; 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | } 12 | 13 | .sider { 14 | overflow: auto; 15 | min-height: calc(100vh - 50px); //min-height 可以解决height导致侧边菜单留白问题 16 | left: 0px; 17 | } 18 | 19 | .seperate-line { 20 | border-top: 1px solid #e1e4e8 !important; 21 | } 22 | 23 | .view-box { 24 | height: 100%; 25 | } 26 | 27 | .layout-style { 28 | margin: 17px 25px; 29 | } 30 | 31 | .layout-style-with-height { 32 | margin: 17px 25px; 33 | min-height: calc(100vh - 135px); 34 | } 35 | 36 | .content-style { 37 | background: #fff; 38 | padding: 15px; 39 | margin-top: 10px; 40 | height: 100%; 41 | border-radius: 6px; 42 | } 43 | 44 | .table { 45 | margin-top: 15px; 46 | } 47 | 48 | .inline-second-item { 49 | margin-left: 15px; 50 | } 51 | -------------------------------------------------------------------------------- /sql-detect-h5/src/assets/styles/index.less: -------------------------------------------------------------------------------- 1 | @import 'app'; 2 | @import 'antd'; 3 | @import 'theme'; 4 | -------------------------------------------------------------------------------- /sql-detect-h5/src/assets/styles/theme.less: -------------------------------------------------------------------------------- 1 | //侧边菜单 2 | .ant-menu-dark { 3 | background-color: #6caf96; 4 | } 5 | 6 | .ant-menu-dark .ant-menu-inline.ant-menu-sub { 7 | background: #3e9577; 8 | } 9 | //头部菜单 10 | // .ant-menu-light{ 11 | // background-color: #FFF; 12 | // } 13 | 14 | //顶部 15 | .header { 16 | background: #ffffff; 17 | width: 100%; 18 | font-family: Times New Roman, Microsoft YaHei, Arial, -apple-system, system-ui, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif, BlinkMacSystemFont, Helvetica Neue, PingFang SC, 19 | Hiragino Sans GB; 20 | } 21 | 22 | //trigger 23 | .ant-layout-sider-trigger { 24 | background: #85c7ae; 25 | } 26 | -------------------------------------------------------------------------------- /sql-detect-h5/src/assets/styles/theme/fonts/element-icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangyiming/sql-detect/657febab417d30ca0863f40a0d50279673506c6b/sql-detect-h5/src/assets/styles/theme/fonts/element-icons.ttf -------------------------------------------------------------------------------- /sql-detect-h5/src/assets/styles/theme/fonts/element-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangyiming/sql-detect/657febab417d30ca0863f40a0d50279673506c6b/sql-detect-h5/src/assets/styles/theme/fonts/element-icons.woff -------------------------------------------------------------------------------- /sql-detect-h5/src/components/Footer.vue: -------------------------------------------------------------------------------- 1 | 2 | Powered By artemis-fe 3 | 4 | 9 | -------------------------------------------------------------------------------- /sql-detect-h5/src/components/LeftSider.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{ item.title }} 7 | 8 | 9 | 10 | 11 | 12 | 13 | 52 | -------------------------------------------------------------------------------- /sql-detect-h5/src/components/SubMenu.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{ props.menuInfo.title }} 6 | 7 | 8 | 9 | 10 | {{ item.title }} 11 | 12 | 13 | 14 | 15 | 16 | 21 | -------------------------------------------------------------------------------- /sql-detect-h5/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Antd from 'ant-design-vue' 3 | import App from './App.vue' 4 | import 'ant-design-vue/dist/antd.less' 5 | import './assets/styles/index.less' 6 | import router from './router' 7 | import store from './stores' 8 | import LeftSider from './components/LeftSider' 9 | import echarts from 'echarts' 10 | import VJsoneditor from 'v-jsoneditor/src/index' 11 | import '@/assets/styles/theme/index.css' 12 | import EmaProxy from 'ema-proxy' 13 | import JsonExcel from 'vue-json-excel' 14 | 15 | window.EMA = new EmaProxy() 16 | 17 | Vue.prototype.$echarts = echarts 18 | 19 | Vue.config.productionTip = false 20 | 21 | //全局注册插件 22 | Vue.use(Antd) 23 | Vue.use(VJsoneditor) 24 | 25 | //全局注册组件 26 | Vue.component('left-sider', LeftSider) 27 | Vue.component('downloadExcel', JsonExcel) 28 | 29 | //全局设置 30 | Vue.prototype.$message.config({ 31 | top: `80px`, 32 | duration: 2, 33 | maxCount: 3, 34 | }) 35 | 36 | new Vue({ 37 | router, 38 | store, 39 | render: h => h(App), 40 | }).$mount('#app') 41 | -------------------------------------------------------------------------------- /sql-detect-h5/src/requests/quartz.js: -------------------------------------------------------------------------------- 1 | import http from '../utils/http' 2 | 3 | export const listTasks = p => http.get('/quartz/list', p) 4 | export const startTask = p => http.post('/quartz/create', p) 5 | export const pauseTask = p => http.post('/quartz/pause', p) 6 | export const runOnceTask = p => http.post('/quartz/runonce', p) 7 | export const resumeTask = p => http.post('/quartz/resume', p) 8 | export const updateTask = p => http.post('/quartz/update', p) 9 | export const deleteTask = p => http.post('/quartz/delete', p) 10 | -------------------------------------------------------------------------------- /sql-detect-h5/src/requests/sqlexplain.js: -------------------------------------------------------------------------------- 1 | import http from '../utils/http' 2 | 3 | export const query = p => http.post('/sql/tables/query', p) 4 | 5 | export const queryConfig = p => http.post('/sql/config/service/query', p) 6 | export const addConfig = p => http.post('/sql/config/service/add', p) 7 | export const updateConfig = p => http.post('/sql/config/service/update', p) 8 | export const deleteConfig = p => http.get('/sql/config/service/delete', p) 9 | 10 | export const getAllRules = p => http.get('/sql/config/rules/list', p) 11 | export const updateRule = p => http.post('/sql/config/rules/update', p) 12 | export const createRule = p => http.post('/sql/config/rules/create', p) 13 | export const deleteRule = p => http.get('/sql/config/rules/delete', p) 14 | 15 | export const getAllScriptRules = p => http.get('/sql/config/rules/script/list', p) 16 | export const updateScriptRule = p => http.post('/sql/config/rules/script/update', p) 17 | export const createScriptRule = p => http.post('/sql/config/rules/script/create', p) 18 | export const deleteScriptRule = p => http.get('/sql/config/rules/script/delete', p) 19 | 20 | export const overall = p => http.get('sql/statistics/overall', p) 21 | export const serviceDetailOverall = p => http.get('sql/statistics/detail/overall', p) 22 | export const serviceDetailList = p => http.post('sql/statistics/detail/query', p) 23 | 24 | export const addProcessLog = p => http.post('sql/process/log/add', p) 25 | export const queryProcessLog = p => http.get('sql/process/log/query', p) 26 | 27 | export const star = p => http.post('/sql/stars/add', p) 28 | export const unstar = p => http.post('/sql/stars/delete', p) 29 | -------------------------------------------------------------------------------- /sql-detect-h5/src/router.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import Home from './views/home.vue' 4 | 5 | Vue.use(Router) 6 | // 避免重复同导航跳转报错 7 | const originalPush = Router.prototype.push 8 | Router.prototype.push = function push(location) { 9 | return originalPush.call(this, location).catch(err => err) 10 | } 11 | 12 | export default new Router({ 13 | mode: 'hash', 14 | routes: [ 15 | { 16 | path: '/', 17 | name: 'home', 18 | component: Home, 19 | }, 20 | { 21 | path: '/404', 22 | name: '404', 23 | // route level code-splitting 24 | // this generates a separate chunk (about.[hash].js) for this route 25 | // which is lazy-loaded when the route is visited. 26 | component: () => import(/* webpackChunkName: "customUpload" */ './views/404'), 27 | }, 28 | { 29 | path: '/sqlananysis', 30 | name: 'sqlananysis', 31 | component: () => import('./views/sqlananysis'), 32 | children: [ 33 | { 34 | path: 'dashboard', 35 | // eslint-disable-next-line prettier/prettier 36 | component: require('./views/sqlananysis/dashboard').default, 37 | }, 38 | { 39 | path: 'services', 40 | // eslint-disable-next-line prettier/prettier 41 | component: require('./views/sqlananysis/services').default, 42 | }, 43 | { 44 | path: 'tables', 45 | // eslint-disable-next-line prettier/prettier 46 | component: require('./views/sqlananysis/tables').default, 47 | }, 48 | { 49 | path: 'sqls', 50 | // eslint-disable-next-line prettier/prettier 51 | component: require('./views/sqlananysis/sqls').default, 52 | }, 53 | { 54 | path: 'rules', 55 | // eslint-disable-next-line prettier/prettier 56 | component: require('./views/sqlananysis/rules').default, 57 | }, 58 | ], 59 | }, 60 | { 61 | path: '/tool', 62 | name: 'tool', 63 | component: () => import('./views/tool'), 64 | children: [ 65 | { 66 | path: 'tasks', 67 | // eslint-disable-next-line prettier/prettier 68 | component: require('./views/tool/tasks').default, 69 | } 70 | ], 71 | }, 72 | { 73 | path: '/*', 74 | name: '404', 75 | component: () => import('./views/404'), 76 | }, 77 | ], 78 | }) 79 | -------------------------------------------------------------------------------- /sql-detect-h5/src/stores/actions.js: -------------------------------------------------------------------------------- 1 | export const setMenuList = ({ commit }, payload) => { 2 | commit('setMenuList', payload) 3 | } 4 | export const setActiveHeader = ({ commit }, payload) => { 5 | commit('setActiveHeader', payload) 6 | } 7 | export const setActiveSider = ({ commit }, payload) => { 8 | commit('setActiveSider', payload) 9 | } 10 | export const setOpenKey = ({ commit }, payload) => { 11 | commit('setOpenKey', payload) 12 | } 13 | export const setSiderKey = ({ commit }, payload) => { 14 | commit('setSiderKey', payload) 15 | } 16 | export const setLoginUser = ({ commit }, payload) => { 17 | commit('setLoginUser', payload) 18 | } 19 | export const setClientIp = ({ commit }, payload) => { 20 | commit('setClientIp', payload) 21 | } 22 | export const setAppList = ({ commit }, payload) => { 23 | commit('setAppList', payload) 24 | } 25 | -------------------------------------------------------------------------------- /sql-detect-h5/src/stores/getters.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangyiming/sql-detect/657febab417d30ca0863f40a0d50279673506c6b/sql-detect-h5/src/stores/getters.js -------------------------------------------------------------------------------- /sql-detect-h5/src/stores/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import perftest from './modules/perftest' 4 | import * as actions from './actions' 5 | import * as getters from './getters' 6 | import mutations from './mutations' 7 | import createLogger from 'vuex/dist/logger' 8 | 9 | const packageInfo = require('../../package.json') 10 | 11 | Vue.use(Vuex) 12 | 13 | const state = { 14 | menuList: [], 15 | activeHeader: '', 16 | activeSider: '', 17 | openKey: '', 18 | //用来进行重新渲染sider 19 | siderKey: 0, 20 | loginUser: {}, 21 | clientIp: '', 22 | appList: [], 23 | app: { 24 | packageInfo, 25 | }, 26 | userInfo: { 27 | id: 0, 28 | name: '', 29 | gender: 1, 30 | avatarUrl: '', 31 | jobNumber: '', 32 | }, 33 | } 34 | 35 | const debug = process.env.NODE_ENV !== 'production' 36 | 37 | export default new Vuex.Store({ 38 | state, 39 | getters, 40 | mutations, 41 | actions, 42 | modules: { 43 | perftest, 44 | }, 45 | //在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误 46 | strict: debug, 47 | //如果正在使用 vue-devtools,你可能不需要此插件 48 | plugins: debug ? [createLogger()] : [], 49 | }) 50 | -------------------------------------------------------------------------------- /sql-detect-h5/src/stores/modules/perftest.js: -------------------------------------------------------------------------------- 1 | const state = {} 2 | const getters = {} 3 | const actions = {} 4 | const mutations = {} 5 | 6 | export default { 7 | namespaced: true, 8 | state, 9 | getters, 10 | actions, 11 | mutations, 12 | } 13 | -------------------------------------------------------------------------------- /sql-detect-h5/src/stores/mutations.js: -------------------------------------------------------------------------------- 1 | export default { 2 | setMenuList(state, key) { 3 | state.menuList = key 4 | }, 5 | setActiveHeader(state, key) { 6 | state.activeHeader = key 7 | }, 8 | setActiveSider(state, key) { 9 | state.activeSider = key 10 | }, 11 | setOpenKey(state, key) { 12 | state.openKey = key 13 | }, 14 | setSiderKey(state, key) { 15 | state.siderKey = key 16 | }, 17 | setLoginUser(state, key) { 18 | state.loginUser = key 19 | }, 20 | setClientIp(state, key) { 21 | state.clientIp = key 22 | }, 23 | setAppList(state, key) { 24 | state.appList = key 25 | }, 26 | } 27 | -------------------------------------------------------------------------------- /sql-detect-h5/src/utils/helper.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 防抖函数 3 | * @param {*} func 4 | * @param {*} delay 5 | * @param {*} immediate 是否立即执行 6 | */ 7 | export function debounce(func, delay, immediate) { 8 | // 定时任务的id 9 | let timeout 10 | return function () { 11 | let that = this 12 | let args = arguments 13 | if (timeout) clearTimeout(timeout) 14 | if (immediate) { 15 | var callNow = !timeout 16 | timeout = setTimeout(() => { 17 | timeout = null 18 | }, delay) 19 | if (callNow) func.apply(that, args) 20 | } else { 21 | timeout = setTimeout(function () { 22 | func.apply(that, args) 23 | }, delay) 24 | } 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /sql-detect-h5/src/utils/http.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | function checkCode(response = {}) { 4 | if (response.status === 200 || response.data.code === 200) { 5 | return response.data 6 | } else { 7 | if (response.message || response.data.message) { 8 | console.log('warning') 9 | } 10 | return {} 11 | } 12 | } 13 | 14 | axios.defaults.withCredentials = true 15 | 16 | // 此处全局设置api 用于请求test-platform-app的服务器端接口转发,两边不加斜杠是坑,大坑,最后生成的地址莫名其妙 17 | // axios.defaults.baseURL = '/api/' 18 | axios.defaults.baseURL = process.env.VUE_APP_API_BASE_URL 19 | 20 | axios.interceptors.request.use( 21 | config => { 22 | return config 23 | }, 24 | error => Promise.reject(error) 25 | ) 26 | axios.interceptors.response.use( 27 | response => checkCode(response), 28 | err => { 29 | if (err.response.status === 401) { 30 | //todo 31 | return 32 | } 33 | return Promise.reject(err) 34 | } 35 | ) 36 | 37 | export default { 38 | post(url, data) { 39 | return axios({ 40 | method: 'post', 41 | url, 42 | data: data, 43 | }) 44 | }, 45 | postFormData(url, data) { 46 | let ret = '' 47 | for (let it in data) { 48 | ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&' 49 | } 50 | return axios({ 51 | method: 'post', 52 | headers: { 53 | 'Content-type': 'application/x-www-form-urlencoded', 54 | }, 55 | url, 56 | data: ret, 57 | }) 58 | }, 59 | postMultipartFormData(url, data) { 60 | return axios({ 61 | method: 'post', 62 | headers: { 63 | 'Content-type': 'multipart/form-data', 64 | }, 65 | url, 66 | data: data, 67 | }) 68 | }, 69 | postMultipartFormDataAndDownloadNew(url, data) { 70 | return axios({ 71 | method: 'post', 72 | headers: { 73 | 'Content-type': 'multipart/form-data', 74 | }, 75 | url, 76 | data: data, 77 | responseType: 'blob', 78 | }) 79 | }, 80 | put(url, data) { 81 | return axios({ 82 | method: 'put', 83 | url, 84 | data: data, 85 | }) 86 | }, 87 | get(url, params) { 88 | return axios({ 89 | method: 'get', 90 | url, 91 | params, 92 | }) 93 | }, 94 | delete(url, params) { 95 | return axios({ 96 | method: 'delete', 97 | url, 98 | data: params, 99 | }) 100 | }, 101 | } 102 | -------------------------------------------------------------------------------- /sql-detect-h5/src/views/404/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 抱歉,您访问的页面不存在 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 97 | -------------------------------------------------------------------------------- /sql-detect-h5/src/views/home.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 如果你喜欢此项目的话,请到Github项目页给个Star~ 7 | 8 | 9 | 10 | 11 | 12 | 13 | 24 | -------------------------------------------------------------------------------- /sql-detect-h5/src/views/sqlananysis/dashboard.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 慢SQL分析 12 | 13 | 14 | 15 | Dashboard 16 | 17 | 18 | 19 | 20 | 21 | 22 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /sql-detect-h5/src/views/sqlananysis/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 54 | -------------------------------------------------------------------------------- /sql-detect-h5/src/views/sqlananysis/tables.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 慢SQL分析 12 | 13 | 14 | 15 | 表结构信息 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 查询 36 | 清空 37 | 38 | 39 | 40 | 41 | 42 | 43 | {{ formatTime(record.createTime) }} 44 | 45 | 46 | {{ formatTime(record.updateTime) }} 47 | 48 | 49 | 50 | 51 | 52 | 53 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /sql-detect-h5/src/views/tool/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 50 | -------------------------------------------------------------------------------- /sql-detect-h5/vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | configureWebpack: config => { 3 | config.module.rules.push({ 4 | test: /\.md$/, 5 | use: [ 6 | { 7 | loader: 'html-loader', 8 | }, 9 | { 10 | loader: 'markdown-loader', 11 | options: {}, 12 | }, 13 | ], 14 | }) 15 | }, 16 | css: { 17 | loaderOptions: { 18 | less: { 19 | modifyVars: { 20 | 'searchform-bgcolor': '#fbfbfb', 21 | 'primary-color': '#16817a', 22 | }, 23 | javascriptEnabled: true, 24 | }, 25 | }, 26 | }, 27 | devServer: { 28 | hot: true, 29 | // host: 'web.xxx.com', 30 | port: 8088, 31 | open: true, 32 | https: true, 33 | proxy: { 34 | '/detect': { 35 | //本地 36 | target: 'http://localhost:8080/detect/', 37 | //target: 'http://xxx.xxx.com/detect/', 38 | ws: false, 39 | changOrigin: true, 40 | secure: true, 41 | pathRewrite: { 42 | '^/detect/': '', 43 | }, 44 | }, 45 | }, 46 | }, 47 | } 48 | -------------------------------------------------------------------------------- /sql-detect-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.7.0 9 | 10 | 11 | 4.0.0 12 | 13 | sql-detect-server 14 | 15 | 16 | 8 17 | 8 18 | 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-web 24 | 25 | 26 | org.mybatis.spring.boot 27 | mybatis-spring-boot-starter 28 | 29 | 30 | mysql 31 | mysql-connector-java 32 | 33 | 34 | 2.2.2 35 | 36 | 37 | mysql 38 | mysql-connector-java 39 | 5.1.49 40 | runtime 41 | 42 | 43 | org.springframework.boot 44 | spring-boot-starter-validation 45 | 46 | 47 | com.github.pagehelper 48 | pagehelper-spring-boot-starter 49 | 1.4.2 50 | 51 | 52 | org.springframework.boot 53 | spring-boot-starter-quartz 54 | 55 | 56 | com.github.answerail 57 | dinger-spring-boot-starter 58 | 1.2.0 59 | 60 | 61 | org.springframework.boot 62 | spring-boot-devtools 63 | runtime 64 | true 65 | 66 | 67 | org.projectlombok 68 | lombok 69 | true 70 | 71 | 72 | org.springframework.boot 73 | spring-boot-starter-test 74 | test 75 | 76 | 77 | com.alibaba 78 | fastjson 79 | 1.2.75 80 | provided 81 | 82 | 83 | 84 | commons-codec 85 | commons-codec 86 | 1.2 87 | 88 | 89 | commons-lang 90 | commons-lang 91 | 2.6 92 | 93 | 94 | com.alibaba 95 | druid 96 | 1.1.23 97 | 98 | 99 | org.codehaus.groovy 100 | groovy 101 | 2.5.5 102 | 103 | 104 | org.codehaus.groovy 105 | groovy-jsr223 106 | 2.5.5 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/Application.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class Application { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(Application.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/config/AfterServiceStarted.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.config; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.JSONArray; 5 | import com.alibaba.fastjson.JSONObject; 6 | import com.tangym.sql.server.constant.SqlExplainRules; 7 | import com.tangym.sql.server.entity.Quartz; 8 | import com.tangym.sql.server.entity.SqlExplainConfigRules; 9 | import com.tangym.sql.server.mapper.QuartzMapper; 10 | import com.tangym.sql.server.mapper.SqlExplainConfigRulesMapper; 11 | import com.tangym.sql.server.mapper.SqlExplainConfigRulesScriptMapper; 12 | import com.tangym.sql.server.util.QuartzUtils; 13 | import lombok.extern.slf4j.Slf4j; 14 | import org.quartz.Scheduler; 15 | import org.quartz.SchedulerException; 16 | import org.springframework.boot.ApplicationArguments; 17 | import org.springframework.boot.ApplicationRunner; 18 | import org.springframework.stereotype.Component; 19 | 20 | import javax.annotation.Resource; 21 | import java.util.HashSet; 22 | import java.util.List; 23 | 24 | @Slf4j 25 | @Component 26 | public class AfterServiceStarted implements ApplicationRunner { 27 | 28 | @Resource 29 | private QuartzMapper quartzMapper; 30 | @Resource 31 | private Scheduler scheduler; 32 | @Resource 33 | private SqlExplainConfigRulesMapper sqlExplainConfigRulesMapper; 34 | @Resource 35 | private SqlExplainConfigRulesScriptMapper sqlExplainConfigRulesScriptMapper; 36 | 37 | public static void SyncSqlExplainRules(SqlExplainConfigRules sqlExplainConfigRules, List allScripts) { 38 | if (sqlExplainConfigRules != null) { 39 | String detail = sqlExplainConfigRules.getRuleDetail(); 40 | if (null != detail) { 41 | JSONObject jsonObject = JSON.parseObject(detail); 42 | Object keySet = jsonObject.get("keySet"); 43 | if (null != keySet) { 44 | JSONArray ksa = JSONArray.parseArray(keySet.toString()); 45 | if (ksa.size() > 0) SqlExplainRules.KEYSET = new HashSet<>(ksa.toJavaList(String.class)); 46 | } 47 | Object typeSet = jsonObject.get("typeSet"); 48 | if (null != typeSet) { 49 | JSONArray tsa = JSONArray.parseArray(typeSet.toString()); 50 | if (tsa.size() > 0) SqlExplainRules.TYPESET = new HashSet<>(tsa.toJavaList(String.class)); 51 | } 52 | } 53 | } 54 | 55 | if (null != allScripts && allScripts.size() > 0) { 56 | SqlExplainRules.SCRIPTSET = new HashSet<>(allScripts); 57 | } 58 | } 59 | 60 | @Override 61 | public void run(ApplicationArguments args) { 62 | SqlExplainConfigRules sqlExplainConfigRules = sqlExplainConfigRulesMapper.selectByRuleKey("criteria"); 63 | List allScripts = sqlExplainConfigRulesScriptMapper.selectAllScripts(); 64 | if (null != sqlExplainConfigRules) SyncSqlExplainRules(sqlExplainConfigRules, allScripts); 65 | 66 | // todo 本地调试是否开启定时任务,以部署环境系统名为判断 67 | if ("Linux".equals(System.getProperty("os.name"))) { 68 | List quartzs = quartzMapper.selectAll(); 69 | if (quartzs.size() > 0) { 70 | quartzs.forEach(quartz -> { 71 | if (quartz.getRunAfterStart()) { 72 | try { 73 | QuartzUtils.createScheduleJob(scheduler, quartz); 74 | } catch (SchedulerException | ClassNotFoundException e) { 75 | e.printStackTrace(); 76 | } 77 | } 78 | }); 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/config/DataSourceConfig.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.config; 2 | 3 | import org.apache.ibatis.session.SqlSessionFactory; 4 | import org.mybatis.spring.SqlSessionFactoryBean; 5 | import org.mybatis.spring.annotation.MapperScan; 6 | import org.springframework.beans.factory.annotation.Qualifier; 7 | import org.springframework.boot.context.properties.ConfigurationProperties; 8 | import org.springframework.boot.jdbc.DataSourceBuilder; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.context.annotation.Primary; 12 | import org.springframework.core.io.support.PathMatchingResourcePatternResolver; 13 | 14 | import javax.sql.DataSource; 15 | 16 | @Configuration 17 | @MapperScan( 18 | basePackages = "com.tangym.sql.server.mapper", 19 | sqlSessionFactoryRef = "sqlSessionFactory" 20 | ) 21 | public class DataSourceConfig { 22 | @Bean 23 | @Primary 24 | @ConfigurationProperties(prefix = "spring.datasource.hikari") 25 | public DataSource dataSource() { 26 | return DataSourceBuilder.create().build(); 27 | } 28 | 29 | @Bean 30 | @Primary 31 | public SqlSessionFactory sqlSessionFactory( 32 | @Qualifier("dataSource") DataSource dataSource 33 | ) throws Exception { 34 | SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); 35 | sqlSessionFactoryBean.setDataSource(dataSource); 36 | sqlSessionFactoryBean.setMapperLocations( 37 | new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*.xml") 38 | ); 39 | org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration(); 40 | configuration.setMapUnderscoreToCamelCase(true); 41 | sqlSessionFactoryBean.setConfiguration(configuration); 42 | return sqlSessionFactoryBean.getObject(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/config/SqlExplainDingConfig.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.config; 2 | 3 | import com.github.jaemon.dinger.support.CustomMessage; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | import java.text.MessageFormat; 8 | 9 | /** 10 | * 钉钉消息通知 11 | */ 12 | @Configuration 13 | public class SqlExplainDingConfig { 14 | @Bean 15 | public CustomMessage markDownMessage() { 16 | return (projectId, request) -> 17 | MessageFormat.format("### 【慢SQL预警】\n{0}", request.getContent()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/constant/SqlExplainRules.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.constant; 2 | 3 | import java.util.Arrays; 4 | import java.util.HashSet; 5 | import java.util.Set; 6 | 7 | /** 8 | * 慢查规则配置 9 | */ 10 | public class SqlExplainRules { 11 | public static Set KEYSET = new HashSet<>(Arrays.asList("NULL")); 12 | 13 | public static Set TYPESET = new HashSet<>(Arrays.asList("index", "ALL")); 14 | 15 | public static Set SCRIPTSET = new HashSet<>(); 16 | } 17 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/controller/QuartzController.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.controller; 2 | 3 | import com.tangym.sql.server.dto.response.ApiResponse; 4 | import com.tangym.sql.server.dto.response.QuartzResponse; 5 | import com.tangym.sql.server.entity.Quartz; 6 | import com.tangym.sql.server.mapper.QuartzMapper; 7 | import com.tangym.sql.server.util.QuartzUtils; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.quartz.Scheduler; 10 | import org.quartz.Trigger; 11 | import org.springframework.util.ObjectUtils; 12 | import org.springframework.web.bind.annotation.*; 13 | 14 | import javax.annotation.Resource; 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | @Slf4j 19 | @RestController 20 | @RequestMapping("/quartz") 21 | public class QuartzController { 22 | 23 | @Resource 24 | private Scheduler scheduler; 25 | @Resource 26 | private QuartzMapper quartzMapper; 27 | 28 | 29 | @PostMapping("/create") 30 | public ApiResponse> createJob(@RequestBody Quartz quartz) { 31 | try { 32 | Quartz q = quartzMapper.selectByKey(quartz.getJobClass()); 33 | if (ObjectUtils.isEmpty(q)) { 34 | quartz.setRunAfterStart(false); 35 | int i = quartzMapper.insert(quartz); 36 | if (i > 0) { 37 | QuartzUtils.createScheduleJob(scheduler, quartz); 38 | } 39 | } 40 | QuartzUtils.createScheduleJob(scheduler, quartz); 41 | } catch (Exception e) { 42 | return ApiResponse.failResponse(e.getMessage()); 43 | } 44 | return ApiResponse.succResponse(); 45 | } 46 | 47 | @PostMapping("/pause") 48 | public ApiResponse> pauseJob(@RequestBody Quartz quartz) { 49 | try { 50 | QuartzUtils.pauseScheduleJob(scheduler, quartz.getJobName()); 51 | } catch (Exception e) { 52 | return ApiResponse.failResponse(e.getMessage()); 53 | } 54 | return ApiResponse.succResponse("暂停成功"); 55 | } 56 | 57 | @PostMapping("/runonce") 58 | public ApiResponse> runOnce(@RequestBody Quartz quartz) { 59 | try { 60 | QuartzUtils.runOnce(scheduler, quartz.getJobName()); 61 | } catch (Exception e) { 62 | return ApiResponse.failResponse(String.format("运行一次失败:%s", e.getMessage())); 63 | } 64 | return ApiResponse.succResponse("执行成功"); 65 | } 66 | 67 | @PostMapping("/resume") 68 | public ApiResponse> resume(@RequestBody Quartz quartz) { 69 | try { 70 | QuartzUtils.resumeScheduleJob(scheduler, quartz.getJobName()); 71 | } catch (Exception e) { 72 | return ApiResponse.failResponse(e.getMessage()); 73 | } 74 | return ApiResponse.succResponse("成功恢复运行"); 75 | } 76 | 77 | @PostMapping("/update") 78 | public ApiResponse> update(@RequestBody Quartz quartz) { 79 | try { 80 | int i = quartzMapper.updateByPrimaryKey(quartz); 81 | if (i > 0) { 82 | QuartzUtils.updateScheduleJob(scheduler, quartz); 83 | } 84 | } catch (Exception e) { 85 | return ApiResponse.failResponse(e.getMessage()); 86 | } 87 | return ApiResponse.succResponse("更新成功"); 88 | } 89 | 90 | @PostMapping("/delete") 91 | public ApiResponse> delete(@RequestBody Quartz quartz) { 92 | try { 93 | int i = quartzMapper.deleteByPrimaryKey(quartz.getId()); 94 | if (i > 0) { 95 | QuartzUtils.deleteScheduleJob(scheduler, quartz.getJobName()); 96 | } 97 | } catch (Exception e) { 98 | return ApiResponse.failResponse(e.getMessage()); 99 | } 100 | return ApiResponse.succResponse("完成删除"); 101 | } 102 | 103 | @GetMapping("/list") 104 | public ApiResponse> list() { 105 | List list = quartzMapper.selectAll(); 106 | List res = new ArrayList<>(); 107 | list.forEach(e -> { 108 | Trigger.TriggerState state = QuartzUtils.getStatusOfScheduleJob(scheduler, e); 109 | QuartzResponse quartzResponse = QuartzResponse.builder().id(e.getId()).jobName(e.getJobName()).jobClass(e.getJobClass()).cronExpression(e.getCronExpression()).status(state).runAfterStart(e.getRunAfterStart()).build(); 110 | res.add(quartzResponse); 111 | }); 112 | return ApiResponse.succResponse(res); 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/controller/SqlAppConfigsController.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.controller; 2 | 3 | import com.github.pagehelper.PageHelper; 4 | import com.github.pagehelper.PageInfo; 5 | import com.tangym.sql.server.dto.request.SqlAppConfigsPageRequest; 6 | import com.tangym.sql.server.dto.response.ApiResponse; 7 | import com.tangym.sql.server.entity.SqlAppConfigs; 8 | import com.tangym.sql.server.mapper.SqlAppConfigsMapper; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.springframework.web.bind.annotation.*; 11 | 12 | import javax.annotation.Resource; 13 | import java.util.List; 14 | 15 | @Slf4j 16 | @RestController 17 | @RequestMapping("/sql/config/service") 18 | public class SqlAppConfigsController { 19 | @Resource 20 | private SqlAppConfigsMapper sqlAppConfigsMapper; 21 | 22 | @PostMapping("/query") 23 | public ApiResponse> queryByPage(@RequestBody SqlAppConfigsPageRequest request) { 24 | PageHelper.startPage(request.getPageNum(), request.getPageSize()); 25 | List configs = sqlAppConfigsMapper.selectBySelective(request.getAppConfigs()); 26 | PageInfo sqlAppConfigsPageInfo = new PageInfo<>(configs); 27 | return ApiResponse.succResponse(sqlAppConfigsPageInfo); 28 | } 29 | 30 | @PostMapping("/add") 31 | public ApiResponse> add(@RequestBody SqlAppConfigs sqlAppConfigs) { 32 | int i = sqlAppConfigsMapper.insert(sqlAppConfigs); 33 | if (i > 0) { 34 | return ApiResponse.succResponse("创建成功"); 35 | } 36 | return ApiResponse.failResponse("创建失败"); 37 | } 38 | 39 | @PostMapping("/update") 40 | public ApiResponse> update(@RequestBody SqlAppConfigs sqlAppConfigs) { 41 | int i = sqlAppConfigsMapper.updateByPrimaryKeySelective(sqlAppConfigs); 42 | if (i > 0) { 43 | return ApiResponse.succResponse("更新成功"); 44 | } 45 | return ApiResponse.failResponse("更新失败"); 46 | } 47 | 48 | @GetMapping("/delete") 49 | public ApiResponse> delete(@RequestParam int id) { 50 | int i = sqlAppConfigsMapper.deleteByPrimaryKey(id); 51 | if (i > 0) { 52 | return ApiResponse.succResponse("删除成功"); 53 | } 54 | return ApiResponse.failResponse("删除失败"); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/controller/SqlCheckConfigRuleScriptController.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.controller; 2 | 3 | import com.tangym.sql.server.config.AfterServiceStarted; 4 | import com.tangym.sql.server.dto.response.ApiResponse; 5 | import com.tangym.sql.server.entity.SqlExplainConfigRulesScript; 6 | import com.tangym.sql.server.mapper.SqlExplainConfigRulesScriptMapper; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.web.bind.annotation.*; 9 | 10 | import javax.annotation.Resource; 11 | import java.util.List; 12 | 13 | @Slf4j 14 | @RestController 15 | @RequestMapping("/sql/config/rules/script") 16 | public class SqlCheckConfigRuleScriptController { 17 | @Resource 18 | private SqlExplainConfigRulesScriptMapper scriptMapper; 19 | 20 | @PostMapping("create") 21 | public ApiResponse> create(@RequestBody SqlExplainConfigRulesScript script) { 22 | int i = scriptMapper.insert(script); 23 | if (i > 0) { 24 | return ApiResponse.succResponse("创建成功"); 25 | } 26 | return ApiResponse.failResponse("创建失败"); 27 | } 28 | 29 | @PostMapping("update") 30 | public ApiResponse> update(@RequestBody SqlExplainConfigRulesScript script) { 31 | int i = scriptMapper.updateByPrimaryKey(script); 32 | List allScripts = scriptMapper.selectAllScripts(); 33 | if (i > 0) { 34 | AfterServiceStarted.SyncSqlExplainRules(null, allScripts); 35 | return ApiResponse.succResponse("更新成功"); 36 | } 37 | return ApiResponse.failResponse("更新失败"); 38 | } 39 | 40 | @GetMapping("list") 41 | public ApiResponse> list() { 42 | List rulesScripts = scriptMapper.selectAll(); 43 | return ApiResponse.succResponse(rulesScripts); 44 | } 45 | 46 | @GetMapping("delete") 47 | public ApiResponse> delete(@RequestParam int id) { 48 | int i = scriptMapper.deleteByPrimaryKey(id); 49 | if (i > 0) { 50 | return ApiResponse.succResponse("删除成功"); 51 | } 52 | 53 | return ApiResponse.failResponse("删除失败"); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/controller/SqlCheckConfigRulesController.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.controller; 2 | 3 | import com.tangym.sql.server.config.AfterServiceStarted; 4 | import com.tangym.sql.server.dto.response.ApiResponse; 5 | import com.tangym.sql.server.entity.SqlExplainConfigRules; 6 | import com.tangym.sql.server.mapper.SqlExplainConfigRulesMapper; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.web.bind.annotation.*; 9 | 10 | import javax.annotation.Resource; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | @Slf4j 15 | @RestController 16 | @RequestMapping("/sql/config/rules") 17 | public class SqlCheckConfigRulesController { 18 | @Resource 19 | private SqlExplainConfigRulesMapper sqlExplainConfigRulesMapper; 20 | 21 | @PostMapping("create") 22 | public ApiResponse> create(@RequestBody SqlExplainConfigRules sqlExplainConfigRules) { 23 | int i = sqlExplainConfigRulesMapper.insert(sqlExplainConfigRules); 24 | if (i > 0) { 25 | return ApiResponse.succResponse("创建成功"); 26 | } 27 | return ApiResponse.failResponse("创建失败"); 28 | } 29 | 30 | @PostMapping("update") 31 | public ApiResponse> update(@RequestBody SqlExplainConfigRules sqlExplainConfigRules) { 32 | int i = sqlExplainConfigRulesMapper.updateByPrimaryKey(sqlExplainConfigRules); 33 | if (i > 0) { 34 | if (sqlExplainConfigRules.getRuleKey().equals("criteria")) { 35 | AfterServiceStarted.SyncSqlExplainRules(sqlExplainConfigRules, new ArrayList<>()); 36 | } 37 | return ApiResponse.succResponse("更新成功"); 38 | } 39 | return ApiResponse.failResponse("更新失败"); 40 | } 41 | 42 | @GetMapping("list") 43 | public ApiResponse> list() { 44 | List sqlExplainConfigRules = sqlExplainConfigRulesMapper.selectAll(); 45 | return ApiResponse.succResponse(sqlExplainConfigRules); 46 | } 47 | 48 | @GetMapping("capture") 49 | public String getCapture() { 50 | SqlExplainConfigRules sqlExplainConfigRules = sqlExplainConfigRulesMapper.selectByRuleKey("capture"); 51 | return sqlExplainConfigRules.getRuleDetail(); 52 | } 53 | 54 | @GetMapping("delete") 55 | public ApiResponse> delete(@RequestParam int id) { 56 | int i = sqlExplainConfigRulesMapper.deleteByPrimaryKey(id); 57 | if (i > 0) { 58 | return ApiResponse.succResponse("删除成功"); 59 | } 60 | 61 | return ApiResponse.failResponse("删除失败"); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/controller/SqlProcessLogController.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.controller; 2 | 3 | import com.tangym.sql.server.dto.response.ApiResponse; 4 | import com.tangym.sql.server.entity.SqlExplainInfo; 5 | import com.tangym.sql.server.entity.SqlProcessLog; 6 | import com.tangym.sql.server.mapper.SqlExplainInfoMapper; 7 | import com.tangym.sql.server.mapper.SqlProcessLogMapper; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.util.StringUtils; 10 | import org.springframework.web.bind.annotation.*; 11 | 12 | import javax.annotation.Resource; 13 | import java.time.LocalDateTime; 14 | import java.util.List; 15 | 16 | @Slf4j 17 | @RestController 18 | @RequestMapping("/sql/process/log") 19 | public class SqlProcessLogController { 20 | @Resource 21 | private SqlProcessLogMapper sqlProcessLogMapper; 22 | @Resource 23 | private SqlExplainInfoMapper sqlExplainInfoMapper; 24 | 25 | @PostMapping("/add") 26 | public ApiResponse> add(@RequestBody SqlProcessLog sqlProcessLog) { 27 | if (StringUtils.isEmpty(sqlProcessLog.getRemark())) { 28 | return ApiResponse.failResponse("remark处理说明参数必填"); 29 | } 30 | SqlExplainInfo sqlExplainInfo = new SqlExplainInfo(); 31 | sqlExplainInfo.setId(sqlProcessLog.getExplainInfoId()); 32 | if (sqlProcessLog.getType().equals("ignore") || sqlProcessLog.getType().equals("slow-optimized")) { 33 | sqlExplainInfo.setIsSlow(false); 34 | } 35 | if (sqlProcessLog.getType().equals("slow-todo")) { 36 | sqlExplainInfo.setIsSlow(true); 37 | log.info("add setIsSlow true:{}", sqlExplainInfo.getId()); 38 | } 39 | sqlExplainInfo.setRemark(sqlProcessLog.getRemark()); 40 | sqlExplainInfo.setUpdateTime(LocalDateTime.now()); 41 | int res = sqlExplainInfoMapper.updateByPrimaryKeySelective(sqlExplainInfo); 42 | if (res > 0) { 43 | sqlProcessLog.setCreateTime(LocalDateTime.now()); 44 | int i = sqlProcessLogMapper.insert(sqlProcessLog); 45 | if (i > 0) { 46 | return ApiResponse.succResponse(); 47 | } else { 48 | return ApiResponse.failResponse("数据插入失败"); 49 | } 50 | } 51 | return ApiResponse.failResponse("数据更新失败"); 52 | } 53 | 54 | @GetMapping("/query") 55 | public ApiResponse> query(@RequestParam Integer infoId) { 56 | List sqlProcessLogs = sqlProcessLogMapper.selectByInfoId(infoId); 57 | return ApiResponse.succResponse(sqlProcessLogs); 58 | } 59 | 60 | } 61 | 62 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/controller/SqlStarsController.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.controller; 2 | 3 | import com.tangym.sql.server.dto.response.ApiResponse; 4 | import com.tangym.sql.server.entity.SqlExplainStars; 5 | import com.tangym.sql.server.mapper.SqlExplainStarsMapper; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.web.bind.annotation.PostMapping; 8 | import org.springframework.web.bind.annotation.RequestBody; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | import javax.annotation.Resource; 13 | 14 | @Slf4j 15 | @RestController 16 | @RequestMapping("/sql/stars") 17 | public class SqlStarsController { 18 | @Resource 19 | private SqlExplainStarsMapper sqlExplainStarsMapper; 20 | 21 | @PostMapping("/add") 22 | public ApiResponse> add(@RequestBody SqlExplainStars sqlExplainStars) { 23 | Integer i = sqlExplainStarsMapper.selectByStarInfo(sqlExplainStars); 24 | if (null != i && i > 0) { 25 | return ApiResponse.succResponse("已添加关注"); 26 | } 27 | int res = sqlExplainStarsMapper.insert(sqlExplainStars); 28 | if (res > 0) { 29 | return ApiResponse.succResponse("已添加关注"); 30 | } 31 | return ApiResponse.failResponse("关注失败,请重试"); 32 | } 33 | 34 | @PostMapping("/delete") 35 | public ApiResponse> delete(@RequestBody SqlExplainStars sqlExplainStars) { 36 | int i = sqlExplainStarsMapper.deleteByStarInfo(sqlExplainStars); 37 | if (i > 0) { 38 | return ApiResponse.succResponse("已取消关注"); 39 | } 40 | return ApiResponse.failResponse("取消关注失败,请重试"); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/controller/SqlStatisticsController.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.controller; 2 | 3 | import com.github.pagehelper.PageHelper; 4 | import com.github.pagehelper.PageInfo; 5 | import com.tangym.sql.server.dto.request.SqlDetailListPageRequest; 6 | import com.tangym.sql.server.dto.response.ApiResponse; 7 | import com.tangym.sql.server.entity.SqlAppConfigs; 8 | import com.tangym.sql.server.entity.SqlExplainInfo; 9 | import com.tangym.sql.server.entity.SqlExplainStatistics; 10 | import com.tangym.sql.server.mapper.SqlAppConfigsMapper; 11 | import com.tangym.sql.server.mapper.SqlExplainInfoMapper; 12 | import com.tangym.sql.server.mapper.SqlExplainStarsMapper; 13 | import com.tangym.sql.server.mapper.SqlExplainStatisticsMapper; 14 | import lombok.extern.slf4j.Slf4j; 15 | import org.springframework.util.StringUtils; 16 | import org.springframework.web.bind.annotation.*; 17 | 18 | import javax.annotation.Resource; 19 | import java.util.HashMap; 20 | import java.util.List; 21 | import java.util.Map; 22 | 23 | @Slf4j 24 | @RestController 25 | @RequestMapping("/sql/statistics") 26 | public class SqlStatisticsController { 27 | @Resource 28 | private SqlAppConfigsMapper sqlAppConfigsMapper; 29 | @Resource 30 | private SqlExplainInfoMapper sqlExplainInfoMapper; 31 | @Resource 32 | private SqlExplainStarsMapper sqlExplainStarsMapper; 33 | @Resource 34 | private SqlExplainStatisticsMapper sqlExplainStatisticsMapper; 35 | 36 | 37 | @GetMapping("/overall") 38 | public ApiResponse> overall(@RequestParam String jobNumber, String tabKey) { 39 | if (StringUtils.isEmpty(jobNumber)) { 40 | return ApiResponse.failResponse("工号不可为空"); 41 | } 42 | Map res = new HashMap<>(); 43 | if (tabKey.equals("star")) { 44 | int total = sqlAppConfigsMapper.selectCount(); 45 | res.put("count", total); 46 | List apps = sqlExplainStarsMapper.selectByJobNum(jobNumber); 47 | res.put("starCount", apps.size()); 48 | List sqlExplainStatistics = null; 49 | if (apps.size() != 0) { 50 | sqlExplainStatistics = sqlExplainStatisticsMapper.selectByServiceNames(apps); 51 | } 52 | res.put("list", sqlExplainStatistics); 53 | } 54 | 55 | if (tabKey.equals("all")) { 56 | int count = sqlAppConfigsMapper.selectCount(); 57 | res.put("count", count); 58 | int starTotal = sqlExplainStarsMapper.selectCountByJobNum(jobNumber); 59 | res.put("starCount", starTotal); 60 | List sqlExplainStatistics = sqlExplainStatisticsMapper.selectAll(); 61 | res.put("list", sqlExplainStatistics); 62 | } 63 | return ApiResponse.succResponse(res); 64 | } 65 | 66 | @GetMapping("/detail/overall") 67 | public ApiResponse> serviceDetailOverall(@RequestParam String app) { 68 | Map resMap = new HashMap<>(); 69 | try { 70 | SqlExplainStatistics sqlExplainStatistics = sqlExplainStatisticsMapper.selectByServiceName(app); 71 | SqlAppConfigs appConfigs = sqlAppConfigsMapper.selectByService(app); 72 | resMap.put("users", appConfigs.getUsers()); 73 | resMap.put("explainSwitch", appConfigs.getExplainSwitch()); 74 | resMap.put("dingToken", appConfigs.getDingToken()); 75 | resMap.put("serviceName", app); 76 | resMap.put("health", sqlExplainStatistics.getHealth()); 77 | resMap.put("slowPercent", sqlExplainStatistics.getSlowPercent()); 78 | resMap.put("slowTotal", sqlExplainStatistics.getSlowTotal()); 79 | resMap.put("explainTotal", sqlExplainStatistics.getExplainTotal()); 80 | resMap.put("latestSlowInSeven", sqlExplainStatistics.getLatestSlowInSeven()); 81 | } catch (Exception e) { 82 | return ApiResponse.failResponse(e.getMessage()); 83 | } 84 | return ApiResponse.succResponse(resMap); 85 | } 86 | 87 | @PostMapping("/detail/query") 88 | public ApiResponse> serviceDetailList(@RequestBody SqlDetailListPageRequest request) { 89 | PageHelper.startPage(request.getPageNum(), request.getPageSize()); 90 | List infos = sqlExplainInfoMapper.queryByConditions(request); 91 | PageInfo SqlExplainPageInfo = new PageInfo<>(infos); 92 | return ApiResponse.succResponse(SqlExplainPageInfo); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/controller/SqlTableFingerPrintController.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.controller; 2 | 3 | 4 | import com.github.pagehelper.PageHelper; 5 | import com.github.pagehelper.PageInfo; 6 | import com.tangym.sql.server.dto.request.SqlTableHashPageRequest; 7 | import com.tangym.sql.server.dto.response.ApiResponse; 8 | import com.tangym.sql.server.entity.SqlExplainTableFingerprint; 9 | import com.tangym.sql.server.mapper.SqlExplainTableFingerprintMapper; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.springframework.web.bind.annotation.PostMapping; 12 | import org.springframework.web.bind.annotation.RequestBody; 13 | import org.springframework.web.bind.annotation.RequestMapping; 14 | import org.springframework.web.bind.annotation.RestController; 15 | 16 | import javax.annotation.Resource; 17 | import java.util.List; 18 | 19 | @Slf4j 20 | @RestController 21 | @RequestMapping("/sql/tables") 22 | public class SqlTableFingerPrintController { 23 | @Resource 24 | private SqlExplainTableFingerprintMapper sqlExplainTableFingerprintMapper; 25 | 26 | @PostMapping("/query") 27 | public ApiResponse> queryByPage(@RequestBody SqlTableHashPageRequest request) { 28 | PageHelper.startPage(request.getPageNum(), request.getPageSize()); 29 | List tables = sqlExplainTableFingerprintMapper.selectBySelective(request.getSqlExplainTableFingerprint()); 30 | PageInfo infos = new PageInfo<>(tables); 31 | return ApiResponse.succResponse(infos); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/dto/request/PageRequestDTO.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.dto.request; 2 | 3 | import lombok.Data; 4 | 5 | import javax.validation.constraints.Min; 6 | import javax.validation.constraints.NotNull; 7 | import java.io.Serializable; 8 | 9 | @Data 10 | public class PageRequestDTO implements Serializable { 11 | 12 | private static final long serialVersionUID = 443087049123344968L; 13 | 14 | @Min(1) 15 | @NotNull(message = "页码不能为空!") 16 | private Integer pageNum; 17 | 18 | @Min(0) 19 | @NotNull(message = "页大小不能为空!") 20 | private Integer pageSize; 21 | } 22 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/dto/request/SqlAppConfigsPageRequest.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.dto.request; 2 | 3 | import com.tangym.sql.server.entity.SqlAppConfigs; 4 | import lombok.Data; 5 | 6 | import java.io.Serializable; 7 | 8 | @Data 9 | public class SqlAppConfigsPageRequest extends PageRequestDTO implements Serializable { 10 | private static final long serialVersionUID = -32665127932663209L; 11 | 12 | private SqlAppConfigs appConfigs; 13 | } 14 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/dto/request/SqlDetailListPageRequest.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.dto.request; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import com.tangym.sql.server.entity.SqlExplainInfo; 5 | import lombok.Data; 6 | 7 | import java.io.Serializable; 8 | import java.time.LocalDateTime; 9 | 10 | @Data 11 | public class SqlDetailListPageRequest extends PageRequestDTO implements Serializable { 12 | private static final long serialVersionUID = -1602952120416950952L; 13 | private SqlExplainInfo sqlExplainInfo; 14 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") 15 | private LocalDateTime startTime; 16 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") 17 | private LocalDateTime endTime; 18 | } 19 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/dto/request/SqlTableHashPageRequest.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.dto.request; 2 | 3 | import com.tangym.sql.server.entity.SqlExplainTableFingerprint; 4 | import lombok.Data; 5 | 6 | import java.io.Serializable; 7 | 8 | @Data 9 | public class SqlTableHashPageRequest extends PageRequestDTO implements Serializable { 10 | private static final long serialVersionUID = 1426655952362719905L; 11 | 12 | private SqlExplainTableFingerprint sqlExplainTableFingerprint; 13 | } 14 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/dto/response/ApiResponse.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.dto.response; 2 | 3 | import org.apache.commons.lang.builder.ReflectionToStringBuilder; 4 | 5 | import java.io.Serializable; 6 | 7 | public class ApiResponse implements Serializable { 8 | 9 | private static final long serialVersionUID = 3452913614867544640L; 10 | 11 | private T data; 12 | 13 | private int result; 14 | 15 | private String errorMsg = ""; 16 | 17 | public ApiResponse() { 18 | } 19 | 20 | private ApiResponse(int result, String errorMsg) { 21 | this.result = result; 22 | this.errorMsg = errorMsg; 23 | } 24 | 25 | public static ApiResponse succResponse(T data) { 26 | ApiResponse response = new ApiResponse<>(1, "success"); 27 | response.setData(data); 28 | return response; 29 | } 30 | 31 | public static ApiResponse succResponse() { 32 | return new ApiResponse<>(1, "success"); 33 | } 34 | 35 | public static ApiResponse failResponse(int result, String errorMsg) { 36 | return new ApiResponse<>(result, errorMsg); 37 | } 38 | 39 | public static ApiResponse failResponse(T data) { 40 | ApiResponse response = new ApiResponse<>(0, "请求失败!"); 41 | response.setData(data); 42 | return response; 43 | } 44 | 45 | public T getData() { 46 | return data; 47 | } 48 | 49 | public void setData(T data) { 50 | this.data = data; 51 | } 52 | 53 | public int getResult() { 54 | return this.result; 55 | } 56 | 57 | public void setResult(int result) { 58 | this.result = result; 59 | } 60 | 61 | public String getErrorMsg() { 62 | return this.errorMsg; 63 | } 64 | 65 | public void setErrorMsg(String errorMsg) { 66 | this.errorMsg = errorMsg; 67 | } 68 | 69 | @Override 70 | public String toString() { 71 | return ReflectionToStringBuilder.toString(this); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/dto/response/ExplainResult.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.dto.response; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | @Data 8 | public class ExplainResult implements Serializable { 9 | private static final long serialVersionUID = 7431080679251530072L; 10 | private String selectType; 11 | private String table; 12 | private String partitions; 13 | private String type; 14 | private String possibleKeys; 15 | private String key; 16 | private Integer keyLen; 17 | private String ref; 18 | private Integer rows; 19 | private Integer filtered; 20 | private String Extra; 21 | } 22 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/dto/response/QuartzResponse.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.dto.response; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | import org.quartz.Trigger; 6 | 7 | import java.io.Serializable; 8 | 9 | @Builder 10 | @Data 11 | public class QuartzResponse implements Serializable { 12 | private static final long serialVersionUID = -3551571484983058969L; 13 | 14 | private Integer id; 15 | 16 | /** 17 | * 任务名称 18 | */ 19 | private String jobName; 20 | 21 | /** 22 | * 任务执行类 23 | */ 24 | private String jobClass; 25 | 26 | /** 27 | * 任务运行时间表达式 28 | */ 29 | private String cronExpression; 30 | 31 | /** 32 | * 是否在应用启动后运行 1是 0否 33 | */ 34 | private Boolean runAfterStart; 35 | 36 | /** 37 | * 任务运行状态 38 | */ 39 | private Trigger.TriggerState status; 40 | } 41 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/entity/Quartz.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.entity; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | @Data 8 | public class Quartz implements Serializable { 9 | private static final long serialVersionUID = 4115138464191541188L; 10 | private Integer id; 11 | /** 12 | * 任务名称 13 | */ 14 | private String jobName; 15 | /** 16 | * 任务执行类 17 | */ 18 | private String jobClass; 19 | /** 20 | * 任务运行时间表达式 21 | */ 22 | private String cronExpression; 23 | /** 24 | * 是否在应用启动后运行 1是 0否 25 | */ 26 | private Boolean runAfterStart; 27 | } 28 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/entity/SqlAppConfigs.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.entity; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | @Data 8 | public class SqlAppConfigs implements Serializable { 9 | private static final long serialVersionUID = -2089623614139774652L; 10 | private Integer id; 11 | /** 12 | * 应用名 13 | */ 14 | private String service; 15 | /** 16 | * 应用级别explain检查开关 1开启 0关闭 17 | */ 18 | private Boolean explainSwitch; 19 | /** 20 | * 应用负责人列表,通知到SQL分析群里哪些人 21 | */ 22 | private String users; 23 | /** 24 | * 钉群机器人token 25 | */ 26 | private String dingToken; 27 | } 28 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/entity/SqlExplainConfigRules.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.entity; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | @Data 8 | public class SqlExplainConfigRules implements Serializable { 9 | private static final long serialVersionUID = -6631333819177532067L; 10 | /** 11 | * 主键ID 12 | */ 13 | private Integer id; 14 | /** 15 | * 规则名称 16 | */ 17 | private String ruleName; 18 | /** 19 | * 规则内容 20 | */ 21 | private String ruleDetail; 22 | /** 23 | * 唯一键,识别规则所属 24 | */ 25 | private String ruleKey; 26 | } 27 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/entity/SqlExplainConfigRulesScript.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.entity; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | @Data 8 | public class SqlExplainConfigRulesScript implements Serializable { 9 | private static final long serialVersionUID = 9028680727012191304L; 10 | private Integer id; 11 | /** 12 | * 规则脚本 13 | */ 14 | private String script; 15 | /** 16 | * 规则备注信息 17 | */ 18 | private String remark; 19 | } 20 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/entity/SqlExplainInfo.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.entity; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | import java.time.LocalDateTime; 7 | 8 | @Data 9 | public class SqlExplainInfo implements Serializable { 10 | private static final long serialVersionUID = -62833220359613523L; 11 | 12 | private Integer id; 13 | 14 | /** 15 | * 关联hash签名表id列表 16 | */ 17 | private String tbFingerprints; 18 | 19 | 20 | /** 21 | * 服务名称/应用名称 22 | */ 23 | private String serviceName; 24 | 25 | /** 26 | * 原始不填参数的sql语句 27 | */ 28 | private String originalSql; 29 | 30 | /** 31 | * 原始语句的sql hash值 32 | */ 33 | private String originalSqlHash; 34 | 35 | /** 36 | * 最终可以执行的参数化后的sql 37 | */ 38 | private String parameterizedSql; 39 | 40 | /** 41 | * sql mybatis id 全路径 42 | */ 43 | private String sqlId; 44 | 45 | /** 46 | * host 47 | */ 48 | private String dbHost; 49 | 50 | /** 51 | * 端口号 52 | */ 53 | private String dbPort; 54 | 55 | /** 56 | * 数据库名 57 | */ 58 | private String dbName; 59 | 60 | /** 61 | * 用户名 62 | */ 63 | private String dbUser; 64 | 65 | /** 66 | * 密码 67 | */ 68 | private String dbPwd; 69 | 70 | /** 71 | * mysql server 版本 5 or 8 72 | */ 73 | private Integer serverVersion; 74 | 75 | /** 76 | * explain结果 77 | */ 78 | private String explainRes; 79 | 80 | /** 81 | * 执行explain是否失败 0否 1是 82 | */ 83 | private Integer isFailed; 84 | 85 | /** 86 | * 是否是慢查sql 87 | */ 88 | private Boolean isSlow; 89 | 90 | /** 91 | * 是否已告警 92 | */ 93 | private Boolean isAlert; 94 | 95 | /** 96 | * 线上是否出慢查故障 97 | */ 98 | private Boolean isProdFault; 99 | 100 | /** 101 | * 备注信息 102 | */ 103 | private String remark; 104 | 105 | /** 106 | * 更新时间 107 | */ 108 | private LocalDateTime updateTime; 109 | 110 | /** 111 | * 创建时间 112 | */ 113 | private LocalDateTime createTime; 114 | 115 | } 116 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/entity/SqlExplainStars.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.entity; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | @Data 8 | public class SqlExplainStars implements Serializable { 9 | private static final long serialVersionUID = -6237953424812137538L; 10 | /** 11 | * 主键ID 12 | */ 13 | private Integer id; 14 | /** 15 | * 工号 16 | */ 17 | private String jobNumber; 18 | /** 19 | * 关注应用 20 | */ 21 | private String serviceName; 22 | } 23 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/entity/SqlExplainStatistics.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.entity; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | import java.time.LocalDateTime; 7 | 8 | @Data 9 | public class SqlExplainStatistics implements Serializable { 10 | private static final long serialVersionUID = -3360684907406927226L; 11 | private Integer id; 12 | /** 13 | * 应用名 14 | */ 15 | private String serviceName; 16 | /** 17 | * 健康度 18 | */ 19 | private String health; 20 | /** 21 | * 慢查占比 22 | */ 23 | private String slowPercent; 24 | /** 25 | * 近七日新增慢查数 26 | */ 27 | private Integer latestSlowInSeven; 28 | /** 29 | * 慢查总数 30 | */ 31 | private Integer slowTotal; 32 | /** 33 | * 分析总数 34 | */ 35 | private Integer explainTotal; 36 | /** 37 | * 汇总时间点,以后从此时间点进行数据统计,做累加 38 | */ 39 | private LocalDateTime calcTime; 40 | } 41 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/entity/SqlExplainTableFingerprint.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.entity; 2 | 3 | import lombok.Data; 4 | import lombok.ToString; 5 | 6 | import java.io.Serializable; 7 | import java.time.LocalDateTime; 8 | 9 | @Data 10 | @ToString 11 | public class SqlExplainTableFingerprint implements Serializable { 12 | private static final long serialVersionUID = -6585305026361228847L; 13 | private Integer id; 14 | /** 15 | * 数据库名 16 | */ 17 | private String dbName; 18 | /** 19 | * host 20 | */ 21 | private String dbHost; 22 | /** 23 | * port 24 | */ 25 | private String dbPort; 26 | /** 27 | * 用户名 28 | */ 29 | private String dbUser; 30 | /** 31 | * 密码 32 | */ 33 | private String dbPwd; 34 | /** 35 | * 表名 36 | */ 37 | private String tbName; 38 | /** 39 | * md5 hash值 40 | */ 41 | private String fingerprint; 42 | /** 43 | * 创建时间 44 | */ 45 | private LocalDateTime createTime; 46 | /** 47 | * 更新时间 48 | */ 49 | private LocalDateTime updateTime; 50 | } 51 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/entity/SqlProcessLog.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.entity; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | import java.time.LocalDateTime; 7 | 8 | @Data 9 | public class SqlProcessLog implements Serializable { 10 | private static final long serialVersionUID = -3439213296239654085L; 11 | private Integer id; 12 | /** 13 | * 记录关联sql数据id 14 | */ 15 | private Integer explainInfoId; 16 | /** 17 | * 操作人工号 18 | */ 19 | private String userId; 20 | /** 21 | * 操作人姓名 22 | */ 23 | private String userName; 24 | /** 25 | * 操作类型 26 | */ 27 | private String type; 28 | /** 29 | * 备注 30 | */ 31 | private String remark; 32 | /** 33 | * 记录创建时间 34 | */ 35 | private LocalDateTime createTime; 36 | /** 37 | * 记录修改时间 38 | */ 39 | private LocalDateTime updateTime; 40 | } 41 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/mapper/QuartzMapper.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.mapper; 2 | 3 | import com.tangym.sql.server.entity.Quartz; 4 | import org.apache.ibatis.annotations.Select; 5 | 6 | import java.util.List; 7 | 8 | public interface QuartzMapper { 9 | int deleteByPrimaryKey(Integer id); 10 | 11 | int insert(Quartz record); 12 | 13 | int insertSelective(Quartz record); 14 | 15 | Quartz selectByPrimaryKey(Integer id); 16 | 17 | @Select({"select * from quartz where job_class=#{jobClass}"}) 18 | Quartz selectByKey(String jobClass); 19 | 20 | int updateByPrimaryKeySelective(Quartz record); 21 | 22 | int updateByPrimaryKey(Quartz record); 23 | 24 | @Select({"select * from quartz"}) 25 | List selectAll(); 26 | } 27 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/mapper/SqlAppConfigsMapper.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.mapper; 2 | 3 | import com.tangym.sql.server.entity.SqlAppConfigs; 4 | import org.apache.ibatis.annotations.Select; 5 | 6 | import java.util.List; 7 | 8 | public interface SqlAppConfigsMapper { 9 | int deleteByPrimaryKey(Integer id); 10 | 11 | int insert(SqlAppConfigs record); 12 | 13 | int insertSelective(SqlAppConfigs record); 14 | 15 | SqlAppConfigs selectByPrimaryKey(Integer id); 16 | 17 | int updateByPrimaryKeySelective(SqlAppConfigs record); 18 | 19 | int updateByPrimaryKey(SqlAppConfigs record); 20 | 21 | @Select({"select * from sql_app_configs where service = #{serviceName}"}) 22 | SqlAppConfigs selectByService(String serviceName); 23 | 24 | @Select({"select * from sql_app_configs"}) 25 | List selectAll(); 26 | 27 | List selectBySelective(SqlAppConfigs record); 28 | 29 | @Select({"select distinct service from sql_app_configs"}) 30 | List selectServices(); 31 | 32 | @Select({"select count(id) from sql_app_configs"}) 33 | int selectCount(); 34 | } 35 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/mapper/SqlExplainConfigRulesMapper.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.mapper; 2 | 3 | 4 | import com.tangym.sql.server.entity.SqlExplainConfigRules; 5 | import org.apache.ibatis.annotations.Select; 6 | 7 | import java.util.List; 8 | 9 | public interface SqlExplainConfigRulesMapper { 10 | int deleteByPrimaryKey(Integer id); 11 | 12 | int insert(SqlExplainConfigRules record); 13 | 14 | int insertSelective(SqlExplainConfigRules record); 15 | 16 | SqlExplainConfigRules selectByPrimaryKey(Integer id); 17 | 18 | int updateByPrimaryKeySelective(SqlExplainConfigRules record); 19 | 20 | int updateByPrimaryKey(SqlExplainConfigRules record); 21 | 22 | @Select({"select * from sql_explain_config_rules"}) 23 | List selectAll(); 24 | 25 | @Select({"select * from sql_explain_config_rules where rule_key=#{ruleKey}"}) 26 | SqlExplainConfigRules selectByRuleKey(String ruleKey); 27 | } 28 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/mapper/SqlExplainConfigRulesScriptMapper.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.mapper; 2 | 3 | import com.tangym.sql.server.entity.SqlExplainConfigRulesScript; 4 | import org.apache.ibatis.annotations.Select; 5 | 6 | import java.util.List; 7 | 8 | public interface SqlExplainConfigRulesScriptMapper { 9 | int deleteByPrimaryKey(Integer id); 10 | 11 | int insert(SqlExplainConfigRulesScript record); 12 | 13 | int insertSelective(SqlExplainConfigRulesScript record); 14 | 15 | SqlExplainConfigRulesScript selectByPrimaryKey(Integer id); 16 | 17 | int updateByPrimaryKeySelective(SqlExplainConfigRulesScript record); 18 | 19 | int updateByPrimaryKey(SqlExplainConfigRulesScript record); 20 | 21 | @Select({"select script from sql_explain_config_rules_script"}) 22 | List selectAllScripts(); 23 | 24 | @Select({"select * from sql_explain_config_rules_script"}) 25 | List selectAll(); 26 | } 27 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/mapper/SqlExplainInfoMapper.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.mapper; 2 | 3 | import com.tangym.sql.server.dto.request.SqlDetailListPageRequest; 4 | import com.tangym.sql.server.entity.SqlExplainInfo; 5 | import org.apache.ibatis.annotations.Delete; 6 | import org.apache.ibatis.annotations.Param; 7 | import org.apache.ibatis.annotations.Select; 8 | 9 | import java.time.LocalDateTime; 10 | import java.util.List; 11 | 12 | public interface SqlExplainInfoMapper { 13 | int deleteByPrimaryKey(Integer id); 14 | 15 | int insert(SqlExplainInfo record); 16 | 17 | int insertSelective(SqlExplainInfo record); 18 | 19 | SqlExplainInfo selectByPrimaryKey(Integer id); 20 | 21 | @Select({"select * from sql_explain_info where explain_res is null limit 100"}) 22 | List selectForExplain(); 23 | 24 | @Select({"select * from sql_explain_info where is_failed=1 and create_time > date_sub(current_timestamp(),INTERVAL 1 DAY ) limit 100"}) 25 | List selectFailedForExplain(); 26 | 27 | @Select({"select * from sql_explain_info where is_slow=true and is_alert is null"}) 28 | List selectFailedForAlert(); 29 | 30 | int updateByPrimaryKeySelective(SqlExplainInfo record); 31 | 32 | int updateByPrimaryKey(SqlExplainInfo record); 33 | 34 | @Select({"select count(*) from sql_explain_info where original_sql_hash = #{hash} and create_time > date_sub(current_timestamp(),INTERVAL 1 DAY )"}) 35 | int filterByOriginalSqlHash(String hash); 36 | 37 | @Select({"select * from sql_explain_info where original_sql = #{originalSql} order by id desc limit 2,1 "}) 38 | SqlExplainInfo select2ndRecordCompare(String originalSql); 39 | 40 | int countCheckedByService(@Param("app") String app, @Param("lastCalcTime") LocalDateTime lastCalcTime); 41 | 42 | int countSlowSqlByService(@Param("app") String app, @Param("lastCalcTime") LocalDateTime lastCalcTime); 43 | 44 | @Select({"select count(id) from sql_explain_info where service_name= #{app} and (is_slow=true or remark is not null) and create_time > date_sub(current_timestamp(),INTERVAL 7 DAY )"}) 45 | int countSlowSqlIncreaseSevenDaysByService(String app); 46 | 47 | List queryByConditions(SqlDetailListPageRequest request); 48 | 49 | @Delete({"delete from sql_explain_info where create_time <= DATE (DATE_SUB(NOW(),INTERVAL 15 DAY))"}) 50 | int deleteBeforeHalfMonth(); 51 | } 52 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/mapper/SqlExplainStarsMapper.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.mapper; 2 | 3 | 4 | import com.tangym.sql.server.entity.SqlExplainStars; 5 | import org.apache.ibatis.annotations.Delete; 6 | import org.apache.ibatis.annotations.Select; 7 | 8 | import java.util.List; 9 | 10 | public interface SqlExplainStarsMapper { 11 | int deleteByPrimaryKey(Integer id); 12 | 13 | int insert(SqlExplainStars record); 14 | 15 | int insertSelective(SqlExplainStars record); 16 | 17 | SqlExplainStars selectByPrimaryKey(Integer id); 18 | 19 | @Select({"select service_name from sql_explain_stars where job_number = #{ jobNumber }"}) 20 | List selectByJobNum(String jobNumber); 21 | 22 | @Select({"select count(id) from sql_explain_stars where job_number = #{ jobNumber }"}) 23 | int selectCountByJobNum(String jobNumber); 24 | 25 | int updateByPrimaryKeySelective(SqlExplainStars record); 26 | 27 | int updateByPrimaryKey(SqlExplainStars record); 28 | 29 | @Delete({"delete from sql_explain_stars where job_number=#{jobNumber} and service_name=#{serviceName}"}) 30 | int deleteByStarInfo(SqlExplainStars sqlExplainStars); 31 | 32 | @Select({"select id from sql_explain_stars where job_number=#{jobNumber} and service_name=#{serviceName}"}) 33 | Integer selectByStarInfo(SqlExplainStars sqlExplainStars); 34 | } 35 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/mapper/SqlExplainStatisticsMapper.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.mapper; 2 | 3 | 4 | import com.tangym.sql.server.entity.SqlExplainStatistics; 5 | import org.apache.ibatis.annotations.Param; 6 | import org.apache.ibatis.annotations.Select; 7 | 8 | import java.util.List; 9 | 10 | public interface SqlExplainStatisticsMapper { 11 | int deleteByPrimaryKey(Integer id); 12 | 13 | int insert(SqlExplainStatistics record); 14 | 15 | int insertSelective(SqlExplainStatistics record); 16 | 17 | SqlExplainStatistics selectByPrimaryKey(Integer id); 18 | 19 | int updateByPrimaryKeySelective(SqlExplainStatistics record); 20 | 21 | int updateByPrimaryKey(SqlExplainStatistics record); 22 | 23 | @Select({"select count(id) from sql_explain_statistics where service_name = #{serviceName}"}) 24 | int selectCountByServiceName(String serviceName); 25 | 26 | int updateByServiceName(SqlExplainStatistics sqlExplainStatistics); 27 | 28 | List selectByServiceNames(@Param("apps") List apps); 29 | 30 | @Select({"select * from sql_explain_statistics"}) 31 | List selectAll(); 32 | 33 | @Select({"select * from sql_explain_statistics where service_name = #{serviceName}"}) 34 | SqlExplainStatistics selectByServiceName(String serviceName); 35 | } 36 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/mapper/SqlExplainTableFingerprintMapper.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.mapper; 2 | 3 | 4 | import com.tangym.sql.server.entity.SqlExplainTableFingerprint; 5 | import org.apache.ibatis.annotations.Param; 6 | import org.apache.ibatis.annotations.Select; 7 | 8 | import java.util.List; 9 | 10 | public interface SqlExplainTableFingerprintMapper { 11 | int deleteByPrimaryKey(Integer id); 12 | 13 | int insert(SqlExplainTableFingerprint record); 14 | 15 | int insertSelective(SqlExplainTableFingerprint record); 16 | 17 | SqlExplainTableFingerprint selectByPrimaryKey(Integer id); 18 | 19 | int updateByPrimaryKeySelective(SqlExplainTableFingerprint record); 20 | 21 | int updateByPrimaryKey(SqlExplainTableFingerprint record); 22 | 23 | SqlExplainTableFingerprint selectByDbAndTableName(@Param("dbName") String dbName, @Param("tbName") String tbName); 24 | 25 | @Select({"select * from sql_explain_table_fingerprint where fingerprint is null or update_time > date_sub(current_timestamp(),INTERVAL 1 DAY ) limit 100"}) 26 | List selectForCalcFingerPrints(); 27 | 28 | List selectBySelective(SqlExplainTableFingerprint sqlExplainTableFingerprint); 29 | } 30 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/mapper/SqlProcessLogMapper.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.mapper; 2 | 3 | 4 | import com.tangym.sql.server.entity.SqlProcessLog; 5 | import org.apache.ibatis.annotations.Select; 6 | 7 | import java.util.List; 8 | 9 | public interface SqlProcessLogMapper { 10 | int deleteByPrimaryKey(Integer id); 11 | 12 | int insert(SqlProcessLog record); 13 | 14 | int insertSelective(SqlProcessLog record); 15 | 16 | SqlProcessLog selectByPrimaryKey(Integer id); 17 | 18 | int updateByPrimaryKeySelective(SqlProcessLog record); 19 | 20 | int updateByPrimaryKey(SqlProcessLog record); 21 | 22 | @Select({"select * from sql_process_log where explain_info_id = #{infoId} order by id desc limit 5"}) 23 | List selectByInfoId(Integer infoId); 24 | } 25 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/scheduler/SqlExplainClearDataTask.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.scheduler; 2 | 3 | import com.tangym.sql.server.mapper.SqlExplainInfoMapper; 4 | import lombok.extern.java.Log; 5 | import org.quartz.DisallowConcurrentExecution; 6 | import org.quartz.JobExecutionContext; 7 | import org.quartz.JobExecutionException; 8 | import org.springframework.scheduling.quartz.QuartzJobBean; 9 | 10 | import javax.annotation.Resource; 11 | 12 | /** 13 | * 数据清理任务 14 | */ 15 | @Log 16 | @DisallowConcurrentExecution 17 | public class SqlExplainClearDataTask extends QuartzJobBean { 18 | 19 | @Resource 20 | private SqlExplainInfoMapper sqlExplainInfoMapper; 21 | 22 | @Override 23 | protected void executeInternal(JobExecutionContext jobExecutionContext) { 24 | sqlExplainInfoMapper.deleteBeforeHalfMonth(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/scheduler/SqlExplainDingTalkAlertTask.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.scheduler; 2 | 3 | import com.alibaba.fastjson.JSONArray; 4 | import com.github.jaemon.dinger.DingerSender; 5 | import com.github.jaemon.dinger.core.DingerHelper; 6 | import com.github.jaemon.dinger.core.entity.DingerRequest; 7 | import com.github.jaemon.dinger.core.entity.enums.DingerType; 8 | import com.github.jaemon.dinger.core.entity.enums.MessageSubType; 9 | import com.tangym.sql.server.entity.SqlAppConfigs; 10 | import com.tangym.sql.server.entity.SqlExplainInfo; 11 | import com.tangym.sql.server.mapper.SqlAppConfigsMapper; 12 | import com.tangym.sql.server.mapper.SqlExplainInfoMapper; 13 | import lombok.extern.java.Log; 14 | import org.quartz.DisallowConcurrentExecution; 15 | import org.quartz.JobExecutionContext; 16 | import org.quartz.JobExecutionException; 17 | import org.springframework.beans.factory.annotation.Value; 18 | import org.springframework.scheduling.quartz.QuartzJobBean; 19 | import org.springframework.util.StringUtils; 20 | 21 | import javax.annotation.Resource; 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | import java.util.Map; 25 | import java.util.stream.Collectors; 26 | 27 | /** 28 | * 慢查告警任务 29 | */ 30 | @Log 31 | @DisallowConcurrentExecution 32 | public class SqlExplainDingTalkAlertTask extends QuartzJobBean { 33 | @Resource 34 | private SqlExplainInfoMapper sqlExplainInfoMapper; 35 | @Resource 36 | private SqlAppConfigsMapper sqlAppConfigsMapper; 37 | @Resource 38 | private DingerSender dingerSender; 39 | 40 | @Value("${spring.dinger.dingers.dingtalk.tokenId}") 41 | private String defaultDingToken; 42 | 43 | @Override 44 | protected void executeInternal(JobExecutionContext jobExecutionContext) { 45 | List infos = sqlExplainInfoMapper.selectFailedForAlert(); 46 | if (infos.size() == 0) { 47 | return; 48 | } 49 | Map> stringListMap = infos.stream().collect(Collectors.groupingBy(SqlExplainInfo::getServiceName)); 50 | stringListMap.forEach((k, v) -> { 51 | int count = v.size(); 52 | SqlAppConfigs appConfigs = sqlAppConfigsMapper.selectByService(k); 53 | List> listObjectFir = (List>) JSONArray.parse(appConfigs.getUsers()); 54 | List userList = new ArrayList<>(); 55 | String dingToken = appConfigs.getDingToken(); 56 | listObjectFir.forEach(map -> { 57 | String tel = map.get("tel"); 58 | userList.add(tel); 59 | }); 60 | log.info(String.format("alert:%s", k)); 61 | dingAlert(k, count, userList, dingToken); 62 | 63 | v.forEach(sqlinfo -> { 64 | sqlinfo.setIsAlert(true); 65 | sqlExplainInfoMapper.updateByPrimaryKey(sqlinfo); 66 | }); 67 | }); 68 | } 69 | 70 | private void dingAlert(String serviceName, int count, List userList, String dingToken) { 71 | String template = "**应用:** %s \n **负责人:** %s \n **慢SQL:** %s条 \n [前往查看处理](https://firefly.amh-group.com/#/sqlananysis/sqls?name=%s)"; 72 | StringBuilder users = new StringBuilder(); 73 | userList.forEach(usr -> users.append(String.format(" @%s ", usr))); 74 | String content = String.format(template, serviceName, users, count, serviceName); 75 | if (StringUtils.isEmpty(dingToken)) { 76 | DingerHelper.assignDinger(DingerType.DINGTALK, defaultDingToken, true); 77 | } else { 78 | DingerHelper.assignDinger(DingerType.DINGTALK, dingToken, true); 79 | } 80 | dingerSender.send(MessageSubType.MARKDOWN, DingerRequest.request(content, serviceName, userList)); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/scheduler/SqlExplainFailScheduleTask.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.scheduler; 2 | 3 | import org.quartz.DisallowConcurrentExecution; 4 | import org.quartz.JobExecutionContext; 5 | import org.quartz.JobExecutionException; 6 | import org.springframework.scheduling.quartz.QuartzJobBean; 7 | 8 | import javax.annotation.Resource; 9 | 10 | /** 11 | * 获取分析失败的sql再次进行分析任务 12 | */ 13 | @DisallowConcurrentExecution 14 | public class SqlExplainFailScheduleTask extends QuartzJobBean { 15 | @Resource 16 | private SqlExplainTaskCommon sqlExplainTaskCommon; 17 | 18 | @Override 19 | protected void executeInternal(JobExecutionContext jobExecutionContext) { 20 | sqlExplainTaskCommon.pullData(SqlExplainTaskCommon.Task.FAIL); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/scheduler/SqlExplainScheduleTask.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.scheduler; 2 | 3 | import org.quartz.DisallowConcurrentExecution; 4 | import org.quartz.JobExecutionContext; 5 | import org.quartz.JobExecutionException; 6 | import org.springframework.scheduling.quartz.QuartzJobBean; 7 | 8 | import javax.annotation.Resource; 9 | 10 | 11 | /** 12 | * 获取新捕获的sql进行分析任务 13 | */ 14 | @DisallowConcurrentExecution 15 | public class SqlExplainScheduleTask extends QuartzJobBean { 16 | @Resource 17 | private SqlExplainTaskCommon sqlExplainTaskCommon; 18 | 19 | @Override 20 | protected void executeInternal(JobExecutionContext jobExecutionContext) { 21 | sqlExplainTaskCommon.pullData(SqlExplainTaskCommon.Task.NORMAL); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/scheduler/SqlExplainStatisticsTask.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.scheduler; 2 | 3 | import com.tangym.sql.server.entity.SqlExplainStatistics; 4 | import com.tangym.sql.server.mapper.SqlAppConfigsMapper; 5 | import com.tangym.sql.server.mapper.SqlExplainInfoMapper; 6 | import com.tangym.sql.server.mapper.SqlExplainStatisticsMapper; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.quartz.DisallowConcurrentExecution; 9 | import org.quartz.JobExecutionContext; 10 | import org.quartz.JobExecutionException; 11 | import org.springframework.scheduling.quartz.QuartzJobBean; 12 | 13 | import javax.annotation.Resource; 14 | import java.text.DecimalFormat; 15 | import java.time.LocalDateTime; 16 | import java.util.List; 17 | 18 | /** 19 | * 统计信息汇总任务 20 | */ 21 | @Slf4j 22 | @DisallowConcurrentExecution 23 | public class SqlExplainStatisticsTask extends QuartzJobBean { 24 | @Resource 25 | private SqlAppConfigsMapper sqlAppConfigsMapper; 26 | @Resource 27 | private SqlExplainInfoMapper sqlExplainInfoMapper; 28 | @Resource 29 | private SqlExplainStatisticsMapper sqlExplainStatisticsMapper; 30 | 31 | @Override 32 | protected void executeInternal(JobExecutionContext jobExecutionContext) { 33 | List apps = sqlAppConfigsMapper.selectServices(); 34 | apps.forEach(app -> { 35 | LocalDateTime now = LocalDateTime.now(); 36 | SqlExplainStatistics statistics = sqlExplainStatisticsMapper.selectByServiceName(app); 37 | LocalDateTime lastCalcTime; 38 | Integer explainTotal; 39 | Integer slow; 40 | if (null != statistics) { 41 | lastCalcTime = statistics.getCalcTime(); 42 | explainTotal = statistics.getExplainTotal(); 43 | if (null == explainTotal) { 44 | explainTotal = 0; 45 | } 46 | slow = statistics.getSlowTotal(); 47 | if (null == slow) { 48 | slow = 0; 49 | } 50 | } else { 51 | lastCalcTime = null; 52 | explainTotal = 0; 53 | slow = 0; 54 | } 55 | int checkedCount = sqlExplainInfoMapper.countCheckedByService(app, lastCalcTime); 56 | int checkedTotal = explainTotal + checkedCount; 57 | String health = "excellent"; 58 | int slowTotal = 0; 59 | String percent = "0.00"; 60 | if (checkedTotal != 0) { 61 | int slowCount = sqlExplainInfoMapper.countSlowSqlByService(app, lastCalcTime); 62 | slowTotal = slow + slowCount; 63 | DecimalFormat df = new DecimalFormat("0.00"); 64 | percent = df.format(((double) slowTotal / (checkedTotal)) * 100); 65 | if (Double.parseDouble(percent) <= 0.1) { 66 | health = "excellent"; 67 | } else if (Double.parseDouble(percent) <= 1.0) { 68 | health = "good"; 69 | } else if (Double.parseDouble(percent) <= 2.0) { 70 | health = "average"; 71 | } else { 72 | health = "poor"; 73 | } 74 | } 75 | // 为了这里的数据准确,数据清理时请保留至少一周的数据 76 | int increaseSlowCountSevenDays = sqlExplainInfoMapper.countSlowSqlIncreaseSevenDaysByService(app); 77 | SqlExplainStatistics sqlExplainStatistics = new SqlExplainStatistics(); 78 | sqlExplainStatistics.setServiceName(app); 79 | sqlExplainStatistics.setHealth(health); 80 | sqlExplainStatistics.setSlowPercent(percent); 81 | sqlExplainStatistics.setSlowTotal(slowTotal); 82 | sqlExplainStatistics.setExplainTotal(checkedTotal); 83 | sqlExplainStatistics.setLatestSlowInSeven(increaseSlowCountSevenDays); 84 | sqlExplainStatistics.setCalcTime(now); 85 | int i = sqlExplainStatisticsMapper.selectCountByServiceName(app); 86 | if (i == 0) { 87 | sqlExplainStatisticsMapper.insert(sqlExplainStatistics); 88 | } 89 | sqlExplainStatisticsMapper.updateByServiceName(sqlExplainStatistics); 90 | }); 91 | 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/scheduler/SqlExplainTableHash.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.scheduler; 2 | 3 | import com.tangym.sql.server.entity.SqlExplainTableFingerprint; 4 | import com.tangym.sql.server.mapper.SqlExplainTableFingerprintMapper; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.apache.commons.codec.digest.DigestUtils; 7 | import org.quartz.DisallowConcurrentExecution; 8 | import org.quartz.JobExecutionContext; 9 | import org.quartz.JobExecutionException; 10 | import org.springframework.scheduling.quartz.QuartzJobBean; 11 | 12 | import javax.annotation.Resource; 13 | import java.sql.*; 14 | import java.time.LocalDateTime; 15 | import java.util.List; 16 | 17 | /** 18 | * 表Hash计算任务 19 | */ 20 | @Slf4j 21 | @DisallowConcurrentExecution 22 | public class SqlExplainTableHash extends QuartzJobBean { 23 | 24 | @Resource 25 | private SqlExplainTableFingerprintMapper sqlExplainTableFingerprintMapper; 26 | 27 | @Override 28 | protected void executeInternal(JobExecutionContext jobExecutionContext) { 29 | List sqlExplainTableFingerprints = sqlExplainTableFingerprintMapper.selectForCalcFingerPrints(); 30 | 31 | if (sqlExplainTableFingerprints.size() > 0) { 32 | sqlExplainTableFingerprints.forEach(sqlExplainTableFingerprint -> execDescribe(sqlExplainTableFingerprint)); 33 | } 34 | 35 | } 36 | 37 | private void execDescribe(SqlExplainTableFingerprint info) { 38 | Connection connection = null; 39 | Statement statement = null; 40 | try { 41 | Class.forName("com.mysql.jdbc.Driver"); 42 | String url = String.format("jdbc:mysql://%s:%s/%s?characterEncoding=utf8&useSSL=false", info.getDbHost(), info.getDbPort(), info.getDbName()); 43 | connection = DriverManager.getConnection(url, info.getDbUser(), info.getDbPwd()); 44 | statement = connection.createStatement(); 45 | ResultSet resultSet = null; 46 | try { 47 | resultSet = statement.executeQuery(String.format("SHOW CREATE TABLE %s", info.getTbName())); 48 | } catch (Exception e) { 49 | // 如果发生异常,可能是因为是分表,DB中只存了去除_数字的部分,所有尝试再试一次获取分表_0的 50 | resultSet = statement.executeQuery(String.format("SHOW CREATE TABLE %s_0", info.getTbName())); 51 | } 52 | String ddl = null; 53 | while (resultSet.next()) { 54 | ddl = resultSet.getString("Create Table"); 55 | } 56 | if (null != ddl) { 57 | String md5 = getMD5(ddl); 58 | if (!md5.equals(info.getFingerprint())) { 59 | info.setFingerprint(md5); 60 | info.setUpdateTime(LocalDateTime.now()); 61 | sqlExplainTableFingerprintMapper.updateByPrimaryKeySelective(info); 62 | } 63 | } 64 | resultSet.close(); 65 | statement.close(); 66 | connection.close(); 67 | } catch (ClassNotFoundException | SQLException e) { 68 | log.warn("获取表结构hash签名异常"); 69 | log.warn(info.toString()); 70 | } finally { 71 | try { 72 | if (statement != null) statement.close(); 73 | } catch (SQLException se2) { 74 | statement = null; 75 | } 76 | try { 77 | if (connection != null) connection.close(); 78 | } catch (SQLException se) { 79 | connection = null; 80 | 81 | } 82 | } 83 | } 84 | 85 | private String getMD5(String str) { 86 | return DigestUtils.md5Hex(str).toUpperCase(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/scheduler/SqlExplainTaskCommon.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.scheduler; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.JSONArray; 5 | import com.tangym.sql.server.constant.SqlExplainRules; 6 | import com.tangym.sql.server.dto.response.ExplainResult; 7 | import com.tangym.sql.server.entity.SqlExplainInfo; 8 | import com.tangym.sql.server.entity.SqlExplainTableFingerprint; 9 | import com.tangym.sql.server.mapper.SqlExplainInfoMapper; 10 | import com.tangym.sql.server.mapper.SqlExplainTableFingerprintMapper; 11 | import lombok.SneakyThrows; 12 | import lombok.extern.java.Log; 13 | import org.springframework.stereotype.Component; 14 | 15 | import javax.annotation.Resource; 16 | import javax.script.Bindings; 17 | import javax.script.ScriptEngine; 18 | import javax.script.ScriptEngineManager; 19 | import java.sql.*; 20 | import java.time.LocalDateTime; 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | 24 | /** 25 | * 慢查分析公共方法 核心代码 26 | */ 27 | @Component 28 | @Log 29 | public class SqlExplainTaskCommon { 30 | @Resource 31 | private SqlExplainInfoMapper sqlExplainInfoMapper; 32 | @Resource 33 | private SqlExplainTableFingerprintMapper sqlExplainTableFingerprintMapper; 34 | 35 | ScriptEngineManager factory = new ScriptEngineManager(); 36 | ScriptEngine engine = factory.getEngineByName("groovy"); 37 | Bindings bindings = engine.createBindings(); 38 | 39 | public void pullData(Task task) { 40 | switch (task) { 41 | case NORMAL: 42 | List nomalInfos = sqlExplainInfoMapper.selectForExplain(); 43 | if (null != nomalInfos && nomalInfos.size() > 0) { 44 | for (SqlExplainInfo info : nomalInfos) { 45 | execExplain(info); 46 | } 47 | } 48 | break; 49 | case FAIL: 50 | List failedInfos = sqlExplainInfoMapper.selectFailedForExplain(); 51 | if (null != failedInfos && failedInfos.size() > 0) { 52 | for (SqlExplainInfo info : failedInfos) { 53 | execExplain(info); 54 | } 55 | } 56 | break; 57 | default: 58 | break; 59 | } 60 | 61 | } 62 | 63 | private void execExplain(SqlExplainInfo info) { 64 | Connection connection = null; 65 | Statement statement = null; 66 | try { 67 | Class.forName("com.mysql.jdbc.Driver"); 68 | String url = String.format("jdbc:mysql://%s:%s/%s?characterEncoding=utf8&useSSL=false", info.getDbHost(), info.getDbPort(), info.getDbName()); 69 | connection = DriverManager.getConnection(url, info.getDbUser(), info.getDbPwd()); 70 | statement = connection.createStatement(); 71 | ResultSet resultSet = statement.executeQuery(String.format("EXPLAIN %s", info.getParameterizedSql())); 72 | List explainResultList = new ArrayList<>(); 73 | while (resultSet.next()) { 74 | ExplainResult explainResult = new ExplainResult(); 75 | explainResult.setSelectType(resultSet.getString("select_type")); 76 | explainResult.setTable(resultSet.getString("table")); 77 | explainResult.setPartitions(resultSet.getString("partitions")); 78 | explainResult.setType(resultSet.getString("type")); 79 | explainResult.setPossibleKeys(resultSet.getString("possible_keys")); 80 | if (resultSet.findColumn("key") > 0 && null == resultSet.getString("key")) { 81 | explainResult.setKey("NULL"); 82 | } else { 83 | explainResult.setKey(resultSet.getString("key")); 84 | } 85 | explainResult.setKeyLen(resultSet.getInt("key_len")); 86 | explainResult.setRef(resultSet.getString("ref")); 87 | explainResult.setRows(resultSet.getInt("rows")); 88 | explainResult.setFiltered(resultSet.getInt("filtered")); 89 | explainResult.setExtra(resultSet.getString("Extra")); 90 | explainResultList.add(explainResult); 91 | } 92 | info.setExplainRes(JSON.toJSONString(explainResultList)); 93 | Boolean analysis = analysis(info, explainResultList); 94 | info.setIsSlow(analysis); 95 | info.setUpdateTime(LocalDateTime.now()); 96 | sqlExplainInfoMapper.updateByPrimaryKey(info); 97 | resultSet.close(); 98 | statement.close(); 99 | connection.close(); 100 | } catch (ClassNotFoundException | SQLException e) { 101 | info.setIsFailed(1); 102 | info.setUpdateTime(LocalDateTime.now()); 103 | sqlExplainInfoMapper.updateByPrimaryKey(info); 104 | } finally { 105 | try { 106 | if (statement != null) statement.close(); 107 | } catch (SQLException se2) { 108 | statement = null; 109 | } 110 | try { 111 | if (connection != null) connection.close(); 112 | } catch (SQLException se) { 113 | connection = null; 114 | } 115 | } 116 | } 117 | 118 | @SneakyThrows 119 | private Boolean analysis(SqlExplainInfo info, List explainResultList) { 120 | for (ExplainResult explainResult : explainResultList) { 121 | bindings.put("info", info); 122 | bindings.put("explainResult", explainResult); 123 | boolean flag = false; 124 | for (String script : SqlExplainRules.SCRIPTSET) { 125 | if ((boolean) engine.eval(script, bindings)) { 126 | flag = true; 127 | break; 128 | } 129 | } 130 | if (flag) { 131 | continue; 132 | } 133 | /** 134 | * 慢查询判断规则 {"keySet":["NULL"],"typeSet":["index","ALL"]} 135 | */ 136 | if (SqlExplainRules.KEYSET.contains(explainResult.getKey()) || SqlExplainRules.TYPESET.contains(explainResult.getType())) { 137 | SqlExplainInfo secondRecordForCompare = sqlExplainInfoMapper.select2ndRecordCompare(info.getOriginalSql()); 138 | if (null != secondRecordForCompare && !secondRecordForCompare.getIsSlow()) { 139 | String tbFingerprints = secondRecordForCompare.getTbFingerprints(); 140 | LocalDateTime createTime = secondRecordForCompare.getCreateTime(); 141 | String table = explainResult.getTable(); 142 | List hashIdlist = (List) JSONArray.parse(tbFingerprints); 143 | for (Integer id : hashIdlist) { 144 | SqlExplainTableFingerprint sqlExplainTableFingerprint = sqlExplainTableFingerprintMapper.selectByPrimaryKey(id); 145 | LocalDateTime updateTime = sqlExplainTableFingerprint.getUpdateTime(); 146 | if (sqlExplainTableFingerprint.getTbName().equalsIgnoreCase(table)) { 147 | if (updateTime.isAfter(createTime)) { 148 | return true; 149 | } 150 | } 151 | } 152 | } else { 153 | return true; 154 | } 155 | } 156 | } 157 | return false; 158 | } 159 | 160 | public enum Task { 161 | NORMAL, FAIL 162 | } 163 | 164 | } 165 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/java/com/tangym/sql/server/util/QuartzUtils.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.server.util; 2 | 3 | import com.tangym.sql.server.entity.Quartz; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.quartz.*; 6 | 7 | @Slf4j 8 | public class QuartzUtils { 9 | /** 10 | * 创建定时任务 定时任务创建之后默认启动状态 11 | * 12 | * @param scheduler 调度器 13 | * @param quartz 定时任务信息类 14 | * @throws Exception 15 | */ 16 | public static void createScheduleJob(Scheduler scheduler, Quartz quartz) throws SchedulerException, ClassNotFoundException { 17 | //获取到定时任务的执行类 必须是类的绝对路径名称 18 | //定时任务类需要是job类的具体实现 QuartzJobBean是job的抽象类。 19 | Class extends Job> jobClass = (Class extends Job>) Class.forName(quartz.getJobClass()); 20 | // 构建定时任务信息 21 | JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(quartz.getJobName()).build(); 22 | // 设置定时任务执行方式 23 | CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartz.getCronExpression()); 24 | // 构建触发器trigger 25 | CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(quartz.getJobName()).withSchedule(scheduleBuilder).build(); 26 | scheduler.scheduleJob(jobDetail, trigger); 27 | } 28 | 29 | /** 30 | * 根据任务名称暂停定时任务 31 | * 32 | * @param scheduler 调度器 33 | * @param jobName 定时任务名称 34 | * @throws SchedulerException 35 | */ 36 | public static void pauseScheduleJob(Scheduler scheduler, String jobName) throws SchedulerException { 37 | JobKey jobKey = JobKey.jobKey(jobName); 38 | scheduler.pauseJob(jobKey); 39 | } 40 | 41 | /** 42 | * 根据任务名称恢复定时任务 43 | * 44 | * @param scheduler 调度器 45 | * @param jobName 定时任务名称 46 | * @throws SchedulerException 47 | */ 48 | public static void resumeScheduleJob(Scheduler scheduler, String jobName) throws SchedulerException { 49 | JobKey jobKey = JobKey.jobKey(jobName); 50 | scheduler.resumeJob(jobKey); 51 | } 52 | 53 | /** 54 | * 根据任务名称立即运行一次定时任务 55 | * 56 | * @param scheduler 调度器 57 | * @param jobName 定时任务名称 58 | * @throws SchedulerException 59 | */ 60 | public static void runOnce(Scheduler scheduler, String jobName) throws SchedulerException { 61 | JobKey jobKey = JobKey.jobKey(jobName); 62 | scheduler.triggerJob(jobKey); 63 | } 64 | 65 | /** 66 | * 更新定时任务 67 | * 68 | * @param scheduler 调度器 69 | * @param quartz 定时任务信息类 70 | * @throws SchedulerException 71 | */ 72 | public static void updateScheduleJob(Scheduler scheduler, Quartz quartz) throws SchedulerException { 73 | //获取到对应任务的触发器 74 | TriggerKey triggerKey = TriggerKey.triggerKey(quartz.getJobName()); 75 | //设置定时任务执行方式 76 | CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartz.getCronExpression()); 77 | //重新构建任务的触发器trigger 78 | CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); 79 | trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build(); 80 | //重置对应的job 81 | scheduler.rescheduleJob(triggerKey, trigger); 82 | } 83 | 84 | /** 85 | * 根据定时任务名称从调度器当中删除定时任务 86 | * 87 | * @param scheduler 调度器 88 | * @param jobName 定时任务名称 89 | * @throws SchedulerException 90 | */ 91 | public static void deleteScheduleJob(Scheduler scheduler, String jobName) throws SchedulerException { 92 | JobKey jobKey = JobKey.jobKey(jobName); 93 | scheduler.deleteJob(jobKey); 94 | } 95 | 96 | public static Trigger.TriggerState getStatusOfScheduleJob(Scheduler scheduler, Quartz quartz) { 97 | try { 98 | TriggerKey triggerKey = TriggerKey.triggerKey(quartz.getJobName()); 99 | return scheduler.getTriggerState(triggerKey); 100 | } catch (SchedulerException e) { 101 | e.printStackTrace(); 102 | } 103 | return null; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8080 3 | servlet: 4 | context-path: /detect 5 | application-display-name: detect 6 | 7 | spring: 8 | application: 9 | name: sql-detect-server 10 | datasource: 11 | hikari: 12 | jdbc-url: jdbc:mysql://localhost:3306/sql_detect?serverTimezone=UTC&characterEncoding=utf8&allowMultiQueries=true&autoReconnect=true&useAffectedRows=true&useSSL=false 13 | username: root 14 | password: root 15 | driver-class-name: com.mysql.jdbc.Driver 16 | minimum-idle: 5 17 | maximum-pool-size: 30 18 | dinger: 19 | # 开启插件的服务需要配置 spring.application.name 20 | project-id: ${spring.application.name} 21 | dingers: 22 | # 使用钉钉机器人, 请根据自己机器人配置信息进行修改 23 | dingtalk: 24 | tokenId: b0b3f14df2ee8e8f0003c3490586f36889ea01aff5f96011f3f8cdd2811cdd16 25 | 26 | pagehelper: 27 | helperDialect: mysql 28 | reasonable: true 29 | supportMethodsArguments: true 30 | params: count=countSql 31 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/resources/mapper/QuartzMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | id 13 | , job_name, job_class, cron_expression, run_after_start 14 | 15 | 16 | select 17 | 18 | from quartz 19 | where id = #{id,jdbcType=INTEGER} 20 | 21 | 22 | delete 23 | from quartz 24 | where id = #{id,jdbcType=INTEGER} 25 | 26 | 28 | insert into quartz (job_name, job_class, cron_expression, 29 | run_after_start) 30 | values (#{jobName,jdbcType=VARCHAR}, #{jobClass,jdbcType=VARCHAR}, #{cronExpression,jdbcType=VARCHAR}, 31 | #{runAfterStart,jdbcType=BOOLEAN}) 32 | 33 | 35 | insert into quartz 36 | 37 | 38 | job_name, 39 | 40 | 41 | job_class, 42 | 43 | 44 | cron_expression, 45 | 46 | 47 | run_after_start, 48 | 49 | 50 | 51 | 52 | #{jobName,jdbcType=VARCHAR}, 53 | 54 | 55 | #{jobClass,jdbcType=VARCHAR}, 56 | 57 | 58 | #{cronExpression,jdbcType=VARCHAR}, 59 | 60 | 61 | #{runAfterStart,jdbcType=BOOLEAN}, 62 | 63 | 64 | 65 | 66 | update quartz 67 | 68 | 69 | job_name = #{jobName,jdbcType=VARCHAR}, 70 | 71 | 72 | job_class = #{jobClass,jdbcType=VARCHAR}, 73 | 74 | 75 | cron_expression = #{cronExpression,jdbcType=VARCHAR}, 76 | 77 | 78 | run_after_start = #{runAfterStart,jdbcType=BOOLEAN}, 79 | 80 | 81 | where id = #{id,jdbcType=INTEGER} 82 | 83 | 84 | update quartz 85 | set job_name = #{jobName,jdbcType=VARCHAR}, 86 | job_class = #{jobClass,jdbcType=VARCHAR}, 87 | cron_expression = #{cronExpression,jdbcType=VARCHAR}, 88 | run_after_start = #{runAfterStart,jdbcType=BOOLEAN} 89 | where id = #{id,jdbcType=INTEGER} 90 | 91 | 92 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/resources/mapper/SqlAppConfigsMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | id 13 | , service, explain_switch, users, ding_token 14 | 15 | 16 | select 17 | 18 | from sql_app_configs 19 | where id = #{id,jdbcType=INTEGER} 20 | 21 | 22 | select 23 | 24 | from sql_app_configs 25 | 26 | 27 | 28 | service like concat('%', #{service}, '%') 29 | 30 | 31 | and explain_switch like concat('%', #{explainSwitch}, '%') 32 | 33 | 34 | and users like concat('%', #{users}, '%') 35 | 36 | 37 | and ding_token like concat('%', #{dingToken}, '%') 38 | 39 | 40 | 41 | 42 | 43 | delete 44 | from sql_app_configs 45 | where id = #{id,jdbcType=INTEGER} 46 | 47 | 49 | insert into sql_app_configs (service, explain_switch, users, 50 | ding_token) 51 | values (#{service,jdbcType=VARCHAR}, #{explainSwitch,jdbcType=BOOLEAN}, #{users,jdbcType=VARCHAR}, 52 | #{dingToken,jdbcType=VARCHAR}) 53 | 54 | 56 | insert into sql_app_configs 57 | 58 | 59 | service, 60 | 61 | 62 | explain_switch, 63 | 64 | 65 | users, 66 | 67 | 68 | ding_token, 69 | 70 | 71 | 72 | 73 | #{service,jdbcType=VARCHAR}, 74 | 75 | 76 | #{explainSwitch,jdbcType=BOOLEAN}, 77 | 78 | 79 | #{users,jdbcType=VARCHAR}, 80 | 81 | 82 | #{dingToken,jdbcType=VARCHAR}, 83 | 84 | 85 | 86 | 87 | update sql_app_configs 88 | 89 | 90 | service = #{service,jdbcType=VARCHAR}, 91 | 92 | 93 | explain_switch = #{explainSwitch,jdbcType=BOOLEAN}, 94 | 95 | 96 | users = #{users,jdbcType=VARCHAR}, 97 | 98 | 99 | ding_token = #{dingToken,jdbcType=VARCHAR}, 100 | 101 | 102 | where id = #{id,jdbcType=INTEGER} 103 | 104 | 105 | update sql_app_configs 106 | set service = #{service,jdbcType=VARCHAR}, 107 | explain_switch = #{explainSwitch,jdbcType=BOOLEAN}, 108 | users = #{users,jdbcType=VARCHAR}, 109 | ding_token = #{dingToken,jdbcType=VARCHAR} 110 | where id = #{id,jdbcType=INTEGER} 111 | 112 | 113 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/resources/mapper/SqlExplainConfigRulesMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | id 12 | , rule_name, rule_detail, rule_key 13 | 14 | 15 | select 16 | 17 | from sql_explain_config_rules 18 | where id = #{id,jdbcType=INTEGER} 19 | 20 | 21 | delete 22 | from sql_explain_config_rules 23 | where id = #{id,jdbcType=INTEGER} 24 | 25 | 27 | insert into sql_explain_config_rules (rule_name, rule_detail, rule_key) 28 | values (#{ruleName,jdbcType=VARCHAR}, #{ruleDetail,jdbcType=VARCHAR}, #{ruleKey,jdbcType=VARCHAR}) 29 | 30 | 32 | insert into sql_explain_config_rules 33 | 34 | 35 | rule_name, 36 | 37 | 38 | rule_detail, 39 | 40 | 41 | rule_key, 42 | 43 | 44 | 45 | 46 | #{ruleName,jdbcType=VARCHAR}, 47 | 48 | 49 | #{ruleDetail,jdbcType=VARCHAR}, 50 | 51 | 52 | #{ruleKey,jdbcType=VARCHAR}, 53 | 54 | 55 | 56 | 57 | update sql_explain_config_rules 58 | 59 | 60 | rule_name = #{ruleName,jdbcType=VARCHAR}, 61 | 62 | 63 | rule_detail = #{ruleDetail,jdbcType=VARCHAR}, 64 | 65 | 66 | rule_key = #{ruleKey,jdbcType=VARCHAR}, 67 | 68 | 69 | where id = #{id,jdbcType=INTEGER} 70 | 71 | 72 | update sql_explain_config_rules 73 | set rule_name = #{ruleName,jdbcType=VARCHAR}, 74 | rule_detail = #{ruleDetail,jdbcType=VARCHAR}, 75 | rule_key = #{ruleKey,jdbcType=VARCHAR} 76 | where id = #{id,jdbcType=INTEGER} 77 | 78 | 79 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/resources/mapper/SqlExplainConfigRulesScriptMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | id 11 | , script, remark 12 | 13 | 14 | select 15 | 16 | from sql_explain_config_rules_script 17 | where id = #{id,jdbcType=INTEGER} 18 | 19 | 20 | delete 21 | from sql_explain_config_rules_script 22 | where id = #{id,jdbcType=INTEGER} 23 | 24 | 26 | insert into sql_explain_config_rules_script (script, remark) 27 | values (#{script,jdbcType=VARCHAR}, #{remark,jdbcType=VARCHAR}) 28 | 29 | 31 | insert into sql_explain_config_rules_script 32 | 33 | 34 | script, 35 | 36 | 37 | remark, 38 | 39 | 40 | 41 | 42 | #{script,jdbcType=VARCHAR}, 43 | 44 | 45 | #{remark,jdbcType=VARCHAR}, 46 | 47 | 48 | 49 | 51 | update sql_explain_config_rules_script 52 | 53 | 54 | script = #{script,jdbcType=VARCHAR}, 55 | 56 | 57 | remark = #{remark,jdbcType=VARCHAR}, 58 | 59 | 60 | where id = #{id,jdbcType=INTEGER} 61 | 62 | 63 | update sql_explain_config_rules_script 64 | set script = #{script,jdbcType=VARCHAR}, 65 | remark = #{remark,jdbcType=VARCHAR} 66 | where id = #{id,jdbcType=INTEGER} 67 | 68 | 69 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/resources/mapper/SqlExplainStarsMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | id 11 | , job_number, service_name 12 | 13 | 14 | select 15 | 16 | from sql_explain_stars 17 | where id = #{id,jdbcType=INTEGER} 18 | 19 | 20 | delete 21 | from sql_explain_stars 22 | where id = #{id,jdbcType=INTEGER} 23 | 24 | 26 | insert into sql_explain_stars (job_number, service_name) 27 | values (#{jobNumber,jdbcType=VARCHAR}, #{serviceName,jdbcType=VARCHAR}) 28 | 29 | 31 | insert into sql_explain_stars 32 | 33 | 34 | job_number, 35 | 36 | 37 | service_name, 38 | 39 | 40 | 41 | 42 | #{jobNumber,jdbcType=VARCHAR}, 43 | 44 | 45 | #{serviceName,jdbcType=VARCHAR}, 46 | 47 | 48 | 49 | 50 | update sql_explain_stars 51 | 52 | 53 | job_number = #{jobNumber,jdbcType=VARCHAR}, 54 | 55 | 56 | service_name = #{serviceName,jdbcType=VARCHAR}, 57 | 58 | 59 | where id = #{id,jdbcType=INTEGER} 60 | 61 | 62 | update sql_explain_stars 63 | set job_number = #{jobNumber,jdbcType=VARCHAR}, 64 | service_name = #{serviceName,jdbcType=VARCHAR} 65 | where id = #{id,jdbcType=INTEGER} 66 | 67 | 68 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/resources/mapper/SqlExplainStatisticsMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | id 16 | , service_name, health, slow_percent, latest_slow_in_seven, slow_total, explain_total, calc_time 17 | 18 | 19 | select 20 | 21 | from sql_explain_statistics 22 | where id = #{id,jdbcType=INTEGER} 23 | 24 | 25 | select * from sql_explain_statistics 26 | 27 | 28 | AND service_name IN 29 | 30 | #{item} 31 | 32 | 33 | 34 | 35 | 36 | delete 37 | from sql_explain_statistics 38 | where id = #{id,jdbcType=INTEGER} 39 | 40 | 42 | insert into sql_explain_statistics (service_name, health, slow_percent, 43 | latest_slow_in_seven, slow_total, explain_total, calc_time) 44 | values (#{serviceName,jdbcType=VARCHAR}, #{health,jdbcType=VARCHAR}, #{slowPercent,jdbcType=VARCHAR}, 45 | #{latestSlowInSeven,jdbcType=INTEGER}, #{slowTotal,jdbcType=INTEGER}, #{explainTotal,jdbcType=INTEGER}, 46 | #{calcTime,jdbcType=TIMESTAMP}) 47 | 48 | 50 | insert into sql_explain_statistics 51 | 52 | 53 | service_name, 54 | 55 | 56 | health, 57 | 58 | 59 | slow_percent, 60 | 61 | 62 | latest_slow_in_seven, 63 | 64 | 65 | slow_total, 66 | 67 | 68 | explain_total, 69 | 70 | 71 | calc_time, 72 | 73 | 74 | 75 | 76 | #{serviceName,jdbcType=VARCHAR}, 77 | 78 | 79 | #{health,jdbcType=VARCHAR}, 80 | 81 | 82 | #{slowPercent,jdbcType=VARCHAR}, 83 | 84 | 85 | #{latestSlowInSeven,jdbcType=INTEGER}, 86 | 87 | 88 | #{slowTotal,jdbcType=INTEGER}, 89 | 90 | 91 | #{explainTotal,jdbcType=INTEGER}, 92 | 93 | 94 | #{calcTime,jdbcType=TIMESTAMP}, 95 | 96 | 97 | 98 | 99 | update sql_explain_statistics 100 | 101 | 102 | service_name = #{serviceName,jdbcType=VARCHAR}, 103 | 104 | 105 | health = #{health,jdbcType=VARCHAR}, 106 | 107 | 108 | slow_percent = #{slowPercent,jdbcType=VARCHAR}, 109 | 110 | 111 | latest_slow_in_seven = #{latestSlowInSeven,jdbcType=INTEGER}, 112 | 113 | 114 | slow_total = #{slowTotal,jdbcType=INTEGER}, 115 | 116 | 117 | explain_total = #{explainTotal,jdbcType=INTEGER}, 118 | 119 | 120 | calc_time = #{calcTime,jdbcType=TIMESTAMP}, 121 | 122 | 123 | where id = #{id,jdbcType=INTEGER} 124 | 125 | 126 | update sql_explain_statistics 127 | set service_name = #{serviceName,jdbcType=VARCHAR}, 128 | health = #{health,jdbcType=VARCHAR}, 129 | slow_percent = #{slowPercent,jdbcType=VARCHAR}, 130 | latest_slow_in_seven = #{latestSlowInSeven,jdbcType=INTEGER}, 131 | slow_total = #{slowTotal,jdbcType=INTEGER}, 132 | explain_total = #{explainTotal,jdbcType=INTEGER}, 133 | calc_time = #{calcTime,jdbcType=TIMESTAMP} 134 | where id = #{id,jdbcType=INTEGER} 135 | 136 | 137 | update sql_explain_statistics 138 | set health = #{health,jdbcType=VARCHAR}, 139 | slow_percent = #{slowPercent,jdbcType=VARCHAR}, 140 | latest_slow_in_seven = #{latestSlowInSeven,jdbcType=INTEGER}, 141 | slow_total = #{slowTotal,jdbcType=INTEGER}, 142 | explain_total = #{explainTotal,jdbcType=INTEGER}, 143 | calc_time = #{calcTime,jdbcType=TIMESTAMP} 144 | where service_name = #{serviceName,jdbcType=VARCHAR} 145 | 146 | 147 | -------------------------------------------------------------------------------- /sql-detect-server/src/main/resources/mapper/SqlProcessLogMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | id 16 | , explain_info_id, user_id, user_name, `type`, remark, create_time, update_time 17 | 18 | 19 | select 20 | 21 | from sql_process_log 22 | where id = #{id,jdbcType=INTEGER} 23 | 24 | 25 | delete 26 | from sql_process_log 27 | where id = #{id,jdbcType=INTEGER} 28 | 29 | 31 | insert into sql_process_log (explain_info_id, user_id, user_name, 32 | `type`, remark, create_time, 33 | update_time) 34 | values (#{explainInfoId,jdbcType=INTEGER}, #{userId,jdbcType=VARCHAR}, #{userName,jdbcType=VARCHAR}, 35 | #{type,jdbcType=VARCHAR}, #{remark,jdbcType=VARCHAR}, #{createTime,jdbcType=TIMESTAMP}, 36 | #{updateTime,jdbcType=TIMESTAMP}) 37 | 38 | 40 | insert into sql_process_log 41 | 42 | 43 | explain_info_id, 44 | 45 | 46 | user_id, 47 | 48 | 49 | user_name, 50 | 51 | 52 | `type`, 53 | 54 | 55 | remark, 56 | 57 | 58 | create_time, 59 | 60 | 61 | update_time, 62 | 63 | 64 | 65 | 66 | #{explainInfoId,jdbcType=INTEGER}, 67 | 68 | 69 | #{userId,jdbcType=VARCHAR}, 70 | 71 | 72 | #{userName,jdbcType=VARCHAR}, 73 | 74 | 75 | #{type,jdbcType=VARCHAR}, 76 | 77 | 78 | #{remark,jdbcType=VARCHAR}, 79 | 80 | 81 | #{createTime,jdbcType=TIMESTAMP}, 82 | 83 | 84 | #{updateTime,jdbcType=TIMESTAMP}, 85 | 86 | 87 | 88 | 89 | update sql_process_log 90 | 91 | 92 | explain_info_id = #{explainInfoId,jdbcType=INTEGER}, 93 | 94 | 95 | user_id = #{userId,jdbcType=VARCHAR}, 96 | 97 | 98 | user_name = #{userName,jdbcType=VARCHAR}, 99 | 100 | 101 | `type` = #{type,jdbcType=VARCHAR}, 102 | 103 | 104 | remark = #{remark,jdbcType=VARCHAR}, 105 | 106 | 107 | create_time = #{createTime,jdbcType=TIMESTAMP}, 108 | 109 | 110 | update_time = #{updateTime,jdbcType=TIMESTAMP}, 111 | 112 | 113 | where id = #{id,jdbcType=INTEGER} 114 | 115 | 116 | update sql_process_log 117 | set explain_info_id = #{explainInfoId,jdbcType=INTEGER}, 118 | user_id = #{userId,jdbcType=VARCHAR}, 119 | user_name = #{userName,jdbcType=VARCHAR}, 120 | `type` = #{type,jdbcType=VARCHAR}, 121 | remark = #{remark,jdbcType=VARCHAR}, 122 | create_time = #{createTime,jdbcType=TIMESTAMP}, 123 | update_time = #{updateTime,jdbcType=TIMESTAMP} 124 | where id = #{id,jdbcType=INTEGER} 125 | 126 | 127 | -------------------------------------------------------------------------------- /sql-service-demo/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /sql-service-demo/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.7.0 9 | 10 | 11 | com.tangym 12 | sql-service-demo 13 | 0.0.1-SNAPSHOT 14 | sql-service-demo 15 | sql-service-demo 16 | 17 | 1.8 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-web 23 | 24 | 25 | org.mybatis.spring.boot 26 | mybatis-spring-boot-starter 27 | 28 | 29 | mysql 30 | mysql-connector-java 31 | 32 | 33 | 2.2.2 34 | 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-devtools 39 | runtime 40 | true 41 | 42 | 43 | mysql 44 | mysql-connector-java 45 | 5.1.49 46 | runtime 47 | 48 | 49 | org.projectlombok 50 | lombok 51 | true 52 | 53 | 54 | org.springframework.boot 55 | spring-boot-starter-test 56 | test 57 | 58 | 59 | 60 | 61 | 62 | 63 | org.springframework.boot 64 | spring-boot-maven-plugin 65 | 66 | 67 | 68 | org.projectlombok 69 | lombok 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /sql-service-demo/src/main/java/com/tangym/sqlservice/demo/SqlServiceDemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sqlservice.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SqlServiceDemoApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SqlServiceDemoApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /sql-service-demo/src/main/java/com/tangym/sqlservice/demo/config/DataSourceConfig.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sqlservice.demo.config; 2 | 3 | import org.apache.ibatis.session.SqlSessionFactory; 4 | import org.mybatis.spring.SqlSessionFactoryBean; 5 | import org.mybatis.spring.annotation.MapperScan; 6 | import org.springframework.beans.factory.annotation.Qualifier; 7 | import org.springframework.boot.context.properties.ConfigurationProperties; 8 | import org.springframework.boot.jdbc.DataSourceBuilder; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.context.annotation.Primary; 12 | import org.springframework.core.io.support.PathMatchingResourcePatternResolver; 13 | 14 | import javax.sql.DataSource; 15 | 16 | @Configuration 17 | @MapperScan( 18 | basePackages = "com.tangym.sqlservice.demo.mapper", 19 | sqlSessionFactoryRef = "sqlSessionFactory" 20 | ) 21 | public class DataSourceConfig { 22 | @Bean 23 | @Primary 24 | @ConfigurationProperties(prefix = "spring.datasource.hikari") 25 | public DataSource dataSource() { 26 | return DataSourceBuilder.create().build(); 27 | } 28 | 29 | @Bean 30 | @Primary 31 | public SqlSessionFactory sqlSessionFactory( 32 | @Qualifier("dataSource") DataSource dataSource 33 | ) throws Exception { 34 | SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); 35 | sqlSessionFactoryBean.setDataSource(dataSource); 36 | sqlSessionFactoryBean.setMapperLocations( 37 | new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*.xml") 38 | ); 39 | org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration(); 40 | configuration.setMapUnderscoreToCamelCase(true); 41 | sqlSessionFactoryBean.setConfiguration(configuration); 42 | return sqlSessionFactoryBean.getObject(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /sql-service-demo/src/main/java/com/tangym/sqlservice/demo/controller/DemoController.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sqlservice.demo.controller; 2 | 3 | import com.tangym.sqlservice.demo.domain.DemoTable; 4 | import com.tangym.sqlservice.demo.mapper.DemoTableMapper; 5 | import org.springframework.web.bind.annotation.*; 6 | 7 | import javax.annotation.Resource; 8 | 9 | @RestController 10 | @RequestMapping("/demo") 11 | public class DemoController { 12 | @Resource 13 | private DemoTableMapper demoTableMapper; 14 | 15 | @PostMapping("/add") 16 | public void add(@RequestBody DemoTable demoTable) { 17 | demoTableMapper.insert(demoTable); 18 | } 19 | 20 | @GetMapping("/get") 21 | public DemoTable get(long id) { 22 | return demoTableMapper.selectByPrimaryKey(id); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /sql-service-demo/src/main/java/com/tangym/sqlservice/demo/domain/DemoTable.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sqlservice.demo.domain; 2 | 3 | import java.io.Serializable; 4 | import lombok.Data; 5 | 6 | @Data 7 | public class DemoTable implements Serializable { 8 | 9 | private Integer id; 10 | 11 | private String user; 12 | 13 | private Integer age; 14 | 15 | private static final long serialVersionUID = 227162751497313268L; 16 | } 17 | -------------------------------------------------------------------------------- /sql-service-demo/src/main/java/com/tangym/sqlservice/demo/mapper/DemoTableMapper.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sqlservice.demo.mapper; 2 | 3 | import com.tangym.sqlservice.demo.domain.DemoTable; 4 | 5 | public interface DemoTableMapper { 6 | 7 | int deleteByPrimaryKey(Long id); 8 | 9 | int insert(DemoTable record); 10 | 11 | int insertSelective(DemoTable record); 12 | 13 | DemoTable selectByPrimaryKey(Long id); 14 | 15 | int updateByPrimaryKeySelective(DemoTable record); 16 | 17 | int updateByPrimaryKey(DemoTable record); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /sql-service-demo/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=8081 2 | spring.datasource.hikari.jdbc-url=jdbc:mysql://localhost:3306/sql_detect?serverTimezone=UTC&characterEncoding=utf8&allowMultiQueries=true&autoReconnect=true&useAffectedRows=true&useSSL=false 3 | spring.datasource.hikari.username=root 4 | spring.datasource.hikari.password=root 5 | spring.datasource.hikari.driver-class-name=com.mysql.jdbc.Driver 6 | 7 | 8 | -------------------------------------------------------------------------------- /sql-service-demo/src/main/resources/mapper/DemoTableMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | id,user,age 15 | 16 | 17 | 18 | select 19 | 20 | from demo_table 21 | where id = #{id,jdbcType=INTEGER} 22 | 23 | 24 | 25 | delete from demo_table 26 | where id = #{id,jdbcType=INTEGER} 27 | 28 | 29 | insert into demo_table 30 | ( id,user,age 31 | ) 32 | values (#{id,jdbcType=INTEGER},#{user,jdbcType=VARCHAR},#{age,jdbcType=INTEGER} 33 | ) 34 | 35 | 36 | insert into demo_table 37 | 38 | id, 39 | user, 40 | age, 41 | 42 | 43 | #{id,jdbcType=INTEGER}, 44 | #{user,jdbcType=VARCHAR}, 45 | #{age,jdbcType=INTEGER}, 46 | 47 | 48 | 49 | update demo_table 50 | 51 | 52 | user = #{user,jdbcType=VARCHAR}, 53 | 54 | 55 | age = #{age,jdbcType=INTEGER}, 56 | 57 | 58 | where id = #{id,jdbcType=INTEGER} 59 | 60 | 61 | update demo_table 62 | set 63 | user = #{user,jdbcType=VARCHAR}, 64 | age = #{age,jdbcType=INTEGER} 65 | where id = #{id,jdbcType=INTEGER} 66 | 67 | 68 | -------------------------------------------------------------------------------- /sql-service-demo/src/test/java/com/tangym/sqlservice/demo/SqlServiceDemoApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sqlservice.demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class SqlServiceDemoApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /sql-spring-plugin/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.tangym 8 | sql-spring-plugin 9 | 0.0.1-SNAPSHOT 10 | sql-spring-plugin 11 | sql-spring-plugin 12 | 13 | 14 | 1.8 15 | 8 16 | 8 17 | 18 | 19 | 20 | io.manbang 21 | gravity-plugin-api 22 | 1.0.0 23 | provided 24 | 25 | 26 | org.projectlombok 27 | lombok 28 | 1.18.10 29 | provided 30 | 31 | 32 | com.google.auto.service 33 | auto-service 34 | 1.0-rc7 35 | 36 | 37 | org.aspectj 38 | aspectjrt 39 | 1.9.5 40 | 41 | 42 | org.slf4j 43 | slf4j-api 44 | 1.7.25 45 | 46 | 47 | org.springframework 48 | spring-context 49 | 5.2.7.RELEASE 50 | 51 | 52 | org.springframework 53 | spring-aop 54 | 5.2.7.RELEASE 55 | 56 | 57 | 58 | 59 | 60 | 61 | org.apache.maven.plugins 62 | maven-source-plugin 63 | 3.2.0 64 | 65 | 66 | attach-sources 67 | package 68 | 69 | jar-no-fork 70 | 71 | 72 | 73 | 74 | false 75 | 76 | 77 | 78 | org.apache.maven.plugins 79 | maven-compiler-plugin 80 | 3.8.1 81 | 82 | ${java.version} 83 | ${java.version} 84 | ${java.version} 85 | UTF-8 86 | 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /sql-spring-plugin/src/main/java/com/tangym/sql/spring/plugin/ArgumentPrintAspect.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.spring.plugin; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.aspectj.lang.ProceedingJoinPoint; 5 | import org.aspectj.lang.annotation.Around; 6 | import org.aspectj.lang.annotation.Aspect; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Slf4j 10 | @Aspect 11 | @Component 12 | public class ArgumentPrintAspect { 13 | @Around("(@within(org.springframework.stereotype.Service) || @within(org.springframework.stereotype.Repository) || @within(org.springframework.stereotype.Controller))) " + 14 | "&& execution(public * *(..))") 15 | public Object printIoArgument(ProceedingJoinPoint point) throws Throwable { 16 | log.info("【入参】 {}", point.getArgs()); 17 | Object result = point.proceed(); 18 | log.info("【出参】 {}", result); 19 | return result; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /sql-spring-plugin/src/main/java/com/tangym/sql/spring/plugin/BeanDefinitionRegistryProcessor.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.spring.plugin; 2 | 3 | import io.manbang.gravity.plugin.Witness; 4 | import org.springframework.beans.factory.support.BeanDefinitionRegistry; 5 | 6 | /** 7 | * {@link BeanDefinitionRegistry} 拦截处理器,可以用来注册Spring的Bean定义 8 | */ 9 | public interface BeanDefinitionRegistryProcessor { 10 | /** 11 | * Bean注册的目击者 12 | * 13 | * @return 目击者 14 | */ 15 | default Witness getWitness() { 16 | return Witness.always(); 17 | } 18 | 19 | /** 20 | * 处理指定的Bean注册表,可以给它注册自己的 {@link org.springframework.beans.factory.config.BeanDefinition BeanDefinition} 21 | * 22 | * @param registry Bean定义注册表 23 | */ 24 | void process(BeanDefinitionRegistry registry); 25 | } 26 | -------------------------------------------------------------------------------- /sql-spring-plugin/src/main/java/com/tangym/sql/spring/plugin/RegisterBeanAdvice.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.spring.plugin; 2 | 3 | import io.manbang.gravity.plugin.Advice; 4 | import io.manbang.gravity.plugin.ExecuteContext; 5 | import org.springframework.beans.factory.support.BeanDefinitionRegistry; 6 | 7 | import java.util.ServiceLoader; 8 | 9 | public class RegisterBeanAdvice implements Advice { 10 | @Override 11 | public void exitMethod(ExecuteContext context) { 12 | BeanDefinitionRegistry registry = context.getArgument(); 13 | 14 | ClassLoader classLoader = context.getTargetClass().getClassLoader(); 15 | ServiceLoader processors = ServiceLoader.load(BeanDefinitionRegistryProcessor.class, getClass().getClassLoader()); 16 | processors.forEach(p -> { 17 | if (p.getWitness().saw(classLoader)) { 18 | p.process(registry); 19 | } 20 | }); 21 | } 22 | 23 | @Override 24 | public boolean isSwitchable() { 25 | return false; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /sql-spring-plugin/src/main/java/com/tangym/sql/spring/plugin/SpringPluginDefine.java: -------------------------------------------------------------------------------- 1 | package com.tangym.sql.spring.plugin; 2 | 3 | import com.google.auto.service.AutoService; 4 | 5 | import io.manbang.gravity.plugin.Plugin; 6 | import io.manbang.gravity.plugin.PluginDefine; 7 | import io.manbang.gravity.plugin.Witness; 8 | import net.bytebuddy.description.type.TypeDescription; 9 | import net.bytebuddy.matcher.ElementMatcher; 10 | import net.bytebuddy.matcher.ElementMatchers; 11 | 12 | @AutoService(PluginDefine.class) 13 | public class SpringPluginDefine implements PluginDefine { 14 | @Override 15 | public ElementMatcher getTypeMatcher() { 16 | return ElementMatchers.named("org.springframework.context.support.AbstractApplicationContext"); 17 | } 18 | 19 | @Override 20 | public Plugin[] getPlugins() { 21 | return Plugin.builder() 22 | .advice(ElementMatchers.named("prepareBeanFactory"), "com.tangym.sql.spring.plugin.RegisterBeanAdvice") 23 | .build(); 24 | } 25 | 26 | @Override 27 | public Witness getWitness() { 28 | return Witness.classes("org.springframework.context.support.AbstractApplicationContext"); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /wechat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangyiming/sql-detect/657febab417d30ca0863f40a0d50279673506c6b/wechat.png --------------------------------------------------------------------------------