zoukankan      html  css  js  c++  java
  • 数据库批量数据插入问题分析

    在数据库的相关开发中,经常会遇到数据的批量插入问题。本文主要是通过实验的方式探讨批量数据插入的瓶颈,以及优化建议。


    以10w条记录的插入作为实验对象,采用下面的几种方法插入:

    1.       普通插入:普通的一条条插入

    2.       普通插入+手动提交:setAutoCommit(false)、commit()

    3.       普通插入+手动提交+ prepareStatement方式

    4.       批量插入:addBatch、executeBatch

    5.       批量插入:insert into tableName (x,xx,xxx) values(x,xx,xxx),(xx,xxx,xxxx)…,

    6.       多线程插入。

    7.       InnoDB引擎和MyISAM引擎的比较。


    实验环境:

    数据库:MySQL 5.0

    机器硬件:

    内存 3G 

    CPU AMD双核4400+ 2.3G

    首先建立一个简单的user表:

    CREATE TABLE `user` (                                         

              `id` varchar(50) NOT NULL,                                  

              `seqid` bigint(20) NOT NULL auto_increment,                 

              `name` varchar(50) NOT NULL,                                 

              PRIMARY KEY (`seqid`)

            ) ENGINE=MyISAM DEFAULT CHARSET=utf8

    一、普通插入
    代码:

     1Connection conn=source.getConnection();
     2Statement s=conn.createStatement();
     3String sql="";
     4long start=System.currentTimeMillis();
     5for(int i=0;i<100000;i++)
     6{
     7    sql="insert into user(id,name) value('"+(i+1)+"','chenxinhan')";
     8    s.execute(sql);
     9}
    10s.close();
    11conn.close();
    12long end=System.currentTimeMillis();
    13System.out.println("commonInsert()执行时间为:"+(end-start)+"ms");

    输出结果:
    commonInsert()执行时间为:13828ms

    二、普通插入+手动提交:setAutoCommit(false)、commit()
    代码:

     1Connection conn=source.getConnection();
     2conn.setAutoCommit(false);
     3Statement s=conn.createStatement();
     4String sql="";
     5long start=System.currentTimeMillis();
     6for(int i=0;i<100000;i++)
     7{
     8   sql="insert into user(id,name) value('"+(i+1)+"','chenxinhan')";
     9   s.execute(sql);
    10}
    11conn.commit();
    12s.close();
    13conn.close();
    14long end=System.currentTimeMillis();
    15System.out.println("commonInsert()执行时间为:"+(end-start)+"ms");

    输出结果:
    commonInsert()执行时间为:13813ms
    对比分析:
    可以看出,仅仅是这种方式的设置,对性能的影响并不大。

    三、普通插入+手动提交+ prepareStatement方式
    代码:

     1Connection conn=source.getConnection();
     2        conn.setAutoCommit(false);
     3        PreparedStatement ps=conn.prepareStatement("insert into user(id,name) value(?,?)");
     4        long start=System.currentTimeMillis();
     5        for(int i=0;i<100000;i++)
     6        {
     7            ps.setString(1,(i+1)+"");
     8            ps.setString(2,"chenxinhan");
     9            ps.execute();
    10        }
    11        conn.commit();
    12        ps.close();
    13        conn.close();
    14        long end=System.currentTimeMillis();
    15        System.out.println("prepareStatementInsert()执行时间为:"+(end-start)+"ms");

    输出结果:
    prepareStatementInsert()执行时间为:12797ms
    对比分析:
    采用prepareStatement的方式确实可以提高一点性能,因为减少了数据库引擎解析优化SQL语句的时间,但是由于现在的插入语句太简单,所以性能提升不明显。

    四、批量插入:addBatch、executeBatch
    代码:

     1        Connection conn=source.getConnection();
     2        conn.setAutoCommit(false);
     3        Statement s=conn.createStatement();
     4        String sql="";
     5        long start=System.currentTimeMillis();
     6        for(int j=0;j<100;j++)
     7        {
     8            for(int i=0;i<1000;i++)
     9            {
    10                sql="insert into user(id,name) value('"+(i+1)+"','chenxinhan')";
    11                s.addBatch(sql);
    12            }
    13            s.executeBatch();
    14            conn.commit();
    15            s.clearBatch();
    16        }
    17        s.close();
    18        conn.close();
    19        long end=System.currentTimeMillis();
    20        System.out.println("batchInsert()执行时间为:"+(end-start)+"ms");

    输出结果:
    batchInsert()执行时间为:13625ms
    对比分析:
    按道理,这种批处理的方式是要快些的,但是测试结果却不尽人意,有点不解,请高人拍砖。

    五、批量插入:insert into tableName (x,xx,xxx) values(x,xx,xxx),(xx,xxx,xxxx)…,
    代码:

     1Connection conn=source.getConnection();
     2        conn.setAutoCommit(false);
     3        Statement s=conn.createStatement();
     4        StringBuilder sql=new StringBuilder("");
     5        long start=System.currentTimeMillis();
     6        for(int j=0;j<100;j++)
     7        {
     8            sql=new StringBuilder("");
     9            sql.append("insert into user(id,name) values ");
    10            for(int i=0;i<1000;i++)
    11            {
    12                if(i==999)
    13                    sql.append("('").append(i+1).append("',").append("'cxh')");
    14                else
    15                    sql.append("('").append(i+1).append("',").append("'cxh'),");
    16            }
    17            s.execute(sql.toString());
    18            conn.commit();
    19        }
    20        s.close();
    21        conn.close();
    22        long end=System.currentTimeMillis();
    23        System.out.println("manyInsert()执行时间为:"+(end-start)+"ms");

    输出结果:
    manyInsert()执行时间为:937ms
    对比分析:
    发现采用这种方式的批量插入性能提升最明显,有10倍以上的性能提升。所以这种方式是我推荐的批量插入方式!

    六、多线程插入
    在第五种方式的基础上采用多线程插入。
    代码:

     1final Connection conn=source.getConnection();
     2        for(int j=0;j<3;j++)
     3        {
     4            Thread t=new Thread(){
     5                @Override
     6                public void run() {
     7                    try
     8                    {
     9                        long start=System.currentTimeMillis();
    10                        Statement s=conn.createStatement();
    11                        StringBuilder sql=new StringBuilder("");
    12                        for(int j=0;j<100;j++)
    13                        {
    14                            conn.setAutoCommit(false);
    15                            sql=new StringBuilder("");
    16                            sql.append("insert into user (id,name) values ");
    17                            for(int i=0;i<1000;i++)
    18                            {
    19                                if(i==999)
    20                                    sql.append("('").append(i+1).append("',").append("'cxh')");
    21                                else
    22                                    sql.append("('").append(i+1).append("',").append("'cxh'),");
    23                            }
    24                            s.execute(sql.toString());
    25                            conn.commit();
    26                        }
    27                        s.close();
    28                        long end=System.currentTimeMillis();
    29                        System.out.println("multiThreadBatchInsert()执行时间为:"+(end-start)+"ms");
    30                    }
    31                    catch(Exception e)
    32                    {
    33                        e.printStackTrace();
    34                    }
    35                }
    36            };
    37            t.start();
    38            //t.join();
    39        }

    输出结果:
    multiThreadBatchInsert()执行时间为:2437ms
    multiThreadBatchInsert()执行时间为:2625ms
    multiThreadBatchInsert()执行时间为:2703ms

    注意:上面我采用的是三个线程插入30w条数据。
    取最大时间为2703ms,较上面的937ms,基本还是三倍的时间。
    所以发现此时多线程也解决不了批量数据插入问题。原因就是,这时候的性能瓶颈不是CPU,而是数据库!

    七、InnoDB引擎和MyISAM引擎的比较
    最后,分析一下,这两个引擎对批量数据插入的影响。
    先建立user2数据表:

    CREATE TABLE `user2` (                                         

              `id` varchar(50) NOT NULL,                                  

              `seqid` bigint(20) NOT NULL auto_increment,                 

              `name` varchar(50) NOT NULL,                                 

              PRIMARY KEY (`seqid`)

            ) ENGINE=InnoDB DEFAULT CHARSET=utf8

    代码:

     1Connection conn=source.getConnection();
     2        conn.setAutoCommit(false);
     3        Statement s=conn.createStatement();
     4        StringBuilder sql=new StringBuilder("");
     5        long start=System.currentTimeMillis();
     6        for(int j=0;j<100;j++)
     7        {
     8            sql=new StringBuilder("");
     9            sql.append("insert into user2 (id,name) values ");
    10            for(int i=0;i<1000;i++)
    11            {
    12                if(i==999)
    13                    sql.append("('").append(i+1).append("',").append("'cxh')");
    14                else
    15                    sql.append("('").append(i+1).append("',").append("'cxh'),");
    16            }
    17            s.execute(sql.toString());
    18            conn.commit();
    19        }
    20        s.close();
    21        conn.close();
    22        long end=System.currentTimeMillis();
    23        System.out.println("manyInsert2()执行时间为:"+(end-start)+"ms");

    输出结果:
    manyInsert2()执行时间为:3484ms
    注意:第七项的代码和第五是一样的,除了数据表名称不同(user、user2)
    但是,
    InnoDB :3484ms
    MyISAM:937ms
    所以,MyISAM引擎对大数据量的插入性能较好。

    总结:
    对于大数据量的插入,建议使用insert into tableName (x,xx,xxx) values(x,xx,xxx),(xx,xxx,xxxx)…,的方式,引擎建议使用MyISAM引擎。
    友情提醒:本博文章欢迎转载,但请注明出处:陈新汉

  • 相关阅读:
    js截取字符串区分汉字字母代码
    List 去处自定义重复对象方法
    63. Unique Paths II
    62. Unique Paths
    388. Longest Absolute File Path
    41. First Missing Positive
    140. Word Break II
    139. Word Break
    239. Sliding Window Maximum
    5. Longest Palindromic Substring
  • 原文地址:https://www.cnblogs.com/Syney/p/6856772.html
Copyright © 2011-2022 走看看