可以直接滑到文章末尾看結論
基於 MySQL
數據庫對批量數據插入的支持
下面是把多條插入語句進行拼接,一起執行語句,同時插入大量數據
/*UserDao接口類*/
/**
* 批處理添加用戶
* @param users
*/
void addUser(@Param("users") List<User> users);
<!--userDao.xml配置-->
<insert id="addUser" parameterType="list">
insert into user(username,address,sex,birthday) values
<foreach collection="users" item="user" separator=",">
(#{user.userName},#{user.userAddress},#{user.userSex},#{user.userBirthday})
</foreach>
</insert>
/*測試類*/
@Test
public void testAddUser(){
List<User> users = new ArrayList<>();
for (int i = 0; i < 500; i++) {
users.add(new User("zhangsan" + i + 1, "北京市" + i, "男", new Date()));
}
userDao.addUser(users);
sqlSession.commit();
}
但是以這種方式進行批量操作,如果在數據量特別大的情況下,拼接的sql
的packet
數據包大小是非常大的,對插入會有影響。一般如果一次性插入大量數據的話,一般會分批插入,比如超過1000條,就會提交一次,後面的數據再分批次提交。
MyBatis
基於SqlSession
的ExecutorType
進行批量數據的插入
在SqlMapConfig.xml
中的標籤中設置上面的屬性就可以進行批處理
<settings>
<setting name="defaultExecutorType" value="BATCH"></setting>
</settings>
但是設置後全部的SQL
語句都會按照批處理的方式處理操作,所以這個方法基本不用,採用下面的方式添加批處理。
接口和mapper文件如下
/*UserDao接口*/
/**
* 保存一個用戶
* @param user
* @return
*/
int saveOne(User user);
<!--UserDao.xml配置-->
<!--添加用戶-->
<insert id="saveOne" parameterType="user" useGeneratedKeys="true" keyProperty="userId">
insert into user(username,address,sex,birthday)
values(#{userName},#{userAddress},#{userSex},#{userBirthday})
</insert>
- 不設置批處理
/*測試*/
@Test
public void testAddUserBatch(){
//不設置支持批處理操作
sqlSession = sqlSessionFactory.openSession();
userDao = sqlSession.getMapper(UserDao.class);
long begin=System.currentTimeMillis();
for (int i = 500; i < 1000; i++) {
userDao.saveOne(new User("lisi" + i + 1, "上海市" + i, "男", new Date()));
}
long end= System.currentTimeMillis();
System.out.println(end-begin);//爲1154
sqlSession.commit();
}
分別進行了500次插入操作(分條插入:預編譯+插入),頻繁的和數據庫傳輸數據,插入的效率特別低
- 設置批處理
/*測試*/
@Test
public void testAddUserBatch(){
//設置支持批處理操作
sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
userDao = sqlSession.getMapper(UserDao.class);
long begin=System.currentTimeMillis();
for (int i = 500; i < 1000; i++) {
userDao.saveOne(new User("lisi" + i + 1, "上海市" + i, "男", new Date()));
}
long end= System.currentTimeMillis();
System.out.println(end-begin);//爲428
sqlSession.commit();
}
顯著提高插入的效率(先預編譯,後一起插入)
對於在Spring+MyBatis中如何使用這種方法,可以參考ssm整合Mybatis之批量操作
選擇(結論)
可以看到,上面得出的結論是批處理處理性能高於foreach
,之所以得出這樣的性能應該是因爲假定連接數足夠大,緩存足夠大的情況(真實環境中不存在)。而我們日常開發中會配置數據連接池,數據庫連接池裏連接數有限導致多條批量執行慢於foreach
執行(參考文章mybatis的三種批量插入以及次效率比較),所以還是根據實際業務情況去選擇。真實環境中,如果面臨如此巨大的數據插入,那麼肯定不能在線去做,可以先放到隊列裏面,離線錯開高峯期執行,這樣性能也不是首位的。