diff --git a/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java b/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java index 43003c3548..542316eb99 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java @@ -53,6 +53,7 @@ import com.alibaba.druid.stat.JdbcDataSourceStat; import com.alibaba.druid.stat.JdbcStatManager; import com.alibaba.druid.util.ConcurrentIdentityHashMap; import com.alibaba.druid.util.DruidLoaderUtils; +import com.alibaba.druid.util.Histogram; import com.alibaba.druid.util.JdbcUtils; /** @@ -122,7 +123,7 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements System.out); protected List filters = new ArrayList(); - protected ExceptionSorter exceptionSorter = null; + protected ExceptionSorter exceptionSorter = null; protected Driver driver; @@ -175,6 +176,17 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements protected final AtomicLong startTransactionCount = new AtomicLong(); protected final AtomicLong rollbackCount = new AtomicLong(); + protected final Histogram transactionHistogram = new Histogram( + 10, + 100, + 1000, + 10 * 1000, + 100 * 1000); + + public Histogram getTransactionHistogram() { + return transactionHistogram; + } + public long getCommitCount() { return commitCount.get(); } @@ -1342,4 +1354,5 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements public abstract int getRawDriverMinorVersion(); public abstract String getProperties(); + } diff --git a/src/main/java/com/alibaba/druid/pool/PoolableConnection.java b/src/main/java/com/alibaba/druid/pool/PoolableConnection.java index 7c6658a325..0c9a8bc934 100644 --- a/src/main/java/com/alibaba/druid/pool/PoolableConnection.java +++ b/src/main/java/com/alibaba/druid/pool/PoolableConnection.java @@ -140,6 +140,7 @@ public class PoolableConnection implements PooledConnection, Connection { this.holder = null; conn = null; + startTransactionTimeMillis = 0; } // //////////////////// @@ -566,12 +567,16 @@ public class PoolableConnection implements PooledConnection, Connection { public void commit() throws SQLException { checkOpen(); - holder.getDataSource().incrementCommitCount(); + DruidAbstractDataSource dataSource = holder.getDataSource(); + dataSource.incrementCommitCount(); + try { conn.commit(); } catch (SQLException ex) { handleException(ex); } finally { + long transactionMillis = System.currentTimeMillis() - startTransactionTimeMillis; + dataSource.getTransactionHistogram().recode(transactionMillis); startTransactionTimeMillis = 0; } } @@ -584,14 +589,62 @@ public class PoolableConnection implements PooledConnection, Connection { public void rollback() throws SQLException { checkOpen(); - holder.getDataSource().incrementRollbackCount(); + DruidAbstractDataSource dataSource = holder.getDataSource(); + dataSource.incrementRollbackCount(); + try { conn.rollback(); } catch (SQLException ex) { handleException(ex); + } finally { + long transactionMillis = System.currentTimeMillis() - startTransactionTimeMillis; + dataSource.getTransactionHistogram().recode(transactionMillis); + startTransactionTimeMillis = 0; } } + @Override + public Savepoint setSavepoint(String name) throws SQLException { + checkOpen(); + + return conn.setSavepoint(name); + } + + @Override + public void rollback(Savepoint savepoint) throws SQLException { + checkOpen(); + + DruidAbstractDataSource dataSource = holder.getDataSource(); + dataSource.incrementRollbackCount(); + + try { + conn.rollback(savepoint); + } catch (SQLException ex) { + handleException(ex); + } finally { + long transactionMillis = System.currentTimeMillis() - startTransactionTimeMillis; + dataSource.getTransactionHistogram().recode(transactionMillis); + startTransactionTimeMillis = 0; + } + } + + @Override + public void releaseSavepoint(Savepoint savepoint) throws SQLException { + checkOpen(); + try { + conn.releaseSavepoint(savepoint); + } catch (SQLException ex) { + handleException(ex); + } + } + + @Override + public Clob createClob() throws SQLException { + checkOpen(); + + return conn.createClob(); + } + @Override public boolean isClosed() throws SQLException { if (holder == null) { @@ -699,40 +752,6 @@ public class PoolableConnection implements PooledConnection, Connection { return conn.setSavepoint(); } - @Override - public Savepoint setSavepoint(String name) throws SQLException { - checkOpen(); - - return conn.setSavepoint(name); - } - - @Override - public void rollback(Savepoint savepoint) throws SQLException { - checkOpen(); - try { - conn.rollback(savepoint); - } catch (SQLException ex) { - handleException(ex); - } - } - - @Override - public void releaseSavepoint(Savepoint savepoint) throws SQLException { - checkOpen(); - try { - conn.releaseSavepoint(savepoint); - } catch (SQLException ex) { - handleException(ex); - } - } - - @Override - public Clob createClob() throws SQLException { - checkOpen(); - - return conn.createClob(); - } - @Override public Blob createBlob() throws SQLException { checkOpen(); diff --git a/src/main/java/com/alibaba/druid/util/Histogram.java b/src/main/java/com/alibaba/druid/util/Histogram.java new file mode 100644 index 0000000000..1e21a972be --- /dev/null +++ b/src/main/java/com/alibaba/druid/util/Histogram.java @@ -0,0 +1,69 @@ +package com.alibaba.druid.util; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +public class Histogram { + + private final long[] ranges; + private final AtomicLong[] rangeCounters; + + public Histogram(long... ranges){ + this.ranges = ranges; + this.rangeCounters = new AtomicLong[ranges.length + 1]; + for (int i = 0; i < rangeCounters.length; i++) { + rangeCounters[i] = new AtomicLong(); + } + } + + public Histogram(TimeUnit timeUnit, long... ranges){ + this.ranges = new long[ranges.length]; + for (int i = 0; i < ranges.length; i++) { + this.ranges[i] = TimeUnit.MILLISECONDS.convert(ranges[i], timeUnit); + } + + rangeCounters = new AtomicLong[ranges.length + 1]; + for (int i = 0; i < rangeCounters.length; ++i) { + rangeCounters[i] = new AtomicLong(); + } + } + + public void reset() { + for (int i = 0; i < rangeCounters.length; i++) { + rangeCounters[i].set(0); + } + } + + public void recode(long millis) { + int index = rangeCounters.length - 1; + for (int i = 0; i < ranges.length; ++i) { + if (millis < ranges[i]) { + index = i; + break; + } + } + + rangeCounters[index].incrementAndGet(); + } + + public long get(int index) { + return rangeCounters[index].get(); + } + + @Override + public String toString() { + StringBuilder strBuilder = new StringBuilder(); + for (int i = 0; i < ranges.length; i++) { + strBuilder.append((i == 0 ? 0l : ranges[i - 1]) + " - " + ranges[i] + " : " + rangeCounters[i] + " "); + } + return strBuilder.toString(); + } + + public long[] toArray() { + long[] array = new long[rangeCounters.length]; + for (int i = 0; i < rangeCounters.length; i++) { + array[i] = rangeCounters[i].get(); + } + return array; + } +}