可以直接滑到文章末尾看结论
基于 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的三种批量插入以及次效率比较),所以还是根据实际业务情况去选择。真实环境中,如果面临如此巨大的数据插入,那么肯定不能在线去做,可以先放到队列里面,离线错开高峰期执行,这样性能也不是首位的。