JDBC批量Insert深度优化(有事务)

时间:2022-09-04 09:11:13
环境:
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 + "秒!");
        }
}

 
JDBC批量Insert深度优化(有事务)
 
测试结果意想不到吧,最短时间竟然超过上篇。观察整个测试结果,发现总时间很长,原因是逐条执行的效率太低了。
 
结论:
 
在本测试条件下,得出结论:
 
数据库连接池控制下,不自动提交,事务控制(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批量插入数据优化&comma;使用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&lpar;对增删改查分页查询操作进行了面向对象的封装,对批量增删改操作的事务封装&rpar;

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

  9. MySQL内核深度优化

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

随机推荐

  1. 大型网站提速关键技术&lpar;页面静态化&comma;memcached&comma;MySql优化&rpar;&lpar;三&rpar;

    页面静态化的技术实现有两种方式 使用PHP自己的缓存机制 先说明一下OB缓存的机制. ob1.php 代码:说明的ob的各个用法->项目中 ☞ 如何打开ob缓存 ①     配置php.ini ...

  2. c&num;编码转换

    /// <summary> /// URL编码 /// </summary> /// <param name="Source"></par ...

  3. servlet、filter、listener、interceptor之间的区别和联系

    一.概念 1.servlet:servlet是一种运行服务器端的java应用程序,具有独立于平台和协议的特性,并且可以动态的生成web页面,它工作在客户端请求与服务器响应的中间层. 2.filter: ...

  4. Ⅲ&period;AngularJS的点点滴滴-- 路由

    路由ngRoute (需要依赖ngRoute模块) <html> <script src="http://ajax.googleapis.com/ajax/libs/ang ...

  5. C&num;实现窗体间的通信

    以下将窗体间的几种通信实现方式做一下罗列:首先新建一个窗体Form1,在其中放置一个Textbox.Button控件.再新建一个窗体Form2,其上放置一个Button控件.具体代码示例如下: //F ...

  6. aforge 学习-基本图像处理要用的类库

    1.图像灰度化:Grayscale.CommonAlgorithms.BT709(3种) FiltersSequence =new  FiltersSequence(数组处理函数): 2.二值化(阈值 ...

  7. 理解MySQL数据库事务

    1. 什么是事务处理? 事务处理是一种机制,它是用来管理必须成批执行的mysql操作.来保证数据库不完整的操作结果. 2. 为什么要使用事务处理? 在使用mysql操作数据的过程中,如果只是简单的中小 ...

  8. Python——OS模块

    OS模块 OS模块 #os模块就是对操作系统进行操作,使用该模块必须先导入模块: import os #getcwd() 获取当前工作目录(当前工作目录默认都是当前文件所在的文件夹) result = ...

  9. 第三次Scrum编程冲刺

    小组第三次冲刺的任务及其完成情况描述 回顾第二次任务,完成了基本业务:用户可以使拍摄视频并上传至个人的空间页面,来与关注他的粉丝分享.关注“长大故事”社区中的其他用户,来了解别人的动态.别人所发动态下 ...

  10. POJ2230 Watchcow

    原题链接 类欧拉回路,要求每条边被正反各经过一次,且从\(1\)出发并回到\(1\). 只需每次搜索该点的边时,将该点的边对应的邻接表头及时修改为下一条即可,因为邻接表恰好储存了正反方向的边,所以及时 ...