MyBatis慢查询监控插件

转自:http://blog.itpub.net/29254281/viewspace-1976044/

功能
      1.定期统计每个SQL的执行次数,平均执行时间和平均返回行数,并且打印在日志中
      2.如果有慢SQL或者返回行数很多的SQL,直接在日志中打印详细信息.

maven依赖

  1.     <dependencies>
  2.         <dependency>
  3.             <groupId>org.softee</groupId>
  4.             <artifactId>pojo-mbean</artifactId>
  5.             <version>1.1</version>
  6.         </dependency>
  7.         <dependency>
  8.             <groupId>commons-codec</groupId>
  9.             <artifactId>commons-codec</artifactId>
  10.             <version>1.9</version>
  11.         </dependency>
  12.         <dependency>
  13.             <groupId>org.slf4j</groupId>
  14.             <artifactId>jcl-over-slf4j</artifactId>
  15.             <version>1.7.9</version>
  16.         </dependency>
  17.         <dependency>
  18.             <groupId>org.mybatis</groupId>
  19.             <artifactId>mybatis</artifactId>
  20.             <version>3.2.8</version>
  21.         </dependency>
  22.         <dependency>
  23.             <groupId>org.mybatis</groupId>
  24.             <artifactId>mybatis-spring</artifactId>
  25.             <version>1.2.2</version>
  26.         </dependency>
  27.     </dependencies>

代码:

  1. package com.vv.plugins;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.Collections;  
  5. import java.util.Iterator;  
  6. import java.util.List;  
  7. import java.util.Map;  
  8. import java.util.Properties;  
  9. import java.util.concurrent.ConcurrentHashMap;  
  10. import java.util.concurrent.atomic.AtomicLong;  
  11.   
  12. import javax.management.MalformedObjectNameException;  
  13.   
  14. import org.apache.commons.codec.digest.DigestUtils;  
  15. import org.apache.ibatis.executor.Executor;  
  16. import org.apache.ibatis.mapping.MappedStatement;  
  17. import org.apache.ibatis.plugin.Interceptor;  
  18. import org.apache.ibatis.plugin.Intercepts;  
  19. import org.apache.ibatis.plugin.Invocation;  
  20. import org.apache.ibatis.plugin.Plugin;  
  21. import org.apache.ibatis.plugin.Signature;  
  22. import org.apache.ibatis.session.ResultHandler;  
  23. import org.apache.ibatis.session.RowBounds;  
  24. import org.slf4j.Logger;  
  25. import org.slf4j.LoggerFactory;  
  26. import org.softee.management.annotation.MBean;  
  27. import org.softee.management.annotation.ManagedAttribute;  
  28. import org.softee.management.exception.ManagementException;  
  29. import org.softee.management.helper.MBeanRegistration;  
  30.   
  31. @Intercepts({  
  32.         @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,  
  33.                 RowBounds.class, ResultHandler.class }),  
  34.         @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) })  
  35. public class MapperInterceptor implements Interceptor {  
  36.     public static Logger LOG = LoggerFactory.getLogger(MapperInterceptor.class);  
  37.   
  38.     private Properties properties;  
  39.   
  40.     private static Map MAP = new ConcurrentHashMap(400);  
  41.   
  42.     static {  
  43.         Runnable r = new Runnable() {  
  44.   
  45.             @Override  
  46.             public void run() {  
  47.                 while (true) {  
  48.                     Map oldMap = MAP;  
  49.                     MAP = new ConcurrentHashMap(400);  
  50.                     List list = new ArrayList(oldMap.values());  
  51.                     Collections.sort(list);  
  52.                     Iterator it = list.iterator();  
  53.                     while (it.hasNext()) {  
  54.                         SqlInfo sql = it.next();  
  55.                         long avgts = (sql.getTotalts().get() / sql.getTotalcount().get());  
  56.                         long avgrows = (sql.getTotalrows().get() / sql.getTotalcount().get());  
  57.                         LOG.info("接口 :" + sql.getMapper() + ",SQL_ID:" + sql.getMd5() + ",一共执行" + sql.getTotalcount()  
  58.                                 + "次,平均时间" + avgts + "毫秒," + "平均返回记录数" + avgrows);  
  59.   
  60.                     }  
  61.                     try {  
  62.                         Thread.sleep(MonitorParameter.getInstance().getMonitorInterval() * 1000);  
  63.                     } catch (InterruptedException e) {  
  64.                         // TODO Auto-generated catch block  
  65.                         e.printStackTrace();  
  66.                     }  
  67.                 }  
  68.             }  
  69.         };  
  70.         new Thread(r).start();  
  71.     }  
  72.   
  73.     @Override  
  74.     public Object intercept(Invocation invocation) throws Throwable {  
  75.         long start = System.currentTimeMillis();  
  76.         List returnValue = (List) invocation.proceed();  
  77.         long end = System.currentTimeMillis();  
  78.   
  79.         Object[] args = invocation.getArgs();  
  80.         MappedStatement ms = (MappedStatement) args[0];  
  81.         Object p = args[1];  
  82.         RowBounds bounds = (RowBounds) args[2];  
  83.         String boundSql = ms.getBoundSql(p).getSql();  
  84.   
  85.         SqlInfo info = MAP.get(boundSql);  
  86.         if (info == null) {  
  87.             info = new SqlInfo(ms, p);  
  88.             MAP.put(boundSql, info);  
  89.         }  
  90.         info.logSqlInfo(p, returnValue.size(), end - start);  
  91.   
  92.         return returnValue;  
  93.     }  
  94.   
  95.     @Override  
  96.     public Object plugin(Object target) {  
  97.         return Plugin.wrap(target, this);  
  98.     }  
  99.   
  100.     @Override  
  101.     public void setProperties(Properties properties0) {  
  102.         this.properties = properties0;  
  103.     }  
  104. }  
  105.   
  106. @MBean(objectName = "jsmx:type=MonitorParameter")  
  107. class MonitorParameter {  
  108.     private int maxts = 300;  
  109.     private int maxrows = 100;  
  110.     private int monitorInterval = 30;  
  111.   
  112.     @ManagedAttribute  
  113.     public int getMaxts() {  
  114.         return maxts;  
  115.     }  
  116.   
  117.     @ManagedAttribute  
  118.     public void setMaxts(int maxts) {  
  119.         this.maxts = maxts;  
  120.     }  
  121.   
  122.     @ManagedAttribute  
  123.     public int getMaxrows() {  
  124.         return maxrows;  
  125.     }  
  126.   
  127.     @ManagedAttribute  
  128.     public void setMaxrows(int maxrows) {  
  129.         this.maxrows = maxrows;  
  130.     }  
  131.   
  132.     @ManagedAttribute  
  133.     public int getMonitorInterval() {  
  134.         return monitorInterval;  
  135.     }  
  136.   
  137.     @ManagedAttribute  
  138.     public void setMonitorInterval(int monitorInterval) {  
  139.         this.monitorInterval = monitorInterval;  
  140.     }  
  141.   
  142.     private static class SingletonHandler {  
  143.         private static MonitorParameter obj;  
  144.   
  145.         static {  
  146.             obj = new MonitorParameter();  
  147.             try {  
  148.                 new MBeanRegistration(obj).register();  
  149.             } catch (MalformedObjectNameException e) {  
  150.                 // TODO Auto-generated catch block  
  151.                 e.printStackTrace();  
  152.             } catch (ManagementException e) {  
  153.                 // TODO Auto-generated catch block  
  154.                 e.printStackTrace();  
  155.             }  
  156.         }  
  157.     }  
  158.   
  159.     public static MonitorParameter getInstance() {  
  160.         return SingletonHandler.obj;  
  161.     }  
  162. }  
  163.   
  164. class SqlInfo implements Comparable {  
  165.     public static Logger LOG = LoggerFactory.getLogger(SqlInfo.class);  
  166.   
  167.     private String mapper;  
  168.     private String sql;  
  169.     private String md5;  
  170.     private AtomicLong totalts = new AtomicLong(0);  
  171.     private AtomicLong totalcount = new AtomicLong(0);  
  172.     private AtomicLong totalrows = new AtomicLong(0);  
  173.   
  174.     public void setMapper(String mapper) {  
  175.         this.mapper = mapper;  
  176.     }  
  177.   
  178.     public SqlInfo(MappedStatement mappedStatement, Object parameterObject) {  
  179.         this.sql = mappedStatement.getBoundSql(parameterObject).getSql();  
  180.         this.mapper = mappedStatement.getId();  
  181.         this.md5 = DigestUtils.md5Hex(sql);  
  182.     }  
  183.   
  184.     public void logSqlInfo(Object para, int rows, long ts) {  
  185.         totalts.addAndGet(ts);  
  186.         totalcount.getAndIncrement();  
  187.         totalrows.addAndGet(rows);  
  188.   
  189.         if (ts > MonitorParameter.getInstance().getMaxts() || rows > MonitorParameter.getInstance().getMaxrows()) {  
  190.             LOG.warn("接口:" + this.mapper);  
  191.             LOG.warn("SQL_ID:" + this.md5);  
  192.             LOG.warn("SQL:" + this.sql);  
  193.             LOG.warn("参数:" + para);  
  194.             LOG.warn("执行时间:" + ts);  
  195.             LOG.warn("返回记录:" + rows);  
  196.         }  
  197.     }  
  198.   
  199.     public String getSql() {  
  200.         return sql;  
  201.     }  
  202.   
  203.     public String getMd5() {  
  204.         return md5;  
  205.     }  
  206.   
  207.     public AtomicLong getTotalts() {  
  208.         return totalts;  
  209.     }  
  210.   
  211.     public AtomicLong getTotalcount() {  
  212.         return totalcount;  
  213.     }  
  214.   
  215.     public AtomicLong getTotalrows() {  
  216.         return totalrows;  
  217.     }  
  218.   
  219.     public String getMapper() {  
  220.         return mapper;  
  221.     }  
  222.   
  223.     @Override  
  224.     public int compareTo(SqlInfo o) {  
  225.         Long ts = this.getTotalcount().get();  
  226.         Long ots = o.getTotalcount().get();  
  227.         return ts.compareTo(ots);  
  228.     }  
  229.   
  230. }  


使用:
将插件加入Spring管理,并作为插件添加到MyBatis


默认情况下,每30S打印SQL的统计信息,比如
17:56:29.023 [Thread-1] INFO  com.vv.plugins.MapperInterceptor - 接口 :com.vv.songod.mapper.SodMapper.getAllUser,SQL_ID:9f683436fe68935af28708d8f2920792,一共执行1次,平均时间35毫秒,平均返回记录数513
17:56:29.023 [Thread-1] INFO  com.vv.plugins.MapperInterceptor - 接口 :com.vv.songod.mapper.SodMapper.getUserByName,SQL_ID:e65912a2d6863ac310e66cbf3459ff46,一共执行2次,平均时间134毫秒,平均返回记录数1

默认如果有超过300ms执行时间或者返回行数超过100行的SQL,则直接打印在日志中.


另外程序添加了JMX支持,可以直接修改阈值而不需要重启程序.


超过 maxrows的返回行数,直接在日志打印
超过 maxts的执行时间,直接在日志打印
monitorInterval表示 每个多少S打印一次统计信息.

如果需要打印所有SQL的详细信息,则可以把maxts直接设置为0

itpub把泛型替换了,文件下载:MapperInterceptor.txt

参考:
http://www.cnblogs.com/fangjian0423/p/mybatis-interceptor.html
http://www.cnblogs.com/babyhhcsy/p/4500884.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值