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

JDBC批量Insert深度优化(有事务)
环境:
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;
C、D组测试代码:
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 + "秒!");
        }
}
执行结果:
--------C组测试----------
单条执行100000条Insert操作,共耗时:103.656秒!
批量执行100*1000=100000条Insert操作,共耗时:31.328秒!
批量执行250*400=100000条Insert操作,共耗时:31.406秒!
批量执行400*250=100000条Insert操作,共耗时:31.75秒!
批量执行500*200=100000条Insert操作,共耗时:31.438秒!
批量执行1000*100=100000条Insert操作,共耗时:31.968秒!
批量执行2000*50=100000条Insert操作,共耗时:32.938秒!
批量执行2500*40=100000条Insert操作,共耗时:33.141秒!
批量执行5000*20=100000条Insert操作,共耗时:35.265秒!
C组测试过程结束,全部测试耗时:363.656秒!
--------D组测试----------
单条执行100000条Insert操作,共耗时:107.61秒!
批量执行100*1000=100000条Insert操作,共耗时:32.64秒!
批量执行250*400=100000条Insert操作,共耗时:32.641秒!
批量执行400*250=100000条Insert操作,共耗时:33.109秒!
批量执行500*200=100000条Insert操作,共耗时:32.859秒!
批量执行1000*100=100000条Insert操作,共耗时:33.547秒!
批量执行2000*50=100000条Insert操作,共耗时:34.312秒!
批量执行2500*40=100000条Insert操作,共耗时:34.672秒!
批量执行5000*20=100000条Insert操作,共耗时:36.672秒!
D组测试过程结束,全部测试耗时:378.922秒!
测试结果意想不到吧,最短时间竟然超过上篇。观察整个测试结果,发现总时间很长,原因是逐条执行的效率太低了。
结论:
在本测试条件下,得出结论:
数据库连接池控制下,不自动提交,事务控制(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、测试是件很辛苦的事情,你需要有大量的事实来证明你的优化是有效的,而不能单单凭经验,因为每个机器的环境都不一样,使用的方式也不同。
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/549420.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机专业面试中会问到的问题

今年是2015年&#xff0c;在过去几年中&#xff0c;电面&#xff08;电话面试&#xff09;是筛选程序员职位候选人的最流行的方式。它让雇佣双方很容易互相了解对方&#xff0c;候选人不需要去未来雇主的所在地&#xff0c;面试官也不用做额外的安排。这是我介绍程序员面试问题…

ActiveBpel部署运行BPEL流程实例

本文接收使用ActiveBpel开发&#xff0c;部署和运行BPEL流程实例。本文的例子工程和服务工程请见附件。1. 安装ActiveBpel5.0.2要安装ActiveBpel5.0.2&#xff0c;需要先安装JDK1.5&#xff0c;Tomcat。注意这里是JDK1.5版本的&#xff0c;ActiveBpel5.0.2不支持JDK1.5以上的版…

如何用c语言从txt文件中读取数据

用C语言从txt文件中读取数据&#xff0c;可以使用C标准库文件自带的文件接口函数进行操作。 一、打开文件&#xff1a; FILE *fopen(const char *filename, const char *mode); 因为txt文件为文本文件&#xff0c; 所以打开时选择的mode应为"r"或者"rt"。 …

活动目录实战系列五(更改域名)

在单域环境下且没有什么服务的情况的改域名还行&#xff0c;如果网络环境比较复杂或者有其他服务&#xff0c;不建议更改。以免发生错误。在实验中由于虚拟机坏掉在这里使用了两个&#xff0c;有部分图片对不上&#xff0c;请谅解。 WIN2000域不支持域重命名域重命名并不是在域…

关于C语言中的Bool类型变量的一点看法

C语言中并没有bool类型变量。这是C中新增的系统类型。 要在C语言中使用bool类型&#xff0c;可以使用自定义的方式。 使用整型做bool类型。 typedef int bool; 或 typedef unsigned char bool; 只要是整型&#xff0c;都可以当做bool使用。 使用枚举类型做bool类型。 typedef…

Lotus Notes Domino 监控

SUM服务器监控软件对Lotus可以进行全面监控. 监控方法主要采用SNMP(lnSNMP), 它是Lotus的标准监控方法. SUM用户不需要了任何Lotus MIB的信息, 只要启用lnSNMP服务即可实现对Lotus邮件, 文档, ,数据库,Demino, 线程等的监控. 主要监控内容:邮件: 总数,错误,正在传送等,邮件大小…

C语言中的fopen函数

fopen 函数原型&#xff1a;FILE * fopen(const char * path, const char * mode); 相关函数&#xff1a;open, fclose, fopen_s, _wfopen 返回值&#xff1a;文件顺利打开后&#xff0c;指向该流的文件指针就会被返回。如果文件打开失败则返回 NULL&#xff0c;并把错误代码存…

百度笔试

题目大致是这样的&#xff1a; 第一部分选择题&#xff1a;有几道网络相关的题目&#xff0c;巨简单&#xff0c;比如第一题是TCP、RIP、IP、FTP中哪个协议是传输层的......。有一道linux的chown使用题目。其他的全是数据结构的题目&#xff01;什么链&#xff0c;表&#xff0…

学IT,看教程,看视频,你必须知道的学习网站

版权声明&#xff1a;本文为博主原创文章&#xff0c;未经博主允许不得转载。 https://blog.csdn.net/rl529014/article/details/50570287 </div><link rel"stylesheet" href"https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_vie…

MFC消息响应机制及映射机制理解

一&#xff0e;MFC消息响应机制分析 ---- MFC是Windows下程序设计的最流行的一个类库&#xff0c;但是该类库比较庞杂&#xff0c;尤其是它的消息映射机制&#xff0c;更是涉及到很多低层的东西&#xff0c;我们在这里&#xff0c;对它的整个消息映射机制进行了系统的分析&…

KMP算法 Next数组详解(【洛谷3375】KMP字符串匹配 )

版权声明&#xff1a;本文为博主原创文章&#xff0c;未经博主允许不得转载。 https://blog.csdn.net/qq_30974369/article/details/74276186 </div><div id"content_views" class"markdown_views"><!-- flowchart 箭头图标 勿删 --…

“顾问”

关于顾问的定义&#xff0c;有这么一个故事&#xff1a;一个客户花了大价钱启动了一个项目&#xff0c;这个项目主要解决的问题是&#xff1a;“现在几点了&#xff1f;”这时顾问走过来说&#xff1a;“请借你的手表用一下。”然后看了一下说&#xff1a;“现在是下午3点。”然…

转自知乎-我见过最通俗易懂的KMP算法详解

有些算法&#xff0c;适合从它产生的动机&#xff0c;如何设计与解决问题这样正向地去介绍。但KMP算法真的不适合这样去学。最好的办法是先搞清楚它所用的数据结构是什么&#xff0c;再搞清楚怎么用&#xff0c;最后为什么的问题就会有恍然大悟的感觉。我试着从这个思路再介绍一…

Sybase常见问题

1. Sybase数据库日志满碰到这种情况&#xff0c;常会出现能查询数据&#xff0c;但无法更新数据&#xff0c;启动事务等。在代码中跟踪可以发现&#xff0c;连接能够打开&#xff0c;但事务却无法开启。常会提示“Connection to Sybase server has been lost. All active trans…

Redis在Window服务下的安装

Redis 安装1.首先在Windows下下载安装Redis下载地址&#xff1a;https://github.com/MicrosoftArchive/redis/releases根据你电脑系统的实际情况选择32位还是64位&#xff0c;在这里我下载了的是Redis-x64-3.0.500.zip压缩包&#xff0c;压缩后得到解压文件.2.测试运行打开一个…

在一个禁止离婚的国家结婚

爱尔兰是全世界结婚率最低的国家&#xff0c;作为一个信奉天主教的国度&#xff0c;这个国家是禁止离婚的。很多适逢婚龄的男男女女大都选择观望&#xff0c;迟迟不去婚姻登记部门办理手续。所以&#xff0c;在爱尔兰流行不婚和晚婚。但一对中国夫妻在爱尔兰的结婚经历&#xf…

【计算机网络】时延、发送时延、传输时延、处理时延、排队时延、时延带宽积

版权声明&#xff1a;答应我&#xff0c;右上角点个「赞」再走好么&#xff5e;不甚感激&#xff5e;&#xff08;卖萌脸ヾ(^▽^)ノ https://blog.csdn.net/liuchuo/article/details/52893629 </div><link rel"stylesheet" href"https://csdnim…

ASP.NET (C#) 面试笔试题目收集

1. 简述 private、 protected、 public、 internal 修饰符的访问权限。 答 . private : 私有成员, 在类的内部才可以访问。 protected : 保护成员&#xff0c;该类内部和继承类中可以访问。 public : 公共成员&#xff0c;完全公开&#xff0c;没有访问限制。 internal: 在同一…

排队时延(Queuing delay)

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请注明出处。 https://blog.csdn.net/zhangskd/article/details/18224897 </div><link rel"stylesheet" href"https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-f57…

传播时延、发送时延、处理时延和排队时延各自的物理意义是什么?

传播时延、发送时延、处理时延和排队时延各自的物理意义是什么&#xff1f; 传播时延: 信号在传输通道上产生的时延 发送时延&#xff1a;以一定的速率发送完一个一定长度报文所需的时间 处理时延&#xff1a;节点进行报文存储转发处理所产生的时间 排队时延&#xff1a;报文…