1. 池化技术
池化技术 (Pool) 是一种很常见的编程技巧,
在请求量大时能明显优化应用性能,降低系统频繁建连的资源开销
日常工作中常见的有数据库连接池、线程池、携程池,对象池
它们的特点都是将 “昂贵的”、“费时的” 的资源维护在一个特定的 “池子” 中,
规定其最小连接数、最大连接数、阻塞队列等配置,
方便进行统一管理和复用,
通常还会附带一些探活机制、强制回收、监控一类的配套功能。
2. 数据库连接池
数据库连接池负责分配、管理和释放数据库连接,
它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;
并且释放空闲时间超过最大空闲时间的数据库连接
来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高数据库操作的性能。
2.1 数据库连接池
数据库连接池技术带来的优势:
-
资源重用, 减少内存碎片
由于数据库连接得到重用,避免了频繁创建、释放连接引起的大量性能开销。
在减少系统消耗的基础上,
另一方面也增进了系统运行环境的平稳性,减少了内存碎片以及数据库临时进程/线程的数量 -
因为连接已提前准备好, 所以有更快的服务响应速度
数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于池中备用。
此时连接的初始化工作均已完成。
对于业务请求处理而言,直接利用现有可用连接,
避免了数据库连接初始化和释放过程的时间开销,从而缩减了系统整体响应时间。 -
新的资源分配手段,强制收回被占用连接, 从而避免某一应用独占所有数据库资源
对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接的配置,实现数据库连接池技术。
通过对应用最大可用数据库连接数的限制,避免某一应用独占所有数据库资源。
统一的连接管理,避免数据库连接泄漏
在较为完备的数据库连接池实现中,可根据预先的连接占用超时设定,
强制收回被占用连接,从而避免了常规数据库连接操作中可能出现的资源泄漏。
2.2 数据库连接池的参数设计
sql.DB 通过以下方法来配置maxOpen, maxIdle 和 maxLifeTime。
SetMaxOpenConns()
SetMaxIdleConns()
SetConnMaxLifetime()
- 最大连接数maxOpen
最大连接数一般是((核心数 * 2) + 有效磁盘数) - 最大空闲连接数maxIdle
默认情况下sql.DB,最多允许2个空闲连接保留在连接池中 - 当前总连接数
当前总连接数 = 当前空闲连接数 + 当前工作连接数 - 连接的最长时间maxLifeTime
超过这个时间, 连接被清理。
2.3 获取连接,释放连接,清理连接
2.3.1 获取连接:
分两种情况:
- 工作连接数 < 最大连接数:表示用户有机会获取连接
- 当前连接数 >= 最大连接数:Connection 的创建达到了上限,用户只能等待重试工作连接数
在有机会获取连接条件下,又分为两种情况:
- 如果空闲连接池的 size>0,直接从空闲连接池中获取连接
即空闲连接池中的弹出一个 Connection,把这个Connection 加入到工作连接池。 - 如果空闲连接池的 size=0,创建一个新的 Connection 给用户
2.3.2 释放连接:
任何一个连接使用完毕之后需要归还给连接池,这也是数据库连接池实现中比较重要的逻辑,
通常还伴随着对连接的可靠性检测,
如果连接异常关闭,那么不应该继续还给连接池,而是应该新建一个连接进行替换。
如果空闲连接池未满,直接添加进去,并把工作连接池中相应的连接移除;
如果空闲连接池满了,直接close()掉,并把工作连接池中相应的连接移除。
2.3.3 清理连接:
一个数据库连接无法保证长期有效,
例如,MySQL 侧会强制 kill 掉长时间空闲的连接(8h)。
在 sql.DB 中提供了 maxLifeTime 选项设置连接被复用的最大时间,
注意这个时间并不是连接空闲时间,
而是从连接建立到这个时间点就会被回收,从而保证连接活性。
3. 最大连接数并不是设置的越大越好的原因
- cpu原因:
单核CPU的计算机也能“同时”运行数百个线程,但这只不过是操作系统用时间分片玩的一个小把戏。
一颗CPU核心同一时刻只能执行一个线程,然后操作系统切换上下文,核心开始执行另一个线程的代码,
以此类推。给定一颗CPU核心,其顺序执行A和B永远比通过时间分片“同时”执行A和B要快,
一旦线程的数量超过了CPU核心的数量,再增加线程数系统就只会更慢,而不是更快。
所以仅从CPU的角度来考虑,设置线程数等于CPU核数,能提供最优的性能。 - 磁盘, 网络原因
磁盘,网络等因素。在I/O等待时间内,线程是在“阻塞”着等待磁盘,
此时操作系统可以将那个空闲的CPU核心用于服务其他线程,此时就产生了线程的切换,
所以,由于线程总是在I/O上阻塞,我们可以让线程/连接数比CPU核心多一些,
这样能够在同样的时间内完成更多的工作。
最终合理的连接数:
最大连接数 = ((核心数 * 2) + 有效磁盘数)