數據庫批量數據插入問題分析
在數據庫的相關開發中,經常會遇到數據的批量插入問題。本文主要是通過實驗的方式探討批量數據插入的瓶頸,以及優化建議。
以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
一、普通插入
代碼:
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
2
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
3
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
4
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
5
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
6
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/ExpandedBlockStart.gif)
7
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
8
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
9
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/ExpandedBlockEnd.gif)
10
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
11
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
12
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
13
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
輸出結果:
commonInsert()執行時間為:13828ms
二、普通插入+手動提交:setAutoCommit(false)、commit()
代碼:
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
2
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
3
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
4
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
5
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
6
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
7
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/ExpandedBlockStart.gif)
8
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
9
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
10
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/ExpandedBlockEnd.gif)
11
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
12
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
13
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
14
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
15
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
輸出結果:
commonInsert()執行時間為:13813ms
對比分析:
可以看出,僅僅是這種方式的設置,對性能的影響並不大。
三、普通插入+手動提交+ prepareStatement方式
代碼:
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
2
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
3
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
4
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
5
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
6
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/ExpandedBlockStart.gif)
7
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
8
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
9
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
10
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/ExpandedBlockEnd.gif)
11
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
12
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
13
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
14
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
15
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
輸出結果:
prepareStatementInsert()執行時間為:12797ms
對比分析:
采用prepareStatement的方式確實可以提高一點性能,因為減少了數據庫引擎解析優化SQL語句的時間,但是由於現在的插入語句太簡單,所以性能提升不明顯。
四、批量插入:addBatch、executeBatch
代碼:
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
2
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
3
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
4
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
5
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
6
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
7
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/ExpandedBlockStart.gif)
8
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
9
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
10
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
11
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
12
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
13
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
14
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
15
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
16
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/ExpandedBlockEnd.gif)
17
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
18
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
19
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
20
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
輸出結果:
batchInsert()執行時間為:13625ms
對比分析:
按道理,這種批處理的方式是要快些的,但是測試結果卻不盡人意,有點不解,請高人拍磚。
五、批量插入:insert into tableName (x,xx,xxx) values(x,xx,xxx),(xx,xxx,xxxx)…,
代碼:
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
2
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
3
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
4
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
5
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
6
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
7
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/ExpandedBlockStart.gif)
8
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
9
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
10
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
11
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
12
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
13
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
14
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
15
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
16
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
17
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
18
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
19
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/ExpandedBlockEnd.gif)
20
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
21
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
22
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
23
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
輸出結果:
manyInsert()執行時間為:937ms
對比分析:
發現采用這種方式的批量插入性能提升最明顯,有10倍以上的性能提升。所以這種方式是我推薦的批量插入方式!
六、多線程插入
在第五種方式的基礎上采用多線程插入。
代碼:
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
2
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
3
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/ExpandedBlockStart.gif)
4
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
5
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
6
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
7
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
8
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
9
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
10
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
11
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
12
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
13
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
14
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
15
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
16
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
17
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
18
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
19
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
20
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
21
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
22
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
23
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
24
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
25
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
26
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
27
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
28
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
29
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
30
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
31
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
32
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
33
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
34
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
35
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
36
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
37
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
38
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
39
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/ExpandedBlockEnd.gif)
輸出結果:
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
代碼:
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
2
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
3
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
4
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
5
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
6
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
7
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/ExpandedBlockStart.gif)
8
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
9
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
10
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
11
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
12
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
13
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
14
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
15
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
16
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
17
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
18
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
19
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/ExpandedBlockEnd.gif)
20
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
21
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
22
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
23
![技術分享](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
輸出結果:
manyInsert2()執行時間為:3484ms
註意:第七項的代碼和第五是一樣的,除了數據表名稱不同(user、user2)
但是,
InnoDB :3484ms
MyISAM:937ms
所以,MyISAM引擎對大數據量的插入性能較好。
總結:
對於大數據量的插入,建議使用insert into tableName (x,xx,xxx) values(x,xx,xxx),(xx,xxx,xxxx)…,的方式,引擎建議使用MyISAM引擎。
友情提醒:本博文章歡迎轉載,但請註明出處:陳新漢
數據庫批量數據插入問題分析