论坛首页 Java企业应用论坛

为什么PrepareStatement和Batch性能相差不大,欢迎分析

浏览 11738 次
精华帖 (9) :: 良好帖 (0) :: 新手帖 (12) :: 隐藏帖 (0)
作者 正文
   发表时间:2010-08-21   最后修改:2010-08-22
    今天在Javaeye论坛上看到有位网友得出结论:主题:Hibernate性能 - 30倍普通SQL操作 - 探讨 地址:[url]http://www.iteye.com/topic/743613 [/url].贴子中说JDBC测试插入10000条数据花费时间:Total spent 249531毫秒.而且贴子还说:这个时间我觉得是比较合理的执行一条SQL花费25毫秒左右,之前有关效率方面的测试和这个结果差不多。

    不知他在什么配置情况下操作。因为结果是4分多钟。对于此结论很是惊奇,因为以前做过数据同步的东西,于是马上亲自验证,数据库和测试参数和他的都一样。

    先说结果:我的测试最慢的只用了2.6秒。Statement最慢,PrepareStaement快了5秒钟,Batch和PrepareStatement并没有实质性的提高,只是一小点(这个倒是让我奇怪)。从一万到十万数据量都做过测试,但变化不大。我一直认为Batch会提高不少数量级的,可是结果让我失望,也不知哪写得不对,大家可以分析一下代码。
------------------------------------------------------------
结果已出来,是默认的Mysql不是InnoDB,所以没有事务,所以之前测的没有本质变化。实际上在事务下,Batch能提高数量级的提高。

直接pringln 10000的一些对比数据:

清空表
普通的Statement插入数据:
插入数据量:10000
<运行时间: 2.656 秒>
运行时间:2656 毫秒
2.656
================================
清空表
通过PrepareStatement插入数据:
插入数据量:10000
<运行时间: 2.156 秒>
运行时间:2156 毫秒
2.156
================================
清空表
用批处理插入数据:
批量更新成功 10000 条记录!
<运行时间: 2.078 秒>
运行时间:2078 毫秒
2.078
================================

代码如下:

package test;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;

import com.fastbo.util.Clock;
import com.fastbo.util.ConnectionFactory;
import com.fastbo.util.DbUtil;

/**
 * Description: Jdbc相关性能测试,batch处理,PrepareStatement,Statement等。
 * 
 * <p>
 * Mysql数据库:表结构为简单的id,name(varchar:255),type(varchar:255)字段,id自增
 * </p>
 * 
 * @author Peter Wei Email: <a href="mailto:weigbo@163.com">weigbo@163.com </a>
 * 
 * @version 1.0 2010-8-21
 */
public class JdbcTest {

 /**
  * 测试数据量
  */
 public static int TEST_NUM = 10000;

 /**
  * 批处理大小
  */
 public static int BATCH_SIZE = 300;

 /**
  * 清空数据表
  * 
  * @param con
  */
 public static void clear(Connection con) {
  PreparedStatement ps = null;
  StringBuffer buff = new StringBuffer();
  try {
   buff.append("truncate table bobo");
   ps = con.prepareStatement(buff.toString());
   ps.executeUpdate();
   System.out.println("清空表");
  } catch (SQLException e) {
   e.printStackTrace();
  } finally {
   DbUtil.close(ps);
  }
 }

 /**
  * 普通的Statement插入数据
  * 
  * @param con
  */
 public static int add(Connection con) {
  Statement stmt = null;
  int num = 0;
  String sql = "insert into bobo(name,type) values('Peter Wei','test')";
  try {
   stmt = con.createStatement();
   for (int i = 0; i < TEST_NUM; i++) {
    num += stmt.executeUpdate(sql);
   }
   System.out.println("插入数据量:" + num);
  } catch (SQLException e) {
   e.printStackTrace();
  } finally {
   DbUtil.close(stmt);
  }
  return num;

 }

 /**
  * 用PrepareStatement插入数据
  * 
  * @param con
  */
 public static void addByPrepareStatement(Connection con) {

  PreparedStatement ps = null;
  StringBuffer buff = new StringBuffer();
  int num = 0;
  try {
   buff.append("insert into bobo(name,type)");
   buff.append(" values(?,?)");
   ps = con.prepareStatement(buff.toString());
   con.setAutoCommit(false);
   for (int i = 0; i < TEST_NUM; i++) {
	int index = 1;
	ps.setString(index++, "Peter Wei");
	ps.setString(index++, "test");
	num += ps.executeUpdate();
   }
   con.commit();
   con.setAutoCommit(true);
   System.out.println("插入数据量:" + num);
  } catch (SQLException e) {
   e.printStackTrace();
  } finally {
   DbUtil.close(ps);
  }
 }

 /**
  * 用批处理插入数据
  * 
  * @param con
  */
 public static void addByBatch(Connection con) {
  PreparedStatement ps = null;
  StringBuffer buff = new StringBuffer();
  int sum = 0;
  int[] num = null;
  try {
   buff.append("insert into bobo(name,type) values(?,?)");

   con.setAutoCommit(false);
   ps = con.prepareStatement(buff.toString());
   for (int i = 0; i < TEST_NUM; i++) {
    int index = 1;
    ps.setString(index++, "Peter Wei");
    ps.setString(index++, "test");
    ps.addBatch();
    if (i != 0 && i % BATCH_SIZE == 0) {
     num = ps.executeBatch();
     sum += num.length;
     con.commit();
     // System.out.println("batch:" + i);
    }

   }
   num = ps.executeBatch();
   sum += num.length;
   con.commit();
   con.setAutoCommit(true);
   System.out.println("批量更新成功 " + sum + " 条记录!");
  } catch (SQLException e) {
   e.printStackTrace();
  } finally {
   DbUtil.close(ps);
  }
 }

 public static void main(String[] args) {
  Connection con = ConnectionFactory.getConnection();
  clear(con);
  Clock c = new Clock();
  // 普通的Statement插入数据
  System.out.println("普通的Statement插入数据:");
  c.start();
  add(con);
  c.stop();
  System.out.println(c.toString());
  c.readMilli();
  System.out.println(c.read());
  System.out.println("================================");

  clear(con);
  // 通过PrepareStatement插入数据
  System.out.println("通过PrepareStatement插入数据:");
  c = new Clock();
  c.start();
  addByPrepareStatement(con);
  c.stop();
  System.out.println(c.toString());
  c.readMilli();
  System.out.println(c.read());
  System.out.println("================================");

  clear(con);
  // 用批处理插入数据
  System.out.println("用批处理插入数据:");
  c = new Clock();
  c.start();
  addByBatch(con);
  c.stop();
  System.out.println(c.toString());
  c.readMilli();
  System.out.println(c.read());
  System.out.println("================================");

 }

}

   发表时间:2010-08-22   最后修改:2010-08-22
你的例子我测了一下,环境是SQL2000和JTDS。

结果分别是:

清空表
普通的Statement插入数据:
插入数据量:10000
3656
================================
清空表
通过PrepareStatement插入数据:
插入数据量:10000
3985
================================
清空表
用批处理插入数据:
批量更新成功 10000 条记录!
296

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
另外,我又试了一下用Statement作批处理插入时的情况,结果是:
清空表
用批处理2插入数据:
批量更新成功 10000 条记录!
297
================================

用Statement,不使用批处理,但使用批量commit的时候,
代码如下:
con.setAutoCommit(false);
stmt = con.createStatement();
for (int i = 0; i < TEST_NUM; i++) {
	stmt.execute(sql);
	sum++;
	if (i != 0 && i % BATCH_SIZE == 0) {
		con.commit();
	}
}

结果是:
清空表
用批处理3插入数据:
批量更新成功 10000 条记录!
968
================================

0 请登录后投票
   发表时间:2010-08-22  
用Statement,使用批处理,但autoCommit=true的时候,
结果如下:

清空表
用批处理4插入数据:
批量更新成功 10000 条记录!
1719
================================

按上面的测试,影响性能最大的应该是

事务的大小,
批处理的影响次之,
PreparedStatement和Statement似乎区别不是很大
0 请登录后投票
   发表时间:2010-08-22   最后修改:2010-08-22
清空表
通过PrepareStatement插入数据:
插入数据量:10000
3985
================================
清空表
用批处理插入数据:
批量更新成功 10000 条记录!
296

=========================
你确定用我相同的代码,批处理和preparestatement相差有10倍?难道是mysql和sql server数据库内部的机制问题。我有空用oracle测一下。难道是mysql setCommit(false),不起作用,奇怪了。
0 请登录后投票
   发表时间:2010-08-22  
跟驱动的jdbc实现有直接关系,
所以各个数据库是不同的。

and 跟每条记录的数据量和复杂度也有关系。

0 请登录后投票
   发表时间:2010-08-22  
peterwei 写道
清空表
通过PrepareStatement插入数据:
插入数据量:10000
3985
================================
清空表
用批处理插入数据:
批量更新成功 10000 条记录!
296

=========================
你确定用我相同的代码,批处理和preparestatement相差有10倍?难道是mysql和sql server数据库内部的机制问题。我有空用oracle测一下。难道是mysql setCommit(false),不起作用,奇怪了。


^ ^
如果mysql的表不是事务的,那应该测试不出多少差异
0 请登录后投票
   发表时间:2010-08-22  
找不到以前的测试数据量

pcserver,redhat5.4,oracle10g,jdk6,表中有10几个字段,
批量2000,插入效率约每秒3w+,大概是逐条插入的近10倍。

不过用plsql写procedures的话,是每秒7w+,
这说明oracle的native驱动还是比jdbc驱动强太多。



0 请登录后投票
   发表时间:2010-08-22   最后修改:2010-08-22
dotaking 写道
找不到以前的测试数据量

pcserver,redhat5.4,oracle10g,jdk6,表中有10几个字段,
批量2000,插入效率约每秒3w+,大概是逐条插入的近10倍。

不过用plsql写procedures的话,是每秒7w+,
这说明oracle的native驱动还是比jdbc驱动强太多




不是你说的那个原因。
如果是procedure的话,很多操作直接是在DB Server端进行的,客户端调用,省却了很多网络传输的时间和资源开销。
0 请登录后投票
   发表时间:2010-08-22  
Batch和PrepareStatement比Statement并没有实质性的提高,只是一小点(这个倒是让我奇怪)。
--------------------------------------
ok,结果出来了。确实是setAutoCommit(false);不起作用。原来默认装的mysql数据库不是InnoDB,而是MyISAM,所以是没有事务的。楼上的skzr.org说得对。改数据库后,重测了一下,得到我想要的结果。

清空表
普通的Statement插入数据:
插入数据量:10000
<运行时间: 17.828 秒>
运行时间:17828 毫秒
17.828
================================
清空表
通过PrepareStatement插入数据:
插入数据量:10000
<运行时间: 2.594 秒>
运行时间:2594 毫秒
2.594
================================
清空表
用批处理插入数据:
批量更新成功 10000 条记录!
<运行时间: 2.187 秒>
运行时间:2187 毫秒
2.187
================================
0 请登录后投票
   发表时间:2010-08-22   最后修改:2010-08-22
murainwood 写道
dotaking 写道
找不到以前的测试数据量

pcserver,redhat5.4,oracle10g,jdk6,表中有10几个字段,
批量2000,插入效率约每秒3w+,大概是逐条插入的近10倍。

不过用plsql写procedures的话,是每秒7w+,
这说明oracle的native驱动还是比jdbc驱动强太多




不是你说的那个原因。
如果是procedure的话,很多操作直接是在DB Server端进行的,客户端调用,省却了很多网络传输的时间和资源开销。



局域网,网络不是瓶颈。
而且,我说的最后两句,是另外的东西。前面两句才是批量和逐条的效率比较。
只是我找不到测试报告了。~ :(
0 请登录后投票
论坛首页 Java企业应用版

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