环境:
MySQL 5.1
RedHat Linux AS 5
JavaSE 1.5
DbConnectionBroker 微型数据库连接池
 
测试的方案:
执行10万次Insert语句,使用不同方式。
 
A组:静态SQL,自动提交,没事务控制(MyISAM引擎)
1、逐条执行10万次
2、分批执行将10万分成m批,每批n条,分多种分批方案来执行。
 
B组:预编译模式SQL,自动提交,没事务控制(MyISAM引擎)
1、逐条执行10万次
2、分批执行将10万分成m批,每批n条,分多种分批方案来执行。
-------------------------------------------------------------------------------------------
C组:静态SQL,不自动提交,有事务控制(InnoDB引擎)
1、逐条执行10万次
2、分批执行将10万分成m批,每批n条,分多种分批方案来执行。
 
D组:预编译模式SQL,不自动提交,有事务控制(InnoDB引擎)
1、逐条执行10万次
2、分批执行将10万分成m批,每批n条,分多种分批方案来执行。
 
本次主要测试C、D组,并得出测试结果。
 
SQL代码
DROP TABLE IF EXISTS tuser;
CREATE TABLE tuser (     id bigint(20) NOT NULL AUTO_INCREMENT,     name varchar(12) DEFAULT NULL,     remark varchar(24) DEFAULT NULL,     createtime datetime DEFAULT NULL,     updatetime datetime DEFAULT NULL,     PRIMARY KEY (id) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
 
package testbatch;

import java.io.IOException;
import java.sql.*;

/**
* JDBC批量Insert优化(下)
*
* @author leizhimin 2009-7-29 10:03:10
*/
public class TestBatch {
        public static DbConnectionBroker myBroker = null;

static {
                try {
                        myBroker = new DbConnectionBroker("com.mysql.jdbc.Driver",
                                        "jdbc:mysql://192.168.104.163:3306/testdb",
                                        "vcom", "vcom", 2, 4,
                                        "c:\\testdb.log", 0.01);
                } catch (IOException e) {
                        e.printStackTrace();
                }
        }

/**
         * 初始化测试环境
         *
         * @throws SQLException 异常时抛出
         */
        public static void init() throws SQLException {
                Connection conn = myBroker.getConnection();
                conn.setAutoCommit(false);
                Statement stmt = conn.createStatement();
                stmt.addBatch("DROP TABLE IF EXISTS tuser");
                stmt.addBatch("CREATE TABLE tuser (\n" +
                                "    id bigint(20) NOT NULL AUTO_INCREMENT,\n" +
                                "    name varchar(12) DEFAULT NULL,\n" +
                                "    remark varchar(24) DEFAULT NULL,\n" +
                                "    createtime datetime DEFAULT NULL,\n" +
                                "    updatetime datetime DEFAULT NULL,\n" +
                                "    PRIMARY KEY (id)\n" +
                                ") ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8");
                stmt.executeBatch();
                conn.commit();
                myBroker.freeConnection(conn);
        }

/**
         * 100000条静态SQL插入
         *
         * @throws Exception 异常时抛出
         */
        public static void testInsert() throws Exception {
                init();         //初始化环境
                Long start = System.currentTimeMillis();
                for (int i = 0; i < 100000; i++) {
                        String sql = "\n" +
                                        "insert into testdb.tuser \n" +
                                        "\t(name, \n" +
                                        "\tremark, \n" +
                                        "\tcreatetime, \n" +
                                        "\tupdatetime\n" +
                                        "\t)\n" +
                                        "\tvalues\n" +
                                        "\t('" + RandomToolkit.generateString(12) + "', \n" +
                                        "\t'" + RandomToolkit.generateString(24) + "', \n" +
                                        "\tnow(), \n" +
                                        "\tnow()\n" +
                                        ")";
                        Connection conn = myBroker.getConnection();
                        conn.setAutoCommit(false);
                        Statement stmt = conn.createStatement();
                        stmt.execute(sql);
                        conn.commit();
                        myBroker.freeConnection(conn);
                }
                Long end = System.currentTimeMillis();
                System.out.println("单条执行100000条Insert操作,共耗时:" + (end - start) / 1000f + "秒!");
        }

/**
         * 批处理执行静态SQL测试
         *
         * @param m 批次
         * @param n 每批数量
         * @throws Exception 异常时抛出
         */
        public static void testInsertBatch(int m, int n) throws Exception {
                init();             //初始化环境
                Long start = System.currentTimeMillis();
                for (int i = 0; i < m; i++) {
                        //从池中获取连接
                        Connection conn = myBroker.getConnection();
                        conn.setAutoCommit(false);
                        Statement stmt = conn.createStatement();
                        for (int k = 0; k < n; k++) {
                                String sql = "\n" +
                                                "insert into testdb.tuser \n" +
                                                "\t(name, \n" +
                                                "\tremark, \n" +
                                                "\tcreatetime, \n" +
                                                "\tupdatetime\n" +
                                                "\t)\n" +
                                                "\tvalues\n" +
                                                "\t('" + RandomToolkit.generateString(12) + "', \n" +
                                                "\t'" + RandomToolkit.generateString(24) + "', \n" +
                                                "\tnow(), \n" +
                                                "\tnow()\n" +
                                                ")";
                                //加入批处理
                                stmt.addBatch(sql);
                        }
                        stmt.executeBatch();    //执行批处理
                        conn.commit();
//                        stmt.clearBatch();        //清理批处理
                        stmt.close();
                        myBroker.freeConnection(conn); //连接归池
                }
                Long end = System.currentTimeMillis();
                System.out.println("批量执行" + m + "*" + n + "=" + m * n + "条Insert操作,共耗时:" + (end - start) / 1000f + "秒!");
        }

/**
         * 100000条预定义SQL插入
         *
         * @throws Exception 异常时抛出
         */
        public static void testInsert2() throws Exception {     //单条执行100000条Insert操作,共耗时:40.422秒!
                init();         //初始化环境
                Long start = System.currentTimeMillis();
                String sql = "" +
                                "insert into testdb.tuser\n" +
                                "    (name, remark, createtime, updatetime)\n" +
                                "values\n" +
                                "    (?, ?, ?, ?)";
                for (int i = 0; i < 100000; i++) {
                        Connection conn = myBroker.getConnection();
                        conn.setAutoCommit(false);
                        PreparedStatement pstmt = conn.prepareStatement(sql);
                        pstmt.setString(1, RandomToolkit.generateString(12));
                        pstmt.setString(2, RandomToolkit.generateString(24));
                        pstmt.setDate(3, new Date(System.currentTimeMillis()));
                        pstmt.setDate(4, new Date(System.currentTimeMillis()));
                        pstmt.executeUpdate();
                        conn.commit();
                        pstmt.close();
                        myBroker.freeConnection(conn);
                }
                Long end = System.currentTimeMillis();
                System.out.println("单条执行100000条Insert操作,共耗时:" + (end - start) / 1000f + "秒!");
        }

/**
         * 批处理执行预处理SQL测试
         *
         * @param m 批次
         * @param n 每批数量
         * @throws Exception 异常时抛出
         */
        public static void testInsertBatch2(int m, int n) throws Exception {
                init();             //初始化环境
                Long start = System.currentTimeMillis();
                String sql = "" +
                                "insert into testdb.tuser\n" +
                                "    (name, remark, createtime, updatetime)\n" +
                                "values\n" +
                                "    (?, ?, ?, ?)";
                for (int i = 0; i < m; i++) {
                        //从池中获取连接
                        Connection conn = myBroker.getConnection();
                        conn.setAutoCommit(false);
                        PreparedStatement pstmt = conn.prepareStatement(sql);
                        for (int k = 0; k < n; k++) {
                                pstmt.setString(1, RandomToolkit.generateString(12));
                                pstmt.setString(2, RandomToolkit.generateString(24));
                                pstmt.setDate(3, new Date(System.currentTimeMillis()));
                                pstmt.setDate(4, new Date(System.currentTimeMillis()));
                                //加入批处理
                                pstmt.addBatch();
                        }
                        pstmt.executeBatch();    //执行批处理
                        conn.commit();
//                        pstmt.clearBatch();        //清理批处理
                        pstmt.close();
                        myBroker.freeConnection(conn); //连接归池
                }
                Long end = System.currentTimeMillis();
                System.out.println("批量执行" + m + "*" + n + "=" + m * n + "条Insert操作,共耗时:" + (end - start) / 1000f + "秒!");
        }

public static void main(String[] args) throws Exception {
                init();
                Long start = System.currentTimeMillis();
                System.out.println("--------C组测试----------");
                testInsert();
                testInsertBatch(100, 1000);
                testInsertBatch(250, 400);
                testInsertBatch(400, 250);
                testInsertBatch(500, 200);
                testInsertBatch(1000, 100);
                testInsertBatch(2000, 50);
                testInsertBatch(2500, 40);
                testInsertBatch(5000, 20);
                Long end1 = System.currentTimeMillis();
                System.out.println("C组测试过程结束,全部测试耗时:" + (end1 - start) / 1000f + "秒!");

System.out.println("--------D组测试----------");
                testInsert2();
                testInsertBatch2(100, 1000);
                testInsertBatch2(250, 400);
                testInsertBatch2(400, 250);
                testInsertBatch2(500, 200);
                testInsertBatch2(1000, 100);
                testInsertBatch2(2000, 50);
                testInsertBatch2(2500, 40);
                testInsertBatch2(5000, 20);

Long end2 = System.currentTimeMillis();
                System.out.println("D组测试过程结束,全部测试耗时:" + (end2 - end1) / 1000f + "秒!");
        }
}

 
 
测试结果意想不到吧,最短时间竟然超过上篇。观察整个测试结果,发现总时间很长,原因是逐条执行的效率太低了。
 
结论:
 
在本测试条件下,得出结论:
 
数据库连接池控制下,不自动提交,事务控制(InnoDB引擎)
 
1、逐条执行的效率很低很低,尽可能避免逐条执行。
2、事务控制下,静态SQL的效率超过预处理SQL。
3、分批的大小对效率影响挺大的,一般来说,事务控制下,分批大小在100-1000之间比较合适。
4、谈到优化方式,上面的批处理就是很好的优化策略。
 
 
大总结:
 
对比上篇没事务的测试结果,得出一个全面的结论:
 
1、连接池最基本的也是最重要的优化策略,总能大幅提高性能。
 
2、批处理在效率上总是比逐条处理有优势,要处理的数据的记录条数越大,批处理的优势越明显,批处理还有一个好处就是减少了对数据库的链接次数,从而减轻数据库的压力。
 
3、批处理执行SQL的时候,批处理的分批的大小与数据库的吞吐量以及硬件配置有很大关系,需要通过测试找到最佳的分批大小,一般在50-1000之间。
 
4、预处理SQL在没事务的表上效率较高,在有实物的情况下比静态SQL稍有不及。但预定义SQL还有个好处就是消耗的内存较少,静态SQL串会占用大量的内存资源,容易导致内存溢出的问题。因此批量执行时候可以优先选择预定义SQL。
 
5、在批处理执行的时候,每批执行完成后,最好显式的调用pstmt.close()或stmt.close()方法,以便尽快释放执行过的SQL语句,提高内存利用率。
 
6、对于有大量SELECT操作,MyISAM是更好的选择;对于有大量INSERT和UPDATE操作的表,InnoDB效率更好。
 
7、虽然测试结果只能反映特定情况下的一些事实,以上的优化策略是普遍策略,可以明显缩短寻找最优策略的时间,对于效率要求很高的程序,还应该做并发性等测试。
 
8、测试是件很辛苦的事情,你需要有大量的事实来证明你的优化是有效的,而不能单单凭经验,因为每个机器的环境都不一样,使用的方式也不同。

JDBC批量Insert深度优化(有事务)的更多相关文章

  1. JDBC批量插入数据优化,使用addBatch和executeBatch

    JDBC批量插入数据优化,使用addBatch和executeBatch SQL的批量插入的问题,如果来个for循环,执行上万次,肯定会很慢,那么,如何去优化呢? 解决方案:用 preparedSta ...

  2. JDBC批量插入优化addbatch

    // 获取要设置的Arp基准的List后,插入Arp基准表中 public boolean insertArpStandardList(List<ArpTable> list) { Con ...

  3. Mybatis与JDBC批量插入MySQL数据库性能测试及解决方案

    转自http://www.cnblogs.com/fnz0/p/5713102.html 不知道自己什么时候才有这种钻研精神- -. 1      背景 系统中需要批量生成单据数据到数据库表,所以采用 ...

  4. JDBC批量执行executeBatch

    JDBC事务 在数据库中,所谓事务是指一组逻辑操作单元,使数据从一种状态变换到另一种状态.为确保数据库中数据的一致性,数据的操纵应当是离散的成组的逻辑单元:当它全部完成时,数据的一致性可以保持,而当这 ...

  5. Spring源码深度解析之事务

    Spring源码深度解析之事务 目录 一.JDBC方式下的事务使用示例 (1)创建数据表结构 (2)创建对应数据表的PO (3)创建表和实体之间的映射 (4)创建数据操作接口 (5)创建数据操作接口实 ...

  6. JDBC、JTA、Spring的事务管理

    Java事务的类型有三种:JDBC事务.JTA(Java Transaction API)事务.容器事务. 事务就是对一系列的数据库操作(比如插入多条数据)进行统一的提交或回滚操作,如果插入成功,那么 ...

  7. java批量insert入mysql数据库

    mysql 批量insert语句为 insert into Table_(col1,col2...) values(val11,val12...),(val11,val12...),...; java ...

  8. OracleHelper(对增删改查分页查询操作进行了面向对象的封装,对批量增删改操作的事务封装)

    公司的一个新项目使用ASP.NET MVC开发,经理让我写个OracleHelper,我从网上找了一个比较全的OracleHelper类,缺点是查询的时候返回DataSet,数据增删改要写很多代码(当 ...

  9. MySQL内核深度优化

    版权声明:本文由简怀兵原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/179 来源:腾云阁 https://www.qclo ...

随机推荐

  1. json数据传输有感

    必须把object对象o给JSON.stringify(o) json字符串化 传到后台 前台js的对象某属性如果是Array  那后台就是Long[] idsArray  这种bean的属性对应 然 ...

  2. GTA项目 一, 包装外部WebService

    外部WebService返回的是xml太重了. 而JSON是web的新标准.所以要包装一下. 使用NewtonSoft.JSON的dll里面的JsonConvert.SerializeXmlNode方 ...

  3. 2.session与cookie的区别?

    session:储存用户访问的全局唯一变量,存储在服务器上的php指定的目录中的(session_dir)的位置进行的存放 cookie:用来存储连续訪問一个頁面时所使用,是存储在客户端,对于Cook ...

  4. C main

    #include <stdio.h> int main(int argv, char* argc[]) { printf("argv is %d", argv); // ...

  5. JQuery书写Ajax的几种方式?

    1 $.ajax({ type: "Post", //请求方式 ("POST" 或 "GET"), 默认为 "GET" ...

  6. switch结构2016/03/08

    Switch  03/08 一.结构 switch(){  case *: ;break;……default: ;brek;} 练习:输入一个日期,判断这一年第几天? Console.Write(&q ...

  7. 【水题】NOIP201504推销员

    NOIP201504推销员 [问题描述]   阿明是一名推销员,他奉命到螺丝街推销他们公司的产品.螺丝街是一条死胡同,出口与入口是同一个,街道的一侧是围墙,另一侧是住户.螺丝街一共有 N 家住户,第 ...

  8. kernel32.dll出错解决方案

    kernel32.dll 一.什么是kernel32内核文件 kernel32.dll是Windows 9x/Me中非常重要的32位动态链接库文件,属于内核级文件.它控制着系统的内存管理.数据的输入输 ...

  9. UINavigationController详解三(转)ToolBar

    原文出自:http://blog.csdn.net/totogo2010/article/details/7682641,特别感谢. 1.显示Toolbar  在RootViewController. ...

  10. js正则函数match、exec、test、search、replace、split使用介绍集合

    match 方法 使用正则表达式模式对字符串执行查找,并将包含查找的结果作为数组返回. stringObj.match(rgExp) 参数 stringObj 必选项.对其进行查找的 String 对 ...