论坛首页 Java企业应用论坛

使用DBCP 数据库连接池遇到的两个比较怀疑的问题

浏览 15937 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2010-09-21  
yschysn 写道
你insert数据,没有批处理吗!
1、APATCH有自带物理连接池,你不需要理会。
2、需要看看你需要的是大数据量,还是小数据量!
3、建议删除你的pool.然后使用完连接后CLOSE(APATCH自带有物理连接池)



我们的服务器是Jetty,已经内置到我们自己的API中,跟 Tomcat它们无关,我们最终提供给客户的就是一个第三方的jar,同时也含有WEB服务可以处理servlet,但不基于其它的任务WEB服务器,servlet容器 等等...

说的简单点,我们的项目就是一个能提供WEB服务的jar包,只要安装了JDK就可以使用.

0 请登录后投票
   发表时间:2010-09-21  
分离的北极熊 写道
下文引用于网络

DBCP的bug非常多,因此Hibernate3已经不再支持DBCP连接池,而推荐使用C3PO。建议你更换数据库连接池。


然后跑去Hibernate官方论坛看,果然,在Please migrate away
from DBCP看到Gavin说:
引用:
Guys, after many problems with DBCP, I have decided to remove built-in
support for DBCP from Hibernate3, and deprecate DBCP in Hibernate 2.1.
I advise everyone to migrate away from DBCP to something that actually
works, like C3P0 or Proxool.


(If you /must/ use DBCP, you can always write your own connection
provider.)


Actually, it is probably about time we remove any remaining
dependencies to Apache commons stuff, since historically they have
caused just /so/ much trouble. The only Apache things that do seem to
work very well are Ant and log4j. Even commons-logging is a PIA,
especially in Tomcat.

C3P0是Hibernate3.0默认的自带数据库连接池,DBCP是Apache开发的数据库连接池。我们对这两种连接池进行压力测试对比,发现在并发30­0个用户以下时,DBCP比C3P0平均时间快1秒左右。但在并发400个用户时,两者差不多。


速度上虽然DBCP比C3P0快些,但是有BUG:当DBCP建立的数据库连接,因为某种原因断掉后,DBCP将不会再重新创建新的连接,导致必须重新启动To­mcat才能解决问题。DBCP的BUG使我们决定采用C3P0作为数据库连接池。


插一句题外话,C3P0是采用AOP来实现Pool,而DBCP则是多态实现。
0 请登录后投票
   发表时间:2010-09-21  
elf8848 写道
引用

第2,最严重的是,我怀疑程序在启动的时候并没有像想象那样预先先分配若干个可以直接使用的连接对象.
我检查过 -> mysql -> show processlist; 
除了MySql 本地连接外, processlist中就没有其他的记录.
所以是不是几乎或者根本就没有创建已经可以连接的Connection对象呢,这个代码的写法?

要设置initialSize参数,先设为5吧 ,默认值为0, 连接池启动时创建的初始化连接数量,1.2版本后支持 。

引用

        for (int i = 0; i < 1000; i++) {  
            manager.testInsert();  
        }

在一个线程中循环插入1000条,理论上只须要使用一个Connection对象就够了。



一般是否利用通过一个Connection和线程没有关系,DBCP和C3P0都不是用ThreadLocal来做的。如果程序通过ThreadLocal或者传参来作可以使用同一个Connection,但是这里的问题是如果不使用批处理的话,MySQL驱动对于每一个Statement都是一个IO操作,因此IO消耗相对大一些。
0 请登录后投票
   发表时间:2010-09-21  
lvp 写道
jorneyR 写道
MySQL会在连接8个小时不活动后关掉连接,而且再连也没用。
DBCP连接池没有处理MySQL的这个问题,所以只要8小时后你的连接不活动,那就得重启服务器,
C3P0默认已经处理好了这个问题,会定时的测试连接,不让连接被MySQL在这种情况下杀掉。


我已经说过了,现在讨论的是DBCP和MySQL. 不是我想用C3P0就能用的.

我提出的问题可能和MySQL的优化有关,也可能和我连接池写法有关。但是不能说我用DBCP出现现在的几个问题,大家就告诉我应该用C3P0。那如果我问C3P0的问题是不是又要告诉我应该用什么。

解决问题不能回避!

另外MySQL会在连接8小时不活动后关掉连接. DBCP完全可以搞定这个问题。



这个DBCP也能关闭,时间自定义。楼主你的问题还是出现在了系统资源消耗太大。

能不能贴一下 top 信息。
0 请登录后投票
   发表时间:2010-09-21  
Mysql用的不多,不过dbcp现在的表现还好,说dbcp不行是几年前的情况,现在反倒是c3p0缺少维护了。

你这个测试代码是单线程的吧? 体现不了池的优势,其实你最多只能用一个connection。你个maxActive设成多大都没用。

你这个时间把初始化连接池的时间也算在里面了。这个还是小问题,关键是那个测试代码里每次做一次就把connection close了,你每次都close你还是要池干吗? 这不还是每次都要新建connection? connection创建非常昂贵。

另外对于dbcp,在高并发下maxIdle一定不能小于maxActive。

检测连接dbcp一样可以做。文档里有。

另外考虑batch insert。
0 请登录后投票
   发表时间:2010-09-21  
前面有多位朋友提出,让楼主使用 jdbc的批量操作,就是PreparedStatement 类上的addBatch(),executeBatch()方法。

在这里要提醒一下大家,MySql的JDBC驱动,是不支持批量操作的,就算你在代码中调用了批量操作的方法,MySql的JDBC驱动,也是安一般操作来处理的。

同样Fetch Size特性MySql的JDBC驱动也不支持。而Oracle的JDBC驱动是都支持的。

楼主使用的是Mysql数据库, 不要指望通过批处理来提高 性能了。
0 请登录后投票
   发表时间:2010-09-21  
elf8848 写道
前面有多位朋友提出,让楼主使用 jdbc的批量操作,就是PreparedStatement 类上的addBatch(),executeBatch()方法。

在这里要提醒一下大家,MySql的JDBC驱动,是不支持批量操作的,就算你在代码中调用了批量操作的方法,MySql的JDBC驱动,也是安一般操作来处理的。

同样Fetch Size特性MySql的JDBC驱动也不支持。而Oracle的JDBC驱动是都支持的。

楼主使用的是Mysql数据库, 不要指望通过批处理来提高 性能了。


请不要想当然,建议你去看一下MySQL JDBC的源代码!

MySQL JDBC驱动在发送命令是,都是传递一个数组的String类型,然后转化为一个二维byte数组。

如果是一条的Statement的话,这个String数组只有一个元素,如果是Batch的话,则有相应个元素。

最后发送IO命令。不清楚你的结论是哪里来的?
0 请登录后投票
   发表时间:2010-09-23  
mercyblitz 写道
elf8848 写道
前面有多位朋友提出,让楼主使用 jdbc的批量操作,就是PreparedStatement 类上的addBatch(),executeBatch()方法。

在这里要提醒一下大家,MySql的JDBC驱动,是不支持批量操作的,就算你在代码中调用了批量操作的方法,MySql的JDBC驱动,也是安一般操作来处理的。

同样Fetch Size特性MySql的JDBC驱动也不支持。而Oracle的JDBC驱动是都支持的。

楼主使用的是Mysql数据库, 不要指望通过批处理来提高 性能了。


请不要想当然,建议你去看一下MySQL JDBC的源代码!

MySQL JDBC驱动在发送命令是,都是传递一个数组的String类型,然后转化为一个二维byte数组。

如果是一条的Statement的话,这个String数组只有一个元素,如果是Batch的话,则有相应个元素。

最后发送IO命令。不清楚你的结论是哪里来的?



我做了测试,分别向MySql,Oracle插入10万条记录,分别使用了批量操作与一般的循环插入。
《MySql的JDBC驱动不支持批量操作》
http://www.iteye.com/topic/770032

因为作者的帖子是讨论连接池问题的,为了不跑题,不干扰作者,所以重新开一个帖子来说明批量操作这个问题
0 请登录后投票
   发表时间:2010-09-25  
谢谢大家的关注了!我重新参考了一下DBCP的Examples.
并且用其中的多个Example提供的代码进行了测试,怀疑问题还是MySQL的配置和优化问题,局限于自身环境 2G内存还是不够? 不知道是不是这个原因。

现在测试的几个Case是:
1. 10000个线程同时"并发",每个线程完成10条数据的插入操作. 这里说的并发,实际上会受到线程池的调配.
2. 10 个线程,每个线程插入10000条数据.


几次代码和MySQL的配置调整后..测试的效果:
每次插入1W的数据,数据总量20W以前每秒4-5秒,数据总量20W以后每次增加2-3秒,递增,越来越慢
每次插入1W的数据,数据总量170W以前每秒4-5秒,数据总量170W以后每次增加2-3秒,递增,越来越慢
每次插入1W的数据,数据总量270W以前每秒5-6秒,数据总量270W以后每次增加2-3秒,递增,越来越慢
每次插入1W的数据,数据总量300W以前每秒6-8秒,数据总量300W以后每次增加2-3秒,递增,越来越慢

这是改了之后的代码.
package com.apt.dbcp.test;

import java.sql.Connection;
import java.sql.SQLException;
import org.apache.commons.dbcp.ConnectionFactory;
import org.apache.commons.dbcp.DriverManagerConnectionFactory;
import org.apache.commons.dbcp.PoolableConnectionFactory;
import org.apache.commons.dbcp.PoolingDataSource;
import org.apache.commons.pool.impl.GenericObjectPool;

/**
 * Java JDBC Connection Pool Tool.
 * 
 * @author Simon Lv
 * 
 */
public class ConnectionPool {

	private static ConnectionPool pool;
	private final static String URL = "jdbc:mysql://113.12.104.53:3306/test";
	private final static String USER_NAME = "root";
	private final static String PASSWORD = "123456";

	private GenericObjectPool genericObjectPool;
	private ConnectionFactory connectionFactory;
	private PoolingDataSource dataSource;

	// Private constructor.
	private ConnectionPool() {
		initResources();
	}

	/**
	 * Get the ConnectionPool object instance.
	 * 
	 * @return A instance of ConnectionPool.
	 */
	public synchronized static ConnectionPool getPool() {
		if (pool == null) {
			pool = new ConnectionPool();
		}
		return pool;
	}

	/*
	 * Initialization of connection pool resources.
	 */
	private void initResources() {
		genericObjectPool = new GenericObjectPool(null);

		genericObjectPool.setMaxActive(300);
		genericObjectPool.setMaxIdle(14);
		genericObjectPool.setMaxWait(20000);
		genericObjectPool.setMinIdle(5);
		genericObjectPool
				.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_GROW);

		connectionFactory = new DriverManagerConnectionFactory(URL, USER_NAME,
				PASSWORD);
		new PoolableConnectionFactory(connectionFactory, genericObjectPool,
				null, null, false, true);
		dataSource = new PoolingDataSource(genericObjectPool);
	}

	public Connection getConnection() {
		Connection connection = null;
		try {
			connection = dataSource.getConnection();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return connection;
	}

	public void closeConnection(Connection connection) {
		try {
			if (connection != null) {
				//Not a real close operation,just return to the connection pool.
				connection.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	public void showConnectionPool() {
		System.out.println("Max Active:" + genericObjectPool.getMaxActive());
		System.out.println("Max Idle:" + genericObjectPool.getMaxIdle());
		System.out.println("Min Idel:" + genericObjectPool.getMinIdle());

		System.out.println("Max Wait at:" + genericObjectPool.getMaxWait());

		System.out.println("Number of Active :"
				+ genericObjectPool.getNumActive());
		System.out.println("Number of Idle :" + genericObjectPool.getNumIdle());
	}
	
	public void destroyPool(){
		genericObjectPool.clear();
	}
}


抱歉,没有写注释.

我发现存储的能力与MySQL的配置关系很大,我若干次尝试,但是在目前已有的环境下(Windows OS 2G内存),将MySQL中的my.ini 配置成如下(删除掉了不必要的注释等内容):
# MySQL Server Instance Configuration File
[client]

port=3306

[mysql]

default-character-set=latin1

[mysqld]

# 这几个都是我增加的
back_log=500

# 这几个都是我增加的
record_buffer=16M

# 这几个都是我增加的
sort_buffer=16M

#interactive_timeout=7200

# The TCP/IP Port the MySQL Server will listen on
port=3306


#Path to installation directory. All paths are usually resolved relative to this.
basedir="C:/Program Files/MySQL/MySQL Server 5.1/"

#Path to the database root
datadir="C:/Documents and Settings/All Users/Application Data/MySQL/MySQL Server 5.1/Data/"

# The default character set that will be used when a new schema or table is
# created and no character set is defined
default-character-set=latin1

# The default storage engine that will be used when create new tables when
default-storage-engine=INNODB

# Set the SQL mode to strict
sql-mode="STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"

# The maximum amount of concurrent sessions the MySQL server will
# allow. One of these connections will be reserved for a user with
# SUPER privileges to allow the administrator to login even if the
# connection limit has been reached.
# 我增加到了1024个链接
max_connections=1024

# Query cache is used to cache SELECT results and later return them
# without actual executing the same query once again. Having the query
# cache enabled may result in significant speed improvements, if your
# have a lot of identical queries and rarely changing tables. See the
# "Qcache_lowmem_prunes" status variable to check if the current value
# is high enough for your load.
# Note: In case your tables change very often or if your queries are
# textually different every time, the query cache may result in a
# slowdown instead of a performance improvement.
query_cache_size=146M

# The number of open tables for all threads. Increasing this value
# increases the number of file descriptors that mysqld requires.
# Therefore you have to make sure to set the amount of open files
# allowed to at least 4096 in the variable "open-files-limit" in
# section [mysqld_safe]
table_cache=512

# Maximum size for internal (in-memory) temporary tables. If a table
# grows larger than this value, it is automatically converted to disk
# based table This limitation is for a single table. There can be many
# of them.
tmp_table_size=27M


# How many threads we should keep in a cache for reuse. When a client
# disconnects, the client's threads are put in the cache if there aren't
# more than thread_cache_size threads from before.  This greatly reduces
# the amount of thread creations needed if you have a lot of new
# connections. (Normally this doesn't give a notable performance
# improvement if you have a good thread implementation.)
thread_cache_size=16

#*** MyISAM Specific options

# The maximum size of the temporary file MySQL is allowed to use while
# recreating the index (during REPAIR, ALTER TABLE or LOAD DATA INFILE.
# If the file-size would be bigger than this, the index will be created
# through the key cache (which is slower).
myisam_max_sort_file_size=100G

# If the temporary file used for fast index creation would be bigger
# than using the key cache by the amount specified here, then prefer the
# key cache method.  This is mainly used to force long character keys in
# large tables to use the slower key cache method to create the index.
myisam_sort_buffer_size=27M

# Size of the Key Buffer, used to cache index blocks for MyISAM tables.
# Do not set it larger than 30% of your available memory, as some memory
# is also required by the OS to cache rows. Even if you're not using
# MyISAM tables, you should still set it to 8-64M as it will also be
# used for internal temporary disk tables.
# 修改了
key_buffer_size=400M

# Size of the buffer used for doing full table scans of MyISAM tables.
# Allocated per thread, if a full scan is needed.
read_buffer_size=64K
read_rnd_buffer_size=256K

# This buffer is allocated when MySQL needs to rebuild the index in
# REPAIR, OPTIMZE, ALTER table statements as well as in LOAD DATA INFILE
# into an empty table. It is allocated per thread so be careful with
# large settings.
sort_buffer_size=256K


#*** INNODB Specific options ***


# Use this option if you have a MySQL server with InnoDB support enabled
# but you do not plan to use it. This will save memory and disk space
# and speed up some things.
#skip-innodb

# Additional memory pool that is used by InnoDB to store metadata
# information.  If InnoDB requires more memory for this purpose it will
# start to allocate it from the OS.  As this is fast enough on most
# recent operating systems, you normally do not need to change this
# value. SHOW INNODB STATUS will display the current amount used.
innodb_additional_mem_pool_size=9M

# If set to 1, InnoDB will flush (fsync) the transaction logs to the
# disk at each commit, which offers full ACID behavior. If you are
# willing to compromise this safety, and you are running small
# transactions, you may set this to 0 or 2 to reduce disk I/O to the
# logs. Value 0 means that the log is only written to the log file and
# the log file flushed to disk approximately once per second. Value 2
# means the log is written to the log file at each commit, but the log
# file is only flushed to disk approximately once per second.
# 修改成0或者2 速度提高很快 如果是1的话 很慢 ,上面的英文解释很长,网上有很多解释
innodb_flush_log_at_trx_commit=0

# The size of the buffer InnoDB uses for buffering log data. As soon as
# it is full, InnoDB will have to flush it to disk. As it is flushed
# once per second anyway, it does not make sense to have it very large
# (even with long transactions).
innodb_log_buffer_size=5M

# InnoDB, unlike MyISAM, uses a buffer pool to cache both indexes and
# row data. The bigger you set this the less disk I/O is needed to
# access data in tables. On a dedicated database server you may set this
# parameter up to 80% of the machine physical memory size. Do not set it
# too large, though, because competition of the physical memory may
# cause paging in the operating system.  Note that on 32bit systems you
# might be limited to 2-3.5G of user level memory per process, so do not
# set it too high.
# 修改到800M
innodb_buffer_pool_size=800M

# Size of each log file in a log group. You should set the combined size
# of log files to about 25%-100% of your buffer pool size to avoid
# unneeded buffer pool flush activity on log file overwrite. However,
# note that a larger logfile size will increase the time needed for the
# recovery process.
# 按道理应该是 innodb_buffer_pool_size 的25%~100% ,但测试下来不能增加了,想必是我主机内存就2G的问题
innodb_log_file_size=87M

# Number of threads allowed inside the InnoDB kernel. The optimal value
# depends highly on the application, hardware as well as the OS
# scheduler properties. A too high value may lead to thread thrashing.
innodb_thread_concurrency=8


可能还有其它修改 但是记不住了 就直接贴出来.

以下是相关的代码
package com.apt.dbcp.test;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.UUID;
import com.apt.dbcp.util.Utils;

//Test
public class ConnectionPoolTest {

	public synchronized void testInsert() {
		ConnectionPool connectionPool = ConnectionPool.getPool();
		try {
			Connection connection = connectionPool.getConnection();
			if (connection != null) {
				PreparedStatement statement = connection
						.prepareStatement(DBConst.SESSION_INSERT);
				statement.setString(1, UUID.randomUUID().toString());
				statement.setString(2, UUID.randomUUID().toString());
				statement.setString(3, "172.17.20.3");
				statement.setInt(4, 200);
				statement.setString(5, Utils.getYHM());
				statement.setString(6, Utils.getYHM());
				statement.setInt(7, 2);

				statement.executeUpdate();
			}
			connectionPool.closeConnection(connection);
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	public void loopInsert(int times) {
		long start = System.currentTimeMillis();
		for (int i = 0; i < times; i++) {
			testInsert();
		}
		long end = System.currentTimeMillis();

		System.out.println("Take time (MS) - " + (end - start));
		System.out.println("Take time (S) - " + (end - start) / 1000);

		System.out.println("End ..");
	}
}


最终使用线程池启动插入操作
package com.apt.dbcp.test;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadGroupTest {
	private static int corePoolSize = 50;
	private static int maxPoolSize = 100;
	private static long keepAliveTime = 1;

	private static TimeUnit unit = TimeUnit.MILLISECONDS;
	private static BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<Runnable>(
			100);
	
	private static int times=3000 ;
	
	private static long produceTaskSleepTime=0;

	public static void main(String[] args) {
		ThreadPoolExecutor threadPool = new ThreadPoolExecutor(corePoolSize,
				maxPoolSize, keepAliveTime, unit, workQueue,
				new ThreadPoolExecutor.CallerRunsPolicy());
		
		for (int i = 1; i <= times; i++) {
			try {
				threadPool.execute(new Runnable() {
					ConnectionPoolTest test = new ConnectionPoolTest();
					@Override
					public void run() {
						 test.loopInsert(100);
					}
				});
				Thread.sleep(produceTaskSleepTime);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}
0 请登录后投票
   发表时间:2010-09-25  
贴一份简单的LOG
----The 1 times,total counts > 0W----
Start..
Take time (MS) - 7438
Take time (S) - 7
End ..

----The 2 times,total counts > 1W----
Start..
Take time (MS) - 4703
Take time (S) - 4
End ..

----The 3 times,total counts > 2W----
Start..
Take time (MS) - 5093
Take time (S) - 5
End ..

//省去中间的100多次记录,看170W数据总量时的情况


----The 170 times,total counts > 169W----
Start..
Take time (MS) - 5937
Take time (S) - 5
End ..

----The 171 times,total counts > 170W----
Start..
Take time (MS) - 5875
Take time (S) - 5
End ..

----The 172 times,total counts > 171W----
Start..
Take time (MS) - 6390
Take time (S) - 6
End ..

----The 173 times,total counts > 172W----
Start..
Take time (MS) - 5625
Take time (S) - 5
End ..

//省去中间的100多次记录,看270W 左右 数据总量时的情况

----The 277 times,total counts > 276W----
Start..
Take time (MS) - 7203
Take time (S) - 7
End ..

----The 278 times,total counts > 277W----
Start..
Take time (MS) - 7968
Take time (S) - 7
End ..


//省去中间的30多次记录,看300W 左右 数据总量时的情况,会逐步递增..

----The 302 times,total counts > 301W----
Start..
Take time (MS) - 7468
Take time (S) - 7
End ..

----The 303 times,total counts > 302W----
Start..
Take time (MS) - 8125
Take time (S) - 8
End ..

//明显的逐步递增..

----The 313 times,total counts > 312W----
Start..
Take time (MS) - 10625
Take time (S) - 10
End ..

----The 314 times,total counts > 313W----
Start..
Take time (MS) - 11718
Take time (S) - 11
End ..

----The 315 times,total counts > 314W----
Start..
Take time (MS) - 13703
Take time (S) - 13
End ..


//最后 有点动不了了 1W 的数据 要几十秒
----The 338 times,total counts > 337W----
Start..
Take time (MS) - 24812
Take time (S) - 24
End ..

----The 339 times,total counts > 338W----
Start..
Take time (MS) - 26609
Take time (S) - 26
End ..

----The 340 times,total counts > 339W----
Start..
Take time (MS) - 36983
Take time (S) - 36
End ..

----The 341 times,total counts > 340W----
Start..
Take time (MS) - 42811
Take time (S) - 42
End ..

----The 342 times,total counts > 341W----
Start..
Take time (MS) - 54405
Take time (S) - 54
End ..

----The 343 times,total counts > 342W----
Start..


是因为MySQL 单表总量达到300多W就速度下降了吗? 我想求证这个问题.

连接池应该是没有问题了. 因为我单独测试的时候. MySQL showprocesslist.命令中能看到每次线程的状态. 比如我初始化30个已经连接的线程. 在showporcesslist命令下,能够看到这30个线程的状态,且如果所有任务都完成,这30个已经连接的Connection 状态是sleep. 说明了确实连接池中有连接对象与MySQL已经连接好了.用完了就 “休眠”.

现在想验证的问题就是
1.是不是2G的内存和我现在的配置只能做到这个程度.
2.MySQL 单表总量达到300多W就速度下降了吗?
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics