1. 程式人生 > >數據庫批量數據插入問題分析

數據庫批量數據插入問題分析

== 5.0 性能 以及 mon 基礎 use 插入 light

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


以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引擎。
友情提醒:本博文章歡迎轉載,但請註明出處:陳新漢

數據庫批量數據插入問題分析