1 高并发异常
目前应用的 Proxool 连接池,在高并发情况下会出现:
1. DataSource.getConnection() 方法会根据 ProxoolDataSource.simultaneousBuildThrottle 限制连接创建的并发,超出限制将抛出 “java.sql.SQLException: We are already in the process of making 11 connections and the number of simultaneous builds has been throttled to 10 ” 异常;
2. 当满足条件 “connectionCount >= getDefinition().getMaximumConnectionCount() && connectionPool.getAvailableConnectionCount() < 1” 时, DataSource.getConnection() 方法将抛出 “java.sql.SQLException: Couldn't get connection because we are at maximum connection count (200/200) and there are none available ” 异常;
3. 当满足条件 “ connectionCount >= getDefinition().getMaximumConnectionCount() ” 时 , DataSource.getConnection() 方法将抛出 “java.sql.SQLException: org.logicalcobwebs.proxool.ProxoolException: ConnectionCount is 200. Maximum connection count of 200 cannot be exceeded. ” 异常。
第一种异常可以修改配置文件,增加 simultaneousBuildThrottle 避免,但后两种情况只能增加数据库连接。并发量大于数据库连接数,必然会报后两种异常。可见 Proxool 并未具有完整的连接池实现,其尚未具有处理高并发应用的能力。
2 多连接数
为减少以上情况的发生,必须设置 Proxool 数据库连接数大于等于业务线程数
当业务线程数为 300 时,数据库连接池的最大连接数必须大于三百。这里说 “ 大于 ” ,意义是即使业务线程数为 300 、数据库连接数为 300 ,也不能保证不报后两种异常。
维持过多的连接对于数据可游戏服务器都有一定的资源浪费,而且有碍新的服务器分布式、集群的实现。
二 替代方案
经过网上搜索,找到其他连接池实现: DBCP 、 Tomcat JDBC Pool 、 DBPool 、 C3P0 、 BoneCP 、 Db Connection Broker 、 MiniConnectionPoolManager 。
DBCP 为 Tomcat6 默认连接池,但 1.2 版本倍受缓慢诟病,最新版本为 1.4 。 Tomcat JDBCPool 是 Tomcat 在 Tomcat7 中保留 DBCP 连接池以兼容使用 DBCP 连接池的已有应用,并提供新的 Tomcat JDBC Pool 作为 DBCP 的可选替代,修改一行配置文件即可启用 [1] 。 DBPool 为 ObjectWeb 开源产品,但多年未维护。 C3P0 为 Hibernate 默认连接池。 BoneCP 为新兴产品,网上评价极高。
Db Connection Broker 、 MiniConnectionPoolManager 是两个轻量级连接池,实现只有 1 个类。
1 API
其中 DBCP 、 C3P0 已被广泛应用,可以认为稳定可靠。
Db Connection Broker 、 MiniConnectionPoolManager 未提供 javax.sql.DataSource 接口实现,不予考虑。
经过对 API 和部分代码的研究,除 Proxool 以外所有连接池均有无可分配链接时让线程等待的功能,并提供超时参数或等价替代方案,超时未取得连接则抛出异常。
2 性能
为测试连接池性能,撰写测试程序,多线程无延迟持续查询“ select 1 ”,分别测试单位时间内查询次数。
1 在双核、 3G 内存,最大连接 5 ,发连接改为 4 , BoneCP 分 2 区,不计算预热阶段,持续时间 1 分钟:
Proxool 虽然在性能上远超 DBPool ,但第一次测试报 3 次异常,第三次测试报 2 次异常,均为第三种异常“ ProxoolException: ConnectionCount is 5. Maximum connection count of 5 cannot be exceeded. ”,线程数小于连接数竟然还报错,不可容忍; C3P0 明显领先; DBCP 紧随其后,可见两大著名连接池果然给力;新星 BoneCP 一度超越 DBCP ,平均成绩名列第三; TomcatPool 排在第四,但性能稳定。
2 在双核、 3G 内存,最大连接 5 ,并发线程 500 , BoneCP 分 2 区,持续三分钟,不计算预热阶段,测试三次,测试结果如下:
TomcatPool 在高并发低连接数情况下,性能稍强于 DBCP ,但不明显; BoneCP 被 Tomcat 和 DBCP 甩在后面; C3P0 表现一般; DBPool 稳稳淘汰。
3 在 4 核 cpu 、 1000 线程、 200 连接、 BoneCP 分 100 区,连续 15 分钟,不计算预热阶段,测试次数结果如下( DBPool 在多次测试结果中远远落后于其他,为节约时间这次未参与测试):
可见 DBCP 在高并发、高连接数情况下,查询量远远超过位于第三代 C3P0 并甩开后两名十万次左右; BoneCP 和 TomcatPool 性能接近; C3P0 远远落后前三者。
高并发时 visualvm 线程状态记录如下:
从线程状态记录中可见, DBCP 和 TomcatPool 可以快速的交付连接,在途中形成琐碎的绿色条带, DBCP 图中有 3 处可忽略的红色条带,而 TomcatPool 在线程启动时被锁一段时间。 C3P0 大量使用同步锁,产生了大量的红色条带。QueryThread线程为 BoneCP查询线程, 没有使用同步锁。 DBPool 基本上全部使用同步机制,导致大量线程等待。
由于没有对源代码深入分析,尚不知 BoneCP 为何落后 TomcatPool 和 DBCP 。
综上所述,新版 DBCP 连接池仍然是一款高性能的杰作。
以上列出仅为典型测试数据,本次测试实际耗时将近一周,大量数据未能展示。
在一些记录中显示,项目工程中包含的 DBCP 1.2 的性能确实不如人意,仅仅超过 DBPool 这款同样古老的同胞,但新版 DBCP 1.4+commons pool 1.6 的绝佳配合将 DBCP 的性能推进到一个新的高峰。下图为 DBCP 1.2 的线程状态:
BoneCP 这一新兴在速度上给人相当大的惊喜,但其内存回收依赖于独立线程,默认情况下 BoneCP 会启动很多附加线程,其中大部分线程将保留到程序结束。利用其它线程回收未关闭的 connection 、 statement 、 result 带来的结果是,如果未关闭 statement 或 result ,而只关闭 connection ,那么 statement 和 result 将延迟回收,在如此高并发、高频率的测试中这样的延迟回收将导致一分钟之内将 2G 内存装满、程序崩溃。
下图为部分附加线程,实际情况要好长的列表,截图不下来了。。
下图为业务线程结束,并且已经对数据源 close 操作之后,仍然有部分线程持续运行。
下图右上角图像显示 proxool 的内存占用大于其他,而右下角图像显示 BoneCP 线程数远远大于其他。
[1] http://tomcat.apache.org/tomcat-7.0-doc/jdbc-pool.html