数据库连接池

# 数据库连接池

### 什么是连接池

数据库连接池负责<font color='red'>分配、管理和释放数据库连接</font>,它允许应用程序<font color='red'>重复使用</font>一个现有的数据库连接,<font color='orange'>而不是再重新建立一个</font>

### 为什么要用连接池

一个数据库连接对象均对应一个物理数据库连接,每次操作都打开一个物理连接,使用完都关闭连接,<font color='cornflowerblue'>这样造成系统的性能低下</font>。

![img](池.assets/1606768-20190616155729169-544894466.png)

数据库连接池的解决方案是在应用程序启动时建立足够的数据库连接,并讲这些连接组成一个连接池,由应用程序动态地对池中的连接进行申请、使用和释放。

<font color='red'>连接池技术尽可能多地重用了消耗内存地资源,大大节省了内存,提高了服务器地服务效率,能够支持更多的客户服务。</font>通过使用连接池,将<font color='orange'>大大提高程序运行效率</font>,同时,我们可以通过其自身的管理机制来监视数据库连接的数量、使用情况等。

### 数据库连接池接口

Java 为数据库连接池提供了公共接口:<font color='red'>javax.sql.DataSource</font>,各大厂商可以让自己的连接池实现该接口,而应用程序可以方便的切换不同的连接池。

### 数据库连接池的优点

- <font color='red'>资源重用</font>

  由于数据库连接得到重用,避免了频繁创建、释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增进了系统运行环境的平稳性(减少内存碎片以及数据库临时进程/线程的数量)。

- <font color='red'>更快的系统响应速度</font>

  数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而缩减了系统整体响应时间。

- <font color='red'>新的资源分配手段</font>

  对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接的配置,实现数据库连接池技术,几年钱也许还是个新鲜话题,对于目前的业务系统而言,如果设计中还没有考虑到连接池的应用。某一应用最大可用数据库连接数的限制,避免某一应用独占所有数据库资源。

- <font color='red'>统一的连接管理,避免数据库连接泄漏</font>

  在较为完备的数据库连接池实现中,可根据预先的连接占用超时设定,强制收回被占用连接。从而避免了常规数据库连接操作中可能出现的资源泄漏。

### 数据库连接池的工作原理

分位3步:

- 连接池的建立

- 连接池中连接的使用管理

- 连接池的关闭

<font color='red'>连接池的建立。</font>一般在系统初始化时,连接池会根据系统配置建立,并在池中创建了几个连接对象,以便使用时能从连接池中获取。<font color='cornflowerblue'>连接池中的连接不能随意创建和关闭</font>,这样避免了连接随意建立和关闭造成的系统开销。

<font color='red'>连接池的管理。</font>连接池管理策略是连接池机制的核心,连接池内连接的分配和释放对系统的性能有很大的影响。其管理策略是:

```oop

当客户请求数据库连接时,首先查看连接池中是否有空闲连接,如果存在空闲连接,则将连接分配给客户使用;如果没有空闲连接,则查看当前所开的连接数是否已经达到最大连接数,如果没达到就重新创建一个连接给请求的客户;如果达到就按设定的最大等待时间进行等待,如果超出最大等待时间,则抛出异常给客户。

当客户释放数据库连接时,先判断该连接的引用次数是否超过了规定值,如果超过就从连接池中删除该连接,否则保留为其他客户服务。

该策略保证了数据库连接的有效复用,避免频繁的建立、释放连接所带来的系统资源开销。

```

<font color='red'>连接池的关闭。</font>当应用程序退出时,关闭连接池中所有的连接,释放连接池相关的资源,以便连接可以返回池中重复利用。我们可以通过<font color='orange'>Connection</font>对象的<font color='orange'>Close</font>或<font color='orange'>Dispose</font>方法

## 常用的连接池

| 数据库连接池 | 最新版本    | 发布时间 |

| ------------ | ------------ | -------- |

| c3p0        | c3p0-0.9.5.2 | 2015    |

| dbcp        | 2.2.0        | 2017    |

| **druid**    | 0.11.0      | 2017    |

| **HikariCP** | 2.7.6        | 2018    |

#### C3P0

<font color='red'>C3P0</font>在很长一段时间内,它一直是Java领域内数据库连接池的代名词,当年盛极一时的<font color='red'>Hibernate</font>都将其作为内置的数据库连接池,可以业内对它的稳定性还是认可的。<font color='orange'>C3P0功能简单易用,稳定性好这是它的优点,但是性能上的缺点却让它彻底被打入冷宫</font>。<font color='red'>C3P0的性能很差</font>,差到即便是同时代的产品相比它也是垫底的

#### DBCP

属于Apache顶级项目Commons中的核心子项目(最早在Jakarta Commons里就有),在Apache的生态圈中的影响里十分广泛,比如最为大家所熟知的Tomcat就在内部集成了DBCP,实现JPA规范的<font color='orange'>OpenJPA</font>,也是默认集成DBCP的。但DBCP并不是独立实现连接池功能的,它内部依赖于Commons中的另一个子项目Pool,连接池最核心的“池”,就是由Pool组件提供的,因为核心功能依赖于Pool,所以DBCP本身只能做小版本的更新,真正大版本的更迭则完全依托于pool。

#### 性能无敌的<font color='red'>HikariCP</font>

- 字节码精简:优化代码,直到编译后的字节码最少,这样,CPU缓存可以加载更多的程序代码;

- 优化代理和拦截器:减少代码,例如<font color='orange'>HikariCP</font>的Statement proxy只有100行代码;

- 自定义数组类型(<font color='orange'>FastStatementList</font>)代替<font color='orange'>ArrayList</font>:避免每次get()调用都要进行range check,避免调用remove()时的从头到尾的扫描;

- 自定义集合类型(<font color='orange'>ConcurrentBag</font>):提高并发读写的效率;

- 其他缺陷的优化,比如对于耗时超过一个CPU时间片的方法调用的研究(但没说具体怎么优化)。

java代码

```Java

@Test

    //Hikari

    public void test3() throws Exception{

        Properties properties = new Properties();

        properties.load(PoolTest.class.getClassLoader().getResourceAsStream("hikari.properties"));


        HikariConfig hikariConfig = new HikariConfig(properties);

        DataSource dataSource = new HikariDataSource(hikariConfig);

        System.out.println(dataSource.getConnection());

    }

```

properties配置文件

```properties

jdbcUrl=jdbc:mysql://127.0.0.1:3306/job?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai

username=root

password=root

driverClassName=com.mysql.cj.jdbc.Driver

```

#### 功能全面的<font color='red'>Druid</font>(德鲁伊)

Druid 相对于其他数据库连接池的优点

- 强大的监控特性,通过Druid提供的监控功能,可以清楚知道连接池和SQL的工作情况。

- 监控SQL的执行时间、<font color='orange'>ResultSet</font>持有时间、返回行数、更新行数、错误次数、错误堆栈信息;

-  SQL执行的耗时区间分布。什么是耗时区间分布呢?比如说,某个SQL执行了1000次,其中0\~1毫秒区间50次,1\~10毫秒800次,10\~100毫秒100次,100\~1000毫秒30次,1~10秒15次,10秒以上5次。通过耗时区间分布,能够非常清楚知道SQL的执行耗时情况;

-  监控连接池的物理连接创建和销毁次数、逻辑连接的申请和关闭次数、非空等待次数、<font color='orange'>PSCache</font>命中率等。

- 方便扩展。Druid提供了Filter-Chain模式的扩展API,可以自己编写Filter拦截JDBC中的任何方法,可以在上面做任何事情,比如说性能监控、SQL审计、用户名密码加密、日志等等。

<font color='red'>Druid</font>集合了开源和商业数据库连接池的优秀特性,并结合<font color='red'>阿里巴巴</font>大规模苛刻生产环境的使用经验进行优化。

| 配置                          | 缺省值            | 说明                                                        |

| ----------------------------- | ------------------ | ------------------------------------------------------------ |

| name                          |                    | 配置这个属性的意义在于,如果存在多个数据源,监控的时候  可以通过名字来区分开来。如果没有配置,将会生成一个名字,  格式是:"DataSource-" + System.identityHashCode(this) |

| jdbcUrl                      |                    | 连接数据库的url,不同数据库不一样。例如:  mysql : jdbc:mysql://10.20.153.104:3306/druid2  oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto |

| username                      |                    | 连接数据库的用户名                                          |

| password                      |                    | 连接数据库的密码。如果你不希望密码直接写在配置文件中,  可以使用ConfigFilter。详细看这里:  [https://github.com/alibaba/druid/wiki/%E4%BD%BF%E7%94%A8ConfigFilter](https://github.com/alibaba/druid/wiki/使用ConfigFilter) |

| driverClassName              | 根据url自动识别    | 这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName |

| initialSize                  | 0                  | 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时 |

| maxActive                    | 8                  | 最大连接池数量                                              |

| maxIdle                      | 8                  | 已经不再使用,配置了也没效果                                |

| minIdle                      |                    | 最小连接池数量                                              |

| maxWait                      |                    | 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,  缺省启用公平锁,并发效率会有所下降,  如果需要可以通过配置useUnfairLock属性为true使用非公平锁。 |

| poolPreparedStatements        | false              | 是否缓存preparedStatement,也就是PSCache。  PSCache对支持游标的数据库性能提升巨大,比如说oracle。  在mysql5.5以下的版本中没有PSCache功能,建议关闭掉。 作者在5.5版本中使用PSCache,通过监控界面发现PSCache有缓存命中率记录,  该应该是支持PSCache。 |

| maxOpenPreparedStatements    | -1                | 要启用PSCache,必须配置大于0,当大于0时,  poolPreparedStatements自动触发修改为true。  在Druid中,不会存在Oracle下PSCache占用内存过多的问题,  可以把这个数值配置大一些,比如说100 |

| validationQuery              |                    | 用来检测连接是否有效的sql,要求是一个查询语句。  如果validationQuery为null,testOnBorrow、testOnReturn、  testWhileIdle都不会其作用。 |

| testOnBorrow                  | true              | 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 |

| testOnReturn                  | false              | 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能 |

| testWhileIdle                | false              | 建议配置为true,不影响性能,并且保证安全性。  申请连接的时候检测,如果空闲时间大于  timeBetweenEvictionRunsMillis,  执行validationQuery检测连接是否有效。 |

| timeBetweenEvictionRunsMillis |                    | 有两个含义:  1) Destroy线程会检测连接的间隔时间  2) testWhileIdle的判断依据,详细看testWhileIdle属性的说明 |

| numTestsPerEvictionRun        |                    | 不再使用,一个DruidDataSource只支持一个EvictionRun          |

| minEvictableIdleTimeMillis    |                    |                                                              |

| connectionInitSqls            |                    | 物理连接初始化的时候执行的sql                                |

| exceptionSorter              | 根据dbType自动识别 | 当数据库抛出一些不可恢复的异常时,抛弃连接                  |

| filters                      |                    | 属性类型是字符串,通过别名的方式配置扩展插件,  常用的插件有:  监控统计用的filter:stat  日志用的filter:log4j  防御sql注入的filter:wall |

| proxyFilters                  |                    | 类型是List<com.alibaba.druid.filter.Filter>,  如果同时配置了filters和proxyFilters,  是组合关系,并非替换关系 |

Druid java代码

```Java

public void test1() throws Exception{

        Properties properties = new Properties();

        properties.load(PoolTest.class.getClassLoader().getResourceAsStream("jdbc.properties"));

        DruidDataSource druidDataSource = new DruidDataSource();

        druidDataSource.configFromPropety(properties);

        Connection connection = druidDataSource.getConnection();

        System.out.println(connection);

    }

```

Druid properties配置文件

```properties

druid.url=jdbc:mysql://127.0.0.1:3306/job?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai

druid.username=root

druid.password=root

druid.driverClassName=com.mysql.cj.jdbc.Driver

```

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342