你真的知道如何设置数据库连接池的大小吗

前段时间在一个老项目中经历过一个问题:一个 Dubbo 服务,启动的时候慢的要死,后来看日志查原因整个过程一直在初始化数据库连接。一看数据库连接参数,连接池大小:1024。

很多入行晚的同学没有经历过手写 JDBC 连接的日子。那个时候没有数据库连接池的概念,都是原生代码一顿搞,后来有了 iBATIS 之后 Java 开发的繁杂程度才逐渐减轻,也衍生 C3P0 数据库连接池这种基础的东西。罗马不是一天建成的,可是互联网发展太快了,技术压力逼迫下各种中间件被迫研发,大家加班加点搞出来各种高大上的脚手架,也成就很多 伟人

数据库连接使用 TCP 的方式,建立连接需要3次握手,释放连接需要4次挥手,当今这种互联网使用频率下,如果每一次访问数据库都重新建立连接,我估计你们公司倒闭800次都不够。

1. 数据库连接的过程是怎样的

Java 鼻祖 Sun 公司是想以一套API统一天下,奈何各个数据库服务器厂商太给力统一不了。无奈之举是创建了一个统一的接口,提出一套统一接入的步骤,各个厂商实现接口,按照步骤加载自己的数据库。所以现在的方案就是4板斧:

  1. 注册驱动,为人所知的:Class.forName()
  2. 获取Connection,成功即与数据库建立连接;
  3. 拿到Statement对象,用于操作数据库的CRUD;
  4. 获取数据库返回结果ResultSet。

大家应该都知道数据库本身是一个客户端程序,只有启动了才能连接。拿 MYSQL 举例,我们在安装并启动了服务的机器上,命令行的方式输入:mysql -uroot -p 即可连接当前数据库。

MYSQL 连接方式有很多种,区分Unix系统 和 Windows 系统以及通用的连接方式,在这里仅说两种方式:一种为 unix domain socket,另外一种为基于 tcp/ip 协议,一般我们如果远程访问数据库肯定是基于 tcp/ip 的,但是如果我们在本机登录就会分为使用 socket 还是 tcp/ip。

socket:mysql -uroot -p
tcp/ip:mysql -h127.0.0.1 -uroot -p

当数据库服务器和应用服务器位于不同的主机时就要使用 tcp/ip 的方式建立连接。每一个连接在操作系统中占用一个线程来维护。建立连接也分为两类:短连接和长连接。

短连接

所谓短连接就是指应用程序和数据库通信完毕之后连接关闭。这种连接每次的操作就是:

发出请求--->建立连接--->操作数据--->释放连接

这样做的问题是:

  1. 频繁的建立 / 释放连接对数据库来说增加了系统负担;
  2. 应用程序每次操作数据库的过程将会变得很慢;
  3. 应用系统每次建立连接都要占用一个端口,频繁的建立/释放,每个被释放的连接在发出释放请求之后并不是马上就执行,必须经历一个 FIN 阶段的等待直到确认为止。所以在每秒几千次数据库请求的时候,应用服务器端口很有可能被消耗完。

长连接

长连接即在建立连接后一直打开,直到应用程序关闭才释放。使用长连接的好处是减少每次创建连接带来的开销。

对于应用服务器来说维持长连接的好处不言自明,但是对于数据库服务器来说,过多的长连接则是灾难。

MYSQL的TCP连接支持长连接,所以每次操作完数据库,可以不必直接关掉连接,而是等待下次使用的时候在复用这个连接。所有的Socket长连接都是通过TCP自带的ping来维持心跳(TCP保活),从而保持连接状态,而我们熟悉的websocket,也正是通过TCP的心跳来维持连接不被中断。

连接池

长连接的好处这么大,自然大家都用长连接。慢慢就搞出一套长连接维护的工具 - 数据库连接池。

设计连接池也没有多么复杂,大致的步骤就是:

  1. 初始化连接;
  2. 业务取出连接;
  3. 业务发送请求;
  4. 放回连接。

除了上面的基本功能以外,还要处理并发问题,多数据库服务器和多用户,事务处理,连接池的配置与维护。大概就这些功能。有了连接池之后,连接的建立和释放跟业务就没有关系,交给交接池来维护。

2. MYSQL 能支持多少连接

MYSQL 的最大连接数在5.7版本中默认是151, 最大可以达到16384(2^14)。如何设置最大连接数在于你的服务器性能,查看 MYSQL连接数信息命令如下:

mysql> show variables like '%max_connections%';
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| max_connections | 5050  |
+-----------------+-------+
1 row in set (0.00 sec)

我们生产环境MYSQL的最大连接数设置为 5050,注意不能设置的太小,太小造成的后果是连接失败:“query failed Error 1040: Too many connections“ 错误。太大且当连接该数据库的机器比较多的时候则会对当前MYSQL的性能产生影响。

MYSQL官网给出了一个设置最大连接数的建议比例:

Max_used_connections / max_connections * 100% ≈ 85%

即已使用的连接数占总上限的85%左右,如果目前已使用的连接数与最大连接数比例小于10%那很显然设置的过大。

查询当前数据库已建立连接数:

mysql> show status like 'Threads_connected';
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| Threads_connected | 89    |
+-------------------+-------+
1 row in set (0.00 sec)

Mysql的配置可以在全局变量中查询和设置,相关的配置主要可以查询下面这些:

配置 含义
Connections 尝试连接Mysql的连接数,不管连接成功与否,该值都会+1
Threads_connected 已经建立的连接数,单节点下一般小于最大连接池最大连接数
max_connections Mysql限制的最大的可连接的数量
wait_timeout 即MYSQL长连接(非交互式)的最大生命时长,默认是8小时
interactive_timeout 长连接(交互式)的最大生命时长,默认是8小时

3. 连接池设置多少连接才合适

设置连接池的大小肯定不是越大越好,需要考虑的是当前服务所在机器的性能,网络状况,数据库机器性能,数据库特性等等。同时也要做到不浪费系统资源,内存,端口,同步信号量等等。

比如说应用服务器Tomcat设置的最大线程池缺省值200,最大假设每个线程会用到一个数据库连接,那么线程池大小应该小于等于200。

另外需要考虑的是,每申请一个长连接都会在物理网络上建立一个用于长连接维护的进程,而进程的执行跟物理机的CPU核数有关。理论上一个8核的服务器将连接池设置为8最佳,每一个核同时处理一个线程,超过8的并发就有线程上下文切换的开销。

这里有一个 Oracle 性能小组发布的简短视频,连接池测试分2个部分:测试视频1测试视频2 。视频中调整了线程池大小为2048的时候数据库性能陡然下降,后面调整到144就恢复了。PostgreSQL提供了一个设置预期线程池大小的公式:

connections = ((core_count * 2) + effective_spindle_count)

该公式来自于:https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing

其中,core_count是CPU核心, effective_spindle_count 的含义是有效主轴数,如果你的服务器使用的是带有16个磁盘的RAID,那么valid_spindle_count=16。它实质上是服务器可以管理多少个并行I / O请求的度量。旋转硬盘一次(通常)一次只能处理一个I / O请求,如果你有16个,则系统可以同时处理16个I / O请求。

我想 Hikari 作为目前最优秀的数据库连接池之一,提出的这个公式还是经得起检验的。大家不妨在生产环境试试(出问题别找我)。

原文地址:https://www.cnblogs.com/rickiyang/p/12239907.html

时间: 2024-07-31 04:17:56

你真的知道如何设置数据库连接池的大小吗的相关文章

asp.net如何设置数据库连接池的数量

http://www.cnblogs.com/wbcms/archive/2008/10/11/1308725.html 可以使用一组名称-值对以链接字符串的形式配置链接池.例如,可以配置池是否有效(默认是有效的),池的最大.最小容量,用于打 开链接的排队请求被阻断的时间.下面的示例字符串配置了池的最大和最小容量. "Server=(local); Integrated Security=SSPI; Database=Northwind; Max Pool Size=75; Min Pool S

数据库连接池--概述以及DBCP实现

1. 为什么要引入数据库连接池 应用程序直接获取连接的方式中,用户每次请求都需要向服务器获得连接,而服务器创建连接通常需要消耗相对较大的资源,创建时间也较长.假设网站一天10万的访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库服务器的资源,并且极易造成数据库服务器内存溢出,宕机. 2.  开源的数据库连接池 数据库连接池必须实现javax.sql.DataSource接口,并提供getConnection()方法. DBCP 数据库连接池 是Apache软件基金会组织下的开源连接池的

【笔记】python的sqlalchemy数据库连接池原理的说明

sqlalchemy数据库连接池的使用方式是延迟初始化,就是说一开始你调用create_engine(...)后创建的那个数据库池是空的,你后面通过session.connection()或者engine.connect()才开始创建连接, 每当你创建一个连接,你调用engine.pool.status()就可以看到数据库连接池处于什么状态,下面说明以下status()的输出说明: 'Pool size: 16  Connections in pool: 1 Current Overflow:

JDBC 数据库连接池 小结

原文:http://www.cnblogs.com/lihuiyy/archive/2012/02/14/2351768.html 当对数据库的访问不是很频繁时,可以在每次访问数据库时建立一个连接,用完之后关闭.但是,对于一个复杂的数据库应用,频繁的建立.关闭连接,会极大的减低系统性能,造成瓶颈.所以可以使用数据库连接池来达到连接资源的共享,使得对于数据库的连接可以使高效.安全的复用. 1.通过一个小测试来了解数据库连接池的原理 (1)创建一个属性文件  dbpool.properties 1

Java数据库连接池-proxool

连接池技术的思想: 连接复用(高效.安全),避免数据库频繁建立.关闭的开销 --------------------极客学院(参考lulei) 1.配置文件 <proxool> <!-- 连接池别名 --> <alias>localmysqldb</alias> <!-- 连接数据库的驱动URL --> <driver-url><![CDATA[jdbc:mysql://127.0.0.1:3306/test?character

Python数据库连接池模块-----DBUtils使用

python的数据库连接池实现----DBUtils DBUtils 属于WebWare项目的数据库连接池实现模块,用于对数据库连接线程化,使可以安全和有效的访问数据库的模块 DBUtils实际上是一个包含两个子模块的Python包,一个用于连接DB-API 2模块,另一个用于连接典型的PyGreSQL模块. 全局的DB-API 2变量 SteadyDB.py 用于稳定数据库连接 PooledDB.py 连接池 PersistentDB.py 维持持续的数据库连接 SimplePooledDB.

数据库连接池简单介绍和 C3P0的JDBC配置

前面一节我们介绍了怎样利用jdbc连接数据库,已经实现了数据库的连接,可是在实际的项目开发中,能够发现基本上都使用了数据库连接池技术.为什么要使用数据库连接池呢?根源在于对数据库连接的低效管理 答: 普通的JDBC数据库连接,用户请求一次查询的时候就会向数据库发起一次连接.运行完后就断开连接,这种方式会消耗大量的资源和时间.数据库的连接资源并没有得到非常好的重复利用. 若是同一时候有几百人甚至几千人在线.频繁地进行数据库连接操作,这将会占用非常多的系统资源,严重的甚至会造成server的奔溃.这

基于lucene的案例开发:数据库连接池

转载请注明出处:http://blog.csdn.net/xiaojimanman/article/details/43272993 通过java程序去连接数据库时,使用的协议是TCP/IP协议,TCP/IP协议需要进行3次握手.如果每一次数据库操作都需要创建一个新的连接,都要进行3次握手,这是十分浪费资源的,程序的效率也不是很高.为了解决这个问题,我们想可不可以自己维护一些数据库连接,需要数据库操作的时候,直接使用这其中的一个连接,用完了,在还给它,这样的话就不需要每次数据库操作都创建一个新的

一个经试用效果非常不错的数据库连接池--JAVA

前言: 虽说现在许多企业级的应用服务器均自己带有数据库连接池功能,就连 Tomcat 也支持了这种功能.然而在许多时候,我们还是要使用数据库连接池,如:访问数据库的 Java 桌面应用程序等.这个数据库连接池是我根据< Inside Servlets >一书中的示例改写而成,经过试用,效果非常不错.特发布共享. 源代码 //ConnectionPool.java package com.abner.dbconnector; import java.sql.*; import java.util