MyBatis+Spring SQL效率测试报告
1. 数据库结构
2. insert 测试
insert 的测试包括
1) 批量拼接values()插入
2) 有事务for循环插入
3) 无事务for循环插入
测试 SQL:
- <!-- 普通 insert -->
- <insert id="insert"
- parameterType="com.qunar.mybatistask.bean.Post"
- keyProperty="id">
- <![CDATA[
- insert into post (
- title,
- content,
- author,
- status,
- created
- ) values (
- #{title},
- #{content},
- #{author},
- #{status},
- #{created}
- )
- ]]>
- </insert>
- <!-- 拼接values insert -->
- <insert id="batchInsert"
- parameterType="java.util.List">
- <![CDATA[
- insert into post (
- title,
- content,
- author,
- status,
- created
- ) values
- ]]>
- <foreach collection="list" item="post" separator=",">
- (
- #{post.title},
- #{post.content},
- #{post.author},
- #{post.status},
- #{post.created}
- )
- </foreach>
- </insert>
测试代码:
service
- /**
- * 批量拼接VALUES() insert
- *
- * @param postList
- * @return
- */
- @Override
- @Transactional(propagation = Propagation.REQUIRED)
- public int batchInsert(List<Post> postList) {
- int singleNum = 1000;
- int affectedRows = 0;
- for (int i = 0; i < Math.ceil((double)(postList.size() / singleNum)); i++) {
- affectedRows += sqlSession.insert("post.batchInsert", postList.subList(i * singleNum, (i + 1) * singleNum));
- }
- return affectedRows;
- }
- /**
- * 事务内循环insert
- *
- * @param postList
- * @return
- */
- @Override
- @Transactional(propagation = Propagation.REQUIRED)
- public int createList(List<Post> postList) {
- int affectedRows = 0;
- for (Post post : postList) {
- affectedRows += sqlSession.insert("post.insert", post);
- }
- return affectedRows;
- }
test case:
- /**
- * 批量插入效率测试
- *
- * Method: batchInsert(List<Post> postList)
- *
- */
- @Test
- public void testBatchInsert() throws Exception {
- List<Post> postList = Lists.newArrayList();
- for (int i = 0; i < 10000; i++) {
- Post post = new Post();
- post.setAuthor("test");
- post.setContent("test");
- post.setCreated(new Date());
- post.setTitle("test");
- post.setStatus(PostStatus.NORMAL);
- postList.add(post);
- }
- // 批量拼接SQL插入
- long start = System.nanoTime();
- int affectedRows = postService.batchInsert(postList);
- double duration = System.nanoTime() - start;
- System.out.format("batch: %.2f\n", duration / 1.0e9);
- System.out.println("affected rows: " + affectedRows);
- // 事务内循环插入
- start = System.nanoTime();
- affectedRows = postService.createList(postList);
- duration = System.nanoTime() - start;
- System.out.format("transaction: %.2f\n", duration / 1.0e9);
- System.out.println("affected rows: " + affectedRows);
- // 无事务直接循环插入
- start = System.nanoTime();
- affectedRows = 0;
- for (Post post : postList)
- affectedRows += postService.create(post);
- duration = System.nanoTime() - start;
- System.out.format("simple: %.2f\n", duration / 1.0e9);
- System.out.println("affected rows: " + affectedRows);
- }
结果
batch: 1.44
affected rows: 10000
transaction: 2.87
affected rows: 10000
simple: 77.57
affected rows: 10000
总结:
排行
1) 使用拼接的手段,这种插入其实就是batch,只不过这是手动batch
2) 使用事务循环插入,相对于无事务快很多的原因大概是数据库连接和事务开启的次数
3) 无事务循环插入, 我想应该没人这么写
2. 单表循环查询与拼接in查询测试
SQL
- <select id="selectById" parameterType="int" resultType="com.qunar.mybatistask.bean.Post">
- <![CDATA[
- select
- id,
- title,
- content,
- author,
- status,
- created
- from
- post
- where
- id = #{id}
- ]]>
- </select>
- <!-- 拼接where in条件查询 -->
- <select id="selectIn" parameterType="java.util.List" resultType="com.qunar.mybatistask.bean.Post">
- <![CDATA[
- select
- id,
- title,
- content,
- author,
- status,
- created
- from
- post
- where
- id in
- ]]>
- <foreach collection="list" item="id" open="(" close=")" separator=",">
- #{id}
- </foreach>
- </select>
Service
- @Override
- public Post selectById(int id) {
- return sqlSession.selectOne("post.selectById", id);
- }
- @Override
- public List<Post> selectByIds(List<Integer> ids) {
- List<Post> postList = Lists.newArrayList();
- int singleNum = 1000;
- int start;
- int end;
- for (int i = 0; i < Math.ceil(((double)ids.size() / (double)singleNum)); i++) {
- start = i * singleNum;
- end = (i + 1) * singleNum;
- end = end > ids.size() ? ids.size() : end;
- List<Post> result = sqlSession.selectList("post.selectIn", ids.subList(start, end));
- postList.addAll(result);
- }
- return postList;
- }
test case
- /**
- * 使用IN查询效率测试
- *
- * @throws Exception
- */
- @Test
- public void testInSelect() throws Exception {
- List<Integer> ids = Lists.newArrayList();
- for (int i = 1; i < 10000; i++) {
- ids.add(i);
- }
- // in 查询
- long start = System.nanoTime();
- List<Post> list = postService.selectByIds(ids);
- double duration = System.nanoTime() - start;
- System.out.format("in select: %.2f\n", duration / 1.0e9);
- System.out.println("list size: " + list.size());
- // 循环查询
- list = Lists.newArrayList();
- start = System.nanoTime();
- for (int id : ids)
- list.add(postService.selectById(id));
- duration = System.nanoTime() - start;
- System.out.format("simple select: %.2f\n", duration / 1.0e9);
- System.out.println("list size: " + list.size());
- }
结果
in select: 0.55
list size: 9999
simple select: 6.24
list size: 9999
总结:
我想应该没人会用for循环去做查询吧
3. 多表联结查询, join, form 2个table, in, exists 比较
SQL
- <!-- 用于循环查询 -->
- <select id="selectAll" resultType="com.qunar.mybatistask.bean.Comment">
- <![CDATA[
- select
- cmt.id as id,
- cmt.post_id as postId,
- cmt.content as content
- from
- cmt
- ]]>
- </select>
- <!-- join 查询 -->
- <select id="selectJoin" resultType="com.qunar.mybatistask.bean.Comment">
- <![CDATA[
- select
- cmt.id as id,
- cmt.post_id as postId,
- cmt.content as content
- from
- cmt
- join
- post
- on
- post.id = cmt.post_id
- ]]>
- </select>
- <!-- from 2个table -->
- <select id="selectTowTable" resultType="com.qunar.mybatistask.bean.Comment">
- <![CDATA[
- select
- cmt.id as id,
- cmt.post_id as postId,
- cmt.content as content
- from
- cmt, post
- where cmt.post_id = post.id
- ]]>
- </select>
- <!-- in 联表查询 -->
- <select id="selectIn" resultType="com.qunar.mybatistask.bean.Comment">
- <![CDATA[
- select
- cmt.id as id,
- cmt.post_id as postId,
- cmt.content as content
- from
- cmt
- where
- cmt.post_id
- in
- (
- select
- post.id
- from
- post
- )
- ]]>
- </select>
- <!-- exists 联表查询 -->
- <select id="selectExists" resultType="com.qunar.mybatistask.bean.Comment">
- <![CDATA[
- select
- cmt.id as id,
- cmt.post_id as postId,
- cmt.content as content
- from
- cmt
- where
- exists
- (
- select
- post.id
- from
- post
- where
- post.id = cmt.id
- )
- ]]>
- </select>
service
- @Override
- public List<Comment> selectTwoTable() {
- return sqlSession.selectList("comment.selectTowTable");
- }
- @Override
- public List<Comment> selectJoin() {
- return sqlSession.selectList("comment.selectJoin");
- }
- @Override
- public List<Comment> selectIn() {
- return sqlSession.selectList("comment.selectIn");
- }
- @Override
- public List<Comment> selectExists() {
- return sqlSession.selectList("comment.selectExists");
- }
- @Override
- public List<Comment> selectAll() {
- return sqlSession.selectList("comment.selectAll");
- }
test case
- /**
- * 测试JOIN查询效率
- *
- */
- @Test
- public void testJoinSelect() {
- // join 查询
- long start = System.nanoTime();
- List<Comment> list = commentService.selectJoin();
- double duration = System.nanoTime() - start;
- System.out.format("join select: %.2f\n", duration / 1.0e9);
- System.out.println("list size: " + list.size());
- // From 两个表查询
- start = System.nanoTime();
- list = commentService.selectTwoTable();
- duration = System.nanoTime() - start;
- System.out.format("2 table select: %.2f\n", duration / 1.0e9);
- System.out.println("list size: " + list.size());
- // in多表查询
- start = System.nanoTime();
- list = commentService.selectIn();
- duration = System.nanoTime() - start;
- System.out.format("in multi table select: %.2f\n", duration / 1.0e9);
- System.out.println("list size: " + list.size());
- // exists多表查询
- start = System.nanoTime();
- list = commentService.selectExists();
- duration = System.nanoTime() - start;
- System.out.format("exists multi table select: %.2f\n", duration / 1.0e9);
- System.out.println("list size: " + list.size());
- // 分次查询, 太慢了, 忽略这种方法的测试吧
- // start = System.nanoTime();
- // list = commentService.selectAll();
- // for (Comment comment : list) {
- // postService.selectById(comment.getPostId());
- // }
- // duration = System.nanoTime() - start;
- // System.out.format("separate select: %.2f\n", duration / 1.0e9);
- // System.out.println("list size: " + list.size());
- }
结果
join select: 2.44
list size: 210000
2 table select: 2.26
list size: 210000
in multi table select: 2.03
list size: 210000
exists multi table select: 2.35
list size: 210000
总结:
21W条数据下效率都差不多,而且我们一般会使用limit去限制查询的条数,所以应该他们的效率差距应该很小,我通过观察explain发现实际上join和from 2个table的方式的查询的执行计划是一模一样的,而in和exists的执行计划也是一模一样的
这里的表结构相对简单,也基本能用上索引 post_id 和 post.id 这些primary, 具体更加复杂的情况也许会影响这几种查询方式的执行计划, 才会体现出他们直接的差距, 当然我也相信他们执行的效率很大程度上是决定于mysql的优化器的优化策略,而这个优化策略很难人为的去判断,所以也不好说
MyBatis+Spring SQL效率测试报告的更多相关文章
- Spring Boot 集成 MyBatis和 SQL Server实践
概 述 Spring Boot工程集成 MyBatis来实现 MySQL访问的示例我们见过很多,而最近用到了微软的 SQL Server数据库,于是本文则给出一个完整的 Spring Boot + M ...
- 【spring boot】【mybatis】spring boot中mybatis打印sql语句
spring boot中mybatis打印sql语句,怎么打印出来?[参考:https://www.cnblogs.com/sxdcgaq8080/p/9100178.html] 在applicati ...
- 【记录】spring/springboot 配置mybatis打印sql
======================springboot mybatis 打印sql========================================== 方式 一: ##### ...
- 集成框架 javaweb开发平台ssmy_m(生成代码) java struts2 mybatis spring maven jquery
网页地址 http://blog.csdn.net/lpy3654321/article/details/31841573 项目设想,在项目开发中,我们的开发者大多数时间都在反复开发 相同的keywo ...
- MyBatis学习(一)、MyBatis简介与配置MyBatis+Spring+MySql
一.MyBatis简介与配置MyBatis+Spring+MySql 1.1MyBatis简介 MyBatis 是一个可以自定义SQL.存储过程和高级映射的持久层框架.MyBatis 摒除了大部分的J ...
- MyBatis详解 与配置MyBatis+Spring+MySql
MyBatis 是一个可以自定义SQL.存储过程和高级映射的持久层框架.MyBatis 摒除了大部分的JDBC代码.手工设置参数和结果集重获.MyBatis 只使用简单的XML 和注解来配置和映射基本 ...
- Maven+druid+MyBatis+Spring+Oracle+Dubbo开发环境搭建
1.开发工具使用: MyEclipse或Eclipse,数据库使用Oracle.需要用到的软件有Zookeeper(注册中心),Tomcat(Web容器)和Maven(包管理). 2.初始环境配置: ...
- mybatis 打印sql 语句
拦截器 package com.cares.asis.mybatis.interceptor; import java.text.DateFormat; import java.util.Date; ...
- 一、MyBatis简介与配置MyBatis+Spring+MySql
//备注:该博客引自:http://limingnihao.iteye.com/blog/106076 1.1MyBatis简介 MyBatis 是一个可以自定义SQL.存储过程和高级映射的持久层框架 ...
随机推荐
- hdoj2159 FATE(完全背包)
题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=2159 思路 每种怪都有无限个,所以使用完全背包来解决.这题比普通完全背包多了一个条件,就是杀怪的个数不 ...
- JS日期、时间 格式化转换方法
Date.prototype.format = function(fmt) { var o = { "M+" : this.getMonth()+1, //月份 "d+& ...
- ACM训练计划建议(转)
ACM训练计划建议 From:freecode# Date:2015/5/20 前言: 老师要我们整理一份训练计划给下一届的学弟学妹们,整理出来了,费了不少笔墨,就也将它放到博客园上供大家参考. 菜 ...
- 【工具】获取pojo类属性,并写入表格
1.添加依赖 <!-- https://mvnrepository.com/artifact/org.apache.poi/poi --> <dependency> <g ...
- React Native踩坑之The SDK directory 'xxxxx' does not exist
相信和我一样,自己摸索配置环境的过程中,第一次配,很可能就遇到了这个比较简单地错误,没有配置sdk环境 解决办法 在电脑,系统环境变量中,添加一个sdk的环境变量 uploading-image-95 ...
- 五、django rest_framework源码之版本控制剖析
1 绪论 Djangorest_framework的版本控制允许用户更改不同客户端之间的行为,且提供了许多不同的版本控制方案.版本控制由传入的客户端请求确定,可以基于请求URL,也可以基于请求标头. ...
- 数据库中drop、delete与truncate的区别
数据库中drop.delete与truncate的区别 drop直接删掉表: truncate删除表中数据,再插入时自增长id又从1开始 :delete删除表中数据,可以加where字句. (1) D ...
- luoguP4555 [国家集训队]最长双回文串 manacher算法
不算很难的一道题吧.... 很容易想到枚举断点,之后需要处理出以$i$为开头的最长回文串的长度和以$i$为结尾的最长回文串的长度 分别记为$L[i]$和$R[i]$ 由于求$R[i]$相当于把$L[i ...
- Needed Learning(Updating)
决定把掌握不熟练或是模型见的少的知识点在这里列一列 希望能在自己AFO前成功get技能点吧…… 优先级:动态规划-分治-字符串-图论-数据结构-数学-计算几何-其它 动态规划 1.四边形不等式优化 2 ...
- 【推导】【贪心】Codeforces Round #472 (rated, Div. 2, based on VK Cup 2018 Round 2) D. Riverside Curio
题意:海平面每天高度会变化,一个人会在每天海平面的位置刻下一道痕迹(如果当前位置没有已经刻划过的痕迹),并且记录下当天比海平面高的痕迹有多少条,记为a[i].让你最小化每天比海平面低的痕迹条数之和. ...