十万条数据插入只用不到一秒!

3,615 阅读4分钟

前言

框架大大方便了研发,使开发者们只需专注于业务层面的开发,无需处理较底层的逻辑。市面上常用的持久层的框架大致有Hibernate,Mybatis、Mybatis-plus、Spring DataJPA等,面试者经常会被问到比如数据优化、一次性插入十万条数据到数据库如何迅速完成等问题,对于这些需要执行效率的项目来说,这些框架是否能满足需求呢?

研究框架执行效率的原因是一次技术群讨论持久层转移数据,但大家意见不一致,有些人属于是印象流了,没有自己深入研究,那么我就代替大家来做一次测试

测试目标

此次我们会分别测试mybatis、mybatis-plus、spring-datajpa、javax.persistence.entityManager、jdbcTemplate的插入数据执行效率

先公布执行效率结果

jdbcTemplate > entityManager > mybatis > mybatis-plus > datajpa

工程搭建

实体类

简单搭建一个maven项目,编写一个User类,User类包含几种类型:数字、自增id、字符串、日期,都是常用的数据类型

@Data
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    @ApiModelProperty("姓名")
    private String name;

    @ApiModelProperty("年龄")
    private Integer age;

    @ApiModelProperty("地址")
    private String address;

    @ApiModelProperty("出生日期")
    private Date birthday;

}

Controller类

我们提供一些测试的接口以供访问

@RestController
@RequestMapping("/user")
public class UserController {

    @Resource
    private UserService userService;

    @RequestMapping("/createBatch/jpa")
    public R<String> createBatch(@RequestBody UserParam param){
        return userService.createUserBatchByJpa(param);
    }

    @RequestMapping("/createBatch/entityManager")
    public R<String> selfCreateBatch(@RequestBody UserParam param){
        return userService.createUserBatchByEntityManager(param);
    }

    @RequestMapping("/createBatch/self")
    public R<String> createBatchSelf(@RequestBody UserParam param){
        return userService.createUserBatchBySelf(param);
    }

    @RequestMapping("/createBatch/thread")
    public R<String> createBatchThread(@RequestBody UserParam param) throws InterruptedException {
        return userService.createUserBatchByThread(param);
    }

    @RequestMapping("/createBatch/jdbc")
    public R<String> createBatchJdbc(@RequestBody UserParam param) {
        return userService.createUserBatchByJdbc(param);
    }
}

一些静态数据,组成随机实体

private static final List<String> surnameList = new ArrayList<>(Arrays.asList("冯","陈","李","王","程","赵","许","周"));
private static final List<String> lastNameList = new ArrayList<>(Arrays.asList("小二","麒麟","大强","轩轩","傲雪","冬梅","舒佳","牛逼"));
private static final List<String> addressList = new ArrayList<>(Arrays.asList(
		"湖北武汉市江夏区百川路西侧99号",
		"湖北省随州市随县厉山镇神农大道厉山站东南750米",
		"恩施土家族苗族自治州恩施市工农路3巷8号",
		"湖北省恩施土家族苗族自治州恩施市熙苑二期对面",
		"湖北省黄石市大冶市东岳路街道大冶大道63号",
		"湖北省黄石市西塞山区沿湖路476号",
		"湖北省黄石市下陆区广州路26号",
		"大冶市东岳路办事处岳家咀路9号"));

组装数据的函数,此处是1000条数据执行一次,也可换成100条或其他次数

private List<List<User>> createUserList(UserParam param){
	Random rand = new Random();
	List<List<User>> resultList = new ArrayList<>();
	List<User> Users = new ArrayList<>();
	int numbers = param.getNums();
	for (int i = 1; i <= numbers; i++) {
		User User = new User();
		User.setName(surnameList.get(rand.nextInt(7)) + lastNameList.get(rand.nextInt(7)));
		User.setAge(rand.nextInt(100)+3);
		User.setAddress(addressList.get(rand.nextInt(7)));
		User.setBirthday(new Date());
		Users.add(User);
		if(i % 1000 == 0){
			resultList.add(Users);
			Users = new ArrayList<>();
		}
	}
	return resultList;
}

执行方法

JPA

Mybatis-plus

saveAll(),懒得贴代码了,后续有源码分析

Mybatis

JDBC

image.png

测试结果

时间总览

JPA用时

entityManager用时

Mybatis用时

JDBCTemplate用时

多线程用时

以上就是测试各框架和技术栈的运行结果,多线程效率最高,其次是JDBC和entityManager,再是mybatis,spring-datajpa 最慢,mybatis-plus开启rewriteBatchedStatements=true,跟mybatis是一样的,后续分析源码

源码分析

spring-datajpa源码

为何datajpa效率如此低下呢,这里提供部分源码,saveAll()方法其实也是一个个去save,只是我们不用自己写循环,源码用Iterator迭代器获取next元素去一个个save,并且开启事务,效率十分低下,暂无设置修改

image.png

mybatis-plus源码

可以看到这里还是一条一条插入,但是开启rewriteBatchedStatements=true参数后,允许批量插入

entityManager和jdbcTemplate

这俩都是javax提供的接口,sql语句是我们自己拼接的,省去了框架处理的时间

多线程

多线程的处理速度很快,但并没有想象中那么快,为什么呢?数据库不一定会用多线程处理插入

总结

从框架运行速度来说,mybatis >= mybatis-plus >> datajpa 但是无论是什么框架,都会有数据处理,会消耗时间,从这点来说,不可能比得上entityManager和jdbcTemplate直接执行sql语句。 不过我们这仅仅是单表的插入,是比较纯净的环境,如果是生产环境中同时存在读和写,以及多表联动呢,我们下次再来揭晓。 欢迎广大开发好友一起探讨技术