论坛首页 Java企业应用论坛

PreparedStatement真的比Statement快吗?

浏览 43485 次
精华帖 (2) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (1)
作者 正文
   发表时间:2007-01-22  
从开始写java程序就一直被灌输着一种思想,如果使用jdbc一定要使用PreparedStatement,而不要使用Statement对象。
其中的原因有好多,比如可以防止SQL注入攻击,防止数据库缓冲池溢出,代码的可读性,可维护性。这些都很正确。
但是还有一点人们经常提的就是PreparedStatement能够显著的提高执行效率。
看了两篇关于PreparedStatement和Statement的帖子
http://dev2dev.bea.com.cn/bbs/thread.jspa?forumID=121&threadID=10397&start=0&tstart=0
http://www.iteye.com/topic/5631?page=1
里面提到,PreparedStatement并不一定比Statement快,于是想自己动手测试一下
一共分5种情况测试
stmt	  pre-stmt	
--1:每次都单独连数据库--100条
21.936	22.076
22.05	  22.051	
22.172	22.178	
--2:Stmt使用相同的SQL语句,参数值也相同--100条
0.78	  0.796	
0.718	  0.749	
0.749	  0.764	
--3:Stmt使用相同的SQL语句,参数值不同--100条
0.967	  0.749	
0.78	  0.874	
0.749	  0.749	
--4:Stmt使用相同的SQL语句,参数值也相同--10000条
33.079	36.154	
34.156	39.306	
34.359	36.138	
--5:Stmt使用相同的SQL语句,参数值不同--10000条
32.799	34.125	
32.564	35.53	
35.701	34.952	
32.582	40.798	
38.893	42.345	
35.082	41.736	

分析:
第一种情况:
由于我测试时采用jdbc直接链接数据库,所以在第一种情况下插入速度很慢,时间都消耗在了数据库链接的创建上了,符合常理,没什么疑义。
第二种情况:
由于使用Statement插入时sql语句都相同,我通过阅读上面的两篇帖子,猜想可能是Statement语句也被DB缓存了,所以跟
PreparedStatement速度差不多,我想如果每次插入的sql都不同,那么Statement应该慢下来了吧。
第三种情况:
并非像我在执行第二种情况时想象的那样,PreparedStatement还是跟Statement差不多,于是我想是不是数据量不够大呀,于是我增大了
插入数据量,希望PreparedStatement能够比Statement快些
第四/五种情况:
测试结果PreparedStatement反到更慢了。

PreparedStatement什么情况下才会比Statement快?那位大侠来指点一下!

代码如下:
DDL
CREATE TABLE dbo.jdbc_stmt 
(
    id   numeric(10,0) IDENTITY,
    name varchar(10)   NOT NULL,
    grad int           NOT NULL,
    age  int           NOT NULL,
    dept int           NOT NULL
)

测试程序
public void jdbcStmtPerformance()
    {
    	//1:每次都单独连数据库--100条
//    	String stmtSQL = "insert into jdbc_stmt values('ivanl',2,25,1001)";
//    	for(int i = 0; i< 100; i++)
//    	{
//    		logger.debug("stmt #"+i);
//    		getJdbcTemplate().update(stmtSQL);
//    	}
    	
    	//2:Stmt使用相同的SQL语句,参数值也相同--100条
//    	getJdbcTemplate().execute(new StatementCallback(){
//    		public Object doInStatement(Statement stmt) throws SQLException
//    		{
//    			String stmtSQL = "insert into jdbc_stmt values('ivanl',2,25,1001)";
//    	    	for(int i = 0; i< 100; i++)
//    	    	{
//    	    		logger.debug("stmt #"+i);
//    	    		stmt.executeUpdate(stmtSQL);
//    	    	}
//    			return null;
//    		}
//    	});
    	
    	//3:Stmt使用相同的SQL语句,参数值不同--100条
//    	getJdbcTemplate().execute(new StatementCallback(){
//    		public Object doInStatement(Statement stmt) throws SQLException
//    		{
//    			String stmtSQL = "insert into jdbc_stmt values('ivanl',2,25,";
//    	    	for(int i = 0; i< 100; i++)
//    	    	{
//    	    		logger.debug("stmt #"+i);
//    	    		stmt.executeUpdate(stmtSQL+i+")");
//    	    	}
//    			return null;
//    		}
//    	});
    	
//    	4:Stmt使用相同的SQL语句,参数值也相同--10000条
//    	getJdbcTemplate().execute(new StatementCallback(){
//    		public Object doInStatement(Statement stmt) throws SQLException
//    		{
//    			String stmtSQL = "insert into jdbc_stmt values('ivanl',2,25,1001)";
//    	    	for(int i = 0; i< 10000; i++)
//    	    	{
//    	    		logger.debug("stmt #"+i);
//    	    		stmt.executeUpdate(stmtSQL);
//    	    	}
//    			return null;
//    		}
//    	});
    	
//    	5:Stmt使用相同的SQL语句,参数值不同--10000条
    	getJdbcTemplate().execute(new StatementCallback(){
    		public Object doInStatement(Statement stmt) throws SQLException
    		{
    			String stmtSQL = "insert into jdbc_stmt values('ivanl',2,25,";
    	    	for(int i = 0; i< 10000; i++)
    	    	{
    	    		logger.debug("stmt #"+i);
    	    		stmt.executeUpdate(stmtSQL+i+")");
    	    	}
    			return null;
    		}
    	});
    }
    
    
    public void jdbcPreStmtPerformance()
    {
    	//1:每次都单独连数据库--100条
//    	String stmtSQL = "insert into jdbc_stmt values(?,?,?,?)";
//    	for(int i = 0; i< 100; i++)
//    	{
//    		logger.debug("pre-stmt #"+i);
//    		getJdbcTemplate().update(stmtSQL, new Object[]{"ivanl", new Integer(2), new Integer(25), new Integer(1002)});
//    	}
    	
    	//2:Stmt使用相同的SQL语句,参数值也相同--100条
//    	String stmtSQL = "insert into jdbc_stmt values(?,?,?,?)";
//    	getJdbcTemplate().execute(stmtSQL,new PreparedStatementCallback(){
//    		public Object doInPreparedStatement(PreparedStatement ps)  throws SQLException
//    		{
//    	    	for(int i = 0; i< 100; i++)
//    	    	{
//    	    		logger.debug("pre-stmt #"+i);
//    	    		ps.setString(1, "ivanl");
//    	    		ps.setInt(2, 2);
//    	    		ps.setInt(3, 25);
//    	    		ps.setInt(4, 1002);
//    	    		ps.execute();
//    	    	}
//    			return null;
//    		}
//    	});
    	//3:Stmt使用相同的SQL语句,参数值不同--100条
//    	String stmtSQL = "insert into jdbc_stmt values(?,?,?,?)";
//    	getJdbcTemplate().execute(stmtSQL,new PreparedStatementCallback(){
//    		public Object doInPreparedStatement(PreparedStatement ps)  throws SQLException
//    		{
//    	    	for(int i = 0; i< 100; i++)
//    	    	{
//    	    		logger.debug("pre-stmt #"+i);
//    	    		ps.setString(1, "ivanl");
//    	    		ps.setInt(2, 2);
//    	    		ps.setInt(3, 25);
//    	    		ps.setInt(4, i);
//    	    		ps.execute();
//    	    	}
//    			return null;
//    		}
//    	});
    	//4:Stmt使用相同的SQL语句,参数值也相同--10000条
//    	String stmtSQL = "insert into jdbc_stmt values(?,?,?,?)";
//    	getJdbcTemplate().execute(stmtSQL,new PreparedStatementCallback(){
//		public Object doInPreparedStatement(PreparedStatement ps)  throws SQLException
//		{
//	    	for(int i = 0; i< 10000; i++)
//	    	{
//	    		logger.debug("pre-stmt #"+i);
//	    		ps.setString(1, "ivanl");
//	    		ps.setInt(2, 2);
//	    		ps.setInt(3, 25);
//	    		ps.setInt(4, 1002);
//	    		ps.execute();
//	    	}
//			return null;
//		}
//	});
    	//5:Stmt使用相同的SQL语句,参数值不同--10000条
    	String stmtSQL = "insert into jdbc_stmt values(?,?,?,?)";
    	getJdbcTemplate().execute(stmtSQL,new PreparedStatementCallback(){
    		public Object doInPreparedStatement(PreparedStatement ps)  throws SQLException
    		{
    	    	for(int i = 0; i< 10000; i++)
    	    	{
    	    		logger.debug("pre-stmt #"+i);
    	    		ps.setString(1, "ivanl");
    	    		ps.setInt(2, 2);
    	    		ps.setInt(3, 25);
    	    		ps.setInt(4, i);
    	    		ps.execute();
    	    	}
    			return null;
    		}
    	});
    	
    }
   发表时间:2007-01-22  
PreparedStatement的目的主要是批处理,跟Statement比顺序执行的效率本来就是不合理的测试方式
0 请登录后投票
   发表时间:2007-01-22  
PreparedStatement预编译应该是在数据库server端吧。是不是要close Statement 和 PreparedStatement来测试。因为如果都是使用同一个stmt的话, 就没有考虑到编译的效率了。
0 请登录后投票
   发表时间:2007-01-22  
这两个方法是使用junit分别测试的,使用的不是同一个stmt
0 请登录后投票
   发表时间:2007-01-22  
Ivan Li 写道
这两个方法是使用junit分别测试的,使用的不是同一个stmt


你误解我的意思了。 这个跟junit有什么关系
0 请登录后投票
   发表时间:2007-01-22  
我也怀疑楼主的测试方法。
不过我按我的方法测试了一下,
使用select * from test where id=?的查询,而不是insert。
我在循环里关闭statement。

但每次都是statement快一些。


不过我以前的确用PreparedStatement替代Statement得到了数倍的性能提高,
那时的情况是:字段数量很大,数百个字段(表是自动创建的),单次需要插入数千条记录,
所有使用了批处理。即便都是批处理,此时PreparedStatement还是能快约6倍。
0 请登录后投票
   发表时间:2007-01-22  
不同类型的数据库对此的优化程度可能是不同的;
单用户状态下和多用户状态下也是不同的。
0 请登录后投票
   发表时间:2007-01-22  
我对PreparedStatement和Statement的性能测试了一下:
测试代码如下:
Connection con = getOraConnection();
String sql = "select id,name from test where id=";
String tempSql;
int count = 1000;
long time = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
	Statement st = con.createStatement();
	tempSql=sql+(int) (Math.random() * 100);
	st.executeQuery(tempSql);
	st.close();
}
System.out.println("st cost:" + (System.currentTimeMillis() - time));

String psql = "select id,name from test where id=?";
time = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
	int id=(int) (Math.random() * 100);
	PreparedStatement pst = con.prepareStatement(psql);
	pst.setBigDecimal(1, new BigDecimal(id));
	pst.executeQuery();
	pst.close();
}

System.out.println("pst cost:" + (System.currentTimeMillis() - time));

con.close();


test表很简单,id int,name varchar(50).

对几种数据库和相应驱动程序进行测试,结果如下(ms):
oracle: 1235 1109
MSSQL2000(JTDS):391 453
MSSQL2000(MS): 453 640
Mysql5: 391 891
PostgreSQL8.1:  1078    1047
结论:
1.单并发情况下,oracle--PostgreSQL--MSSQL--MySQL的性能依次增高;
2.MySQL不支持Prepared Statement特性,已在其驱动程序的文档中证实,所以在MySQL里使用  PreparedStatement的性能尤其低,可以比Statement慢一倍以上。
  而MSSQL2000下,PreparedStatement比Statement慢,
  Oracle,PostgreSQL对它的支持最好,使用PreparedStatement性能比Statement高。
0 请登录后投票
   发表时间:2007-01-23  
count大点然后Math.random()*100 换成 Math.random()*10000000结果会不会更明显?

对于prepared stmt和stmt,Oracle应该都缓存其编译后的stmt,缓存100个对它来说太易了

对这个测试来说,所有pre stmt只需要编译一个即可,而stmt只要id不同就得编译
0 请登录后投票
   发表时间:2007-01-23  
楼上的,如果你把Statement st = con.createStatement(); 
和PreparedStatement pst = con.prepareStatement(psql); 都写在for循环外面,应该就更能看出谁快谁慢了。我在roacle里测了一下,查询5000条数据,PreparedStatement比Statement快一倍。
0 请登录后投票
论坛首页 Java企业应用版

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