为什么用数据库连接池?

为什么要用数据库连接池?

如果我们分析一下典型的【连接数据库】所涉及的步骤,我们将理解为什么:

使用数据库驱动程序打开与数据库的连接

打开TCP套接字以读取/写入数据

通过套接字读取/写入数据

关闭连接

关闭套接字

很明显,【连接数据库】是相当昂贵的操作,因此,应该想办法尽可能地减少、避免这种操作。

这就是数据库连接池发挥作用的地方。通过简单地实现数据库连接容器(允许我们重用大量现有连接),我们可以有效地节省执行大量昂贵【连接数据库】的成本,从而提高数据库驱动应用程序的整体性能。

HikariCP快速入门

1、依赖

HikariCP

slf4j (不需要日志实现也能跑)

logback-core

logback-classic

1和2以及相应数据库的JDBC驱动是必要的,日志实现可以用其它方案。

2、简单的草稿程序

packageorg.sample.dao;importcom.zaxxer.hikari.HikariConfig;importcom.zaxxer.hikari.HikariDataSource;importorg.sample.entity.Profile;importorg.sample.exception.DaoException;importjava.sql.Connection;importjava.sql.PreparedStatement;importjava.sql.SQLException;public classTest {private static HikariConfig config = newHikariConfig();private staticHikariDataSource ds;static{

config.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/profiles?characterEncoding=utf8");

config.setUsername("root");

config.setPassword("???????");

config.addDataSourceProperty("cachePrepStmts", "true");

config.addDataSourceProperty("prepStmtCacheSize", "250");

config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");

ds= newHikariDataSource(config);

config= newHikariConfig();

}public static Connection getConnection() throwsSQLException {returnds.getConnection();

}privateTest(){}public static voidmain(String[] args) {

Profile profile= newProfile();

profile.setUsername("testname3");

profile.setPassword("123");

profile.setNickname("testnickname");int i = 0;try{

Connection conn=Test.getConnection();

String sql= "INSERT ignore INTO `profiles`.`profile` (`username`, `password`, `nickname`) " +

"VALUES (?, ?, ?)"; //添加ignore出现重复不会抛出异常而是返回0

try (PreparedStatement ps =conn.prepareStatement(sql)) {

ps.setString(1, profile.getUsername());

ps.setString(2, profile.getPassword());

ps.setString(3, profile.getNickname());

i=ps.executeUpdate();

}

}catch(SQLException e) {throw newDaoException(e);

}

System.out.println(i);

}

}

3、设置连接池参数(只列举常用的)

一台四核的电脑基本可以全部采用默认设置?

① autoCommit:控制由连接池所返回的connection默认的autoCommit状况。默认值为是true。

② connectionTimeout:该参数决定无可用connection时的最长等待时间,超时将抛出SQLException。允许的最小值为250,默认值是30000(30秒)。

③ maximumPoolSize:该参数控制连接池所允许的最大连接数(包括在用连接和空闲连接)。基本上,此值将确定应用程序与数据库实际连接的最大数量。它的合理值最好由你的具体执行环境确定。当连接池达到最大连接数,并且没有空闲连接时,调用getConnection()将会被阻塞,最长等待时间取决于connectionTimeout。 对于这个值设定多少比较好,涉及的东西有点多,详细可参看About Pool Sizing,一般可以简单用这个公式计算:连接数 = ((核心数 * 2) + 有效磁盘数),默认值是10。

④ minimumIdle:控制最小的空闲连接数,当连接池内空闲的连接数少于minimumIdle,且总连接数不大于maximumPoolSize时,HikariCP会尽力补充新的连接。出于性能方面的考虑,不建议设置此值,而是让HikariCP把连接池当做固定大小的处理,minimumIdle的默认值等于maximumPoolSize。

⑤ maxLifetime:用来设置一个connection在连接池中的最大存活时间。一个使用中的connection永远不会被移除,只有在它关闭后才会被移除。用微小的负衰减来避免连接池中的connection一次性大量灭绝。我们强烈建议设置这个值,它应该比数据库所施加的时间限制短个几秒。如果设置为0,则表示connection的存活时间为无限大,当然还要受制于idleTimeout。默认值是1800000(30分钟)。(不大理解,然而mysql的时间限制不是8个小时???)

⑥ idleTimeout:控制一个connection所被允许的最大空闲时间。当空闲的连接数超过minimumIdle时,一旦某个connection的持续空闲时间超过idleTimeout,就会被移除。只有当minimumIdle小于maximumPoolSize时,这个参数才生效。默认值是600000(10分钟)。

⑦ poolName:用户定义的连接池名称,主要显示在日志记录和JMX管理控制台中,以标识连接池以及它的配置。默认值由HikariCP自动生成。

4、MySQL配置

jdbcUrl=jdbc:mysql://127.0.0.1:3306/profiles?characterEncoding=utf8

username=root

password=test

dataSource.cachePrepStmts=true

dataSource.prepStmtCacheSize=250

dataSource.prepStmtCacheSqlLimit=2048

dataSource.useServerPrepStmts=true

dataSource.useLocalSessionState=true

dataSource.rewriteBatchedStatements=true

dataSource.cacheResultSetMetadata=true

dataSource.cacheServerConfiguration=true

dataSource.elideSetAutoCommits=true

dataSource.maintainTimeStats=false

① HikariCPDataSource.java,hikari.properties如上所示。

packageorg.sample.db;importcom.zaxxer.hikari.HikariConfig;importcom.zaxxer.hikari.HikariDataSource;importjava.sql.Connection;importjava.sql.SQLException;public classHikariCPDataSource {private static final String HIKARI_PROPERTIES_FILE_PATH = "/hikari.properties";private static HikariConfig config = newHikariConfig(HIKARI_PROPERTIES_FILE_PATH);private static HikariDataSource ds = newHikariDataSource(config);public static Connection getConnection() throwsSQLException {returnds.getConnection();

}

}

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

② ConnectionFactory.java

packageorg.sample.db;importjava.sql.Connection;importjava.sql.SQLException;/*** 线程池版*/

public classConnectionFactory {privateConnectionFactory() {//Exists to defeat instantiation

}private static final ThreadLocal LocalConnectionHolder = new ThreadLocal<>();public static Connection getConnection() throwsSQLException {

Connection conn=LocalConnectionHolder.get();if (conn == null ||conn.isClosed()) {

conn=HikariCPDataSource.getConnection();

LocalConnectionHolder.set(conn);

}returnconn;

}public static voidremoveLocalConnection() {

LocalConnectionHolder.remove();

}

}

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

③ ConnectionProxy.java(代码分层有错误!)

packageorg.sample.manager;importorg.sample.db.ConnectionFactory;importorg.sample.exception.DaoException;importjava.sql.Connection;/*** 对应线程池版本ConnectionFactory,方便在Service层进行事务控制*/

public classConnectionProxy {public static void setAutoCommit(booleanautoCommit) {try{

Connection conn=ConnectionFactory.getConnection();

conn.setAutoCommit(autoCommit);

}catch(Exception e) {throw newDaoException(e);

}

}public static voidcommit() {try{

Connection conn=ConnectionFactory.getConnection();

conn.commit();

}catch(Exception e) {throw newDaoException(e);

}

}public static voidrollback() {try{

Connection conn=ConnectionFactory.getConnection();

conn.rollback();

}catch(Exception e) {throw newDaoException(e);

}

}public static voidclose() {try{

Connection conn=ConnectionFactory.getConnection();

conn.close();

ConnectionFactory.removeLocalConnection();

}catch(Exception e) {throw newDaoException(e);

}

}//TODO 设置隔离级别

}

其它地方把LocalConnectionFactory改为ConnectionFactory,LocalConnectionProxy改为ConnectionProxy就行了!后续如果要换其它连接池,只需要改变ConnectionFactory.java里的一小点代码。

6、测试

packageorg.sample.manager;importorg.junit.Test;importorg.sample.dao.ProfileDAO;importorg.sample.dao.impl.ProfileDAOImpl;importorg.sample.entity.Profile;importorg.sample.exception.DaoException;importjava.util.ArrayList;importjava.util.Collections;importjava.util.LinkedList;importjava.util.List;importjava.util.concurrent.CountDownLatch;importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;importjava.util.concurrent.TimeUnit;importjava.util.logging.Logger;import staticorg.junit.Assert.assertTrue;public classDaoTest {private static final Logger LOGGER = Logger.getLogger(DaoTest.class.getName());private static final String ORIGIN_STRING = "hello";private staticString RandomString() {return Math.random() + ORIGIN_STRING +Math.random();

}private staticProfile RandomProfile() {

Profile profile= newProfile(RandomString(), ORIGIN_STRING, RandomString());returnprofile;

}private static final ProfileDAO PROFILE_DAO =ProfileDAOImpl.INSTANCE;private class Worker implementsRunnable {private final Profile profile =RandomProfile();

@Overridepublic voidrun() {

LOGGER.info(Thread.currentThread().getName()+ " has started his work");try{//ConnectionProxy.setAutoCommit(false);

PROFILE_DAO.saveProfile(profile);//ConnectionProxy.commit();

} catch(DaoException e) {

e.printStackTrace();

}finally{try{

ConnectionProxy.close();

}catch(DaoException e) {

e.printStackTrace();

}

}

LOGGER.info(Thread.currentThread().getName()+ " has finished his work");

}

}/*** numTasks指并发线程数。

* -- 不用连接池:

* numTasks<=100正常运行,完成100个任务耗时大概是550ms~600ms

* numTasks>100报错“too many connections”,偶尔不报错,这是来自mysql数据库本身的限制

* -- 采用连接池

* numTasks>10000仍正常运行,完成10000个任务耗时大概是26s(池大小是10)*/

private static final int NUM_TASKS = 2000;

@Testpublic void test() throwsException {

List workers = new LinkedList<>();for(int i = 0; i != NUM_TASKS; ++i) {

workers.add(newWorker());

}

assertConcurrent("Dao test ", workers, Integer.MAX_VALUE);

}public static void assertConcurrent(final String message, final List extends Runnable> runnables, final int maxTimeoutSeconds) throwsInterruptedException {final int numThreads =runnables.size();final List exceptions = Collections.synchronizedList(new ArrayList());final ExecutorService threadPool =Executors.newFixedThreadPool(numThreads);try{final CountDownLatch allExecutorThreadsReady = newCountDownLatch(numThreads);final CountDownLatch afterInitBlocker = new CountDownLatch(1);final CountDownLatch allDone = newCountDownLatch(numThreads);for (finalRunnable submittedTestRunnable : runnables) {

threadPool.submit(newRunnable() {public voidrun() {

allExecutorThreadsReady.countDown();try{

afterInitBlocker.await();

submittedTestRunnable.run();

}catch (finalThrowable e) {

exceptions.add(e);

}finally{

allDone.countDown();

}

}

});

}//wait until all threads are ready

assertTrue("Timeout initializing threads! Perform long lasting initializations before passing runnables to assertConcurrent", allExecutorThreadsReady.await(runnables.size() * 10, TimeUnit.MILLISECONDS));//start all test runners

afterInitBlocker.countDown();

assertTrue(message+" timeout! More than" + maxTimeoutSeconds + "seconds", allDone.await(maxTimeoutSeconds, TimeUnit.SECONDS));

}finally{

threadPool.shutdownNow();

}

assertTrue(message+ "failed with exception(s)" +exceptions, exceptions.isEmpty());

}

}

本来打算调整连接池参数观察对性能影响的,结果发现即使参数不变,运行时间起伏也有点大。所以暂时先这样了。。。具体原因待探究!

hikaricp mysql_JAVA连接数据库 #03# HikariCP相关推荐

  1. HikariCP数据库连接配置详解

    一.必须配置 HikariCP的必需配置主要有3个,一般来说配置了这3个以后,其他默认设置在大多数系统中都表现良好且无须额外调整.这 3 个必需配置如下: dataSourceClassName或者j ...

  2. 在 Spring Boot 中使用 HikariCP 连接池

    上次帮小王解决了如何在 Spring Boot 中使用 JDBC 连接 MySQL 后,我就一直在等,等他问我第三个问题,比如说如何在 Spring Boot 中使用 HikariCP 连接池.但我等 ...

  3. Spring Boot 2.0选择HikariCP作为默认数据库连接池的五大理由

    转载自公众号:工匠小猪猪的技术世界 摘要: 本文非原创,是「工匠小猪猪的技术世界」搜集了一些HikariCP相关的资料整理给大家的介绍,主要讲解了为什么sb2选择了HikariCP以及HikariCP ...

  4. java 革命_JAVA数据库连接池的革命 -- 从BoneCP到HikariCP(转)

    从BoneCP到HikariCP 今天笔者本想更新一下项目中使用到的BoneCP版本的.却无意发现jolbox网站打不开了.起初以为是被墙掉了,经过一番查找,居然在BoneCP的Github站看到了如 ...

  5. Springboot 2.0选择HikariCP作为默认数据库连接池的五大理由

    转载自公众号:工匠小猪猪的技术世界 摘要: 本文非原创,是笔者搜集了一些HikariCP相关的资料整理给大家的介绍,主要讲解了为什么sb2选择了HikariCP以及HikariCP为什么这么快. Sp ...

  6. Spring Boot学习总结(18)——Springboot 2.0选择HikariCP作为默认数据库连接池的五大理由

    Springboot2默认数据库连接池选择了HikariCP为何选择HikariCP理由一.代码量理由二.口碑理由三.速度理由四.稳定性理由五.可靠性HikariCP为什么这么快优化并精简字节码更好的 ...

  7. SpringBoot 默认数据库连接池 HikariCP

    目录 引言 1.问题描述 2.SpringBoot默认的数据库连接池 3.HikariCP是什么 4.测试依赖 5.配置文件 5.1.数据库连接参数 5.2.连接池数据基本参数 5.3.连接检查参数 ...

  8. hikaricp 连接池分析_SpringBoot 2.0 中 HikariCP 数据库连接池原理解析

    作为后台服务开发,在日常工作中我们天天都在跟数据库打交道,一直在进行各种CRUD操作,都会使用到数据库连接池.按照发展历程,业界知名的数据库连接池有以下几种:c3p0.DBCP.Tomcat JDBC ...

  9. SpringBoot 2.0 中 HikariCP 数据库连接池原理解析

    作为后台服务开发,在日常工作中我们天天都在跟数据库打交道,一直在进行各种CRUD操作,都会使用到数据库连接池.按照发展历程,业界知名的数据库连接池有以下几种:c3p0.DBCP.Tomcat JDBC ...

  10. JAVA JDBC详解

    一.相关概念 什么是JDBC JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一 ...

最新文章

  1. 问题解决:org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
  2. Verilog如何避免Latch
  3. android加载so文件失败,Android无法加载'.so.1'文件
  4. 剖析Caffe源码之Layer
  5. 《如何搭建小微企业风控模型》第八节 反欺诈策略 节选
  6. 如何安装黑苹果双系统
  7. pdf如何转化成word文档?
  8. 编译bib文件,报错repeated entry
  9. 【20CSPS提高组】函数调用
  10. licecap:截屏录制gif图片工具
  11. 【Verilog数字系统设计(夏雨闻)6-------模块的结构、数据类型、变量和基本运算符号2】
  12. 区块链技术及应用发展概述
  13. 万能密码:‘or 1=1-- 实战SQL注入,秒破后台
  14. ioredis pipeline用法
  15. C语言统计数字出现次数
  16. 零基础编程——块语言编程游戏攻略之动画篇
  17. 淘宝代购系统|代购网站建设|代购系统开发代码对接教程
  18. 每天学日语:日语输入法教程及日文键盘分部图
  19. java后端开发三年,你还不了解JVM,凭什么给你涨薪
  20. 做好flash手绘基本功,简单的手绘人物头像和眨眼动画

热门文章

  1. matlab 频域采样定理,信号时域和频域采样函数周期性与原信号的关系
  2. Java、JSP基于Web的小型购书网站
  3. 今年护网蓝队防御具体实施方案
  4. 数据结构与算法分析:C语言描述(原书第2版) PDF+源代码+习题答案
  5. 树型拓扑计算机网络的缺点是,计算机网络拓扑的优缺点 -电脑资料
  6. sniffer与网络执法官,p2p终结者的简单对比
  7. ASP + SQL Server聊天室设计实例
  8. 聊天三个小时后发生的故事
  9. 思科路由器配置命令(二)
  10. U盘存储设备有回收站吗及如何快速恢复u盘数据