天天看點

資料庫批量資料插入問題分析

  在資料庫的相關開發中,經常會遇到資料的批量插入問題。本文主要是通過實驗的方式探讨批量資料插入的瓶頸,以及優化建議。

以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

一、普通插入

代碼:

 1

資料庫批量資料插入問題分析

Connection conn=source.getConnection();

 2

資料庫批量資料插入問題分析

Statement s=conn.createStatement();

 3

資料庫批量資料插入問題分析

String sql="";

 4

資料庫批量資料插入問題分析

long start=System.currentTimeMillis();

 5

資料庫批量資料插入問題分析

for(int i=0;i<100000;i++)

 6

資料庫批量資料插入問題分析

{

 7

資料庫批量資料插入問題分析

    sql="insert into user(id,name) value('"+(i+1)+"','chenxinhan')";

 8

資料庫批量資料插入問題分析

    s.execute(sql);

 9

資料庫批量資料插入問題分析

}

10

資料庫批量資料插入問題分析

s.close();

11

資料庫批量資料插入問題分析

conn.close();

12

資料庫批量資料插入問題分析

long end=System.currentTimeMillis();

13

資料庫批量資料插入問題分析

System.out.println("commonInsert()執行時間為:"+(end-start)+"ms"); 輸出結果:

commonInsert()執行時間為:13828ms

二、普通插入+手動送出:setAutoCommit(false)、commit()

代碼:  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 i=0;i<100000;i++)

 7

資料庫批量資料插入問題分析

{

 8

資料庫批量資料插入問題分析

   sql="insert into user(id,name) value('"+(i+1)+"','chenxinhan')";

 9

資料庫批量資料插入問題分析

   s.execute(sql);

10

資料庫批量資料插入問題分析

}

11

資料庫批量資料插入問題分析

conn.commit();

12

資料庫批量資料插入問題分析

s.close();

13

資料庫批量資料插入問題分析

conn.close();

14

資料庫批量資料插入問題分析

long end=System.currentTimeMillis();

15

資料庫批量資料插入問題分析

System.out.println("commonInsert()執行時間為:"+(end-start)+"ms"); 輸出結果:

commonInsert()執行時間為:13813ms

對比分析:

可以看出,僅僅是這種方式的設定,對性能的影響并不大。

三、普通插入+手動送出+ prepareStatement方式

代碼:  1

資料庫批量資料插入問題分析

Connection 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)…,

代碼:  1

資料庫批量資料插入問題分析

Connection 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倍以上的性能提升。是以這種方式是我推薦的批量插入方式!

六、多線程插入

在第五種方式的基礎上采用多線程插入。

代碼:  1

資料庫批量資料插入問題分析

final 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

代碼:  1

資料庫批量資料插入問題分析

Connection 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引擎。

繼續閱讀