背景
最近在做一个类似于综合报表之类的东西,需要查询所有的记录(数据库记录有限制),大概有1W条记录。该报表需要三个表的数据,也就是根据这 1W 个 ID 去执行查询三次数据库,其中,有一条查询 SQL 是自己写,其他两条是根据别人提供的接口进行查询。
刚开始的时候,没有多想,直接使用 in
进行查询,使用 Mybatis
的 foreach
语句;项目中使用的是 jsonrpc
来请求数据,在测试的时候,发现老是请求不到数据,日志抛出的是 jsonrpc
超时异常,继续查看日志发现,是被阻塞在上面的三条SQL查询中。
在以前分析 Mybatis 的源码的时候,了解到,Mybatis 的 foreach
会有性能问题,所以改了下 SQL,直接在代码中拼接SQL,然后在 Mybatis
中直接使用 #
来获取,替换 class 测试了下,果然一下子就能查询出数据。
前提
这里先不考虑使用 in
好不好,如何去优化 in
,如何使用 exists
或 inner join
进行代替等,这里就只是考虑使用了 in
语句,且使用了 Mybatis
的 foreach
语句进行优化,其实 foreach
的优化很简单,就是把 in 后面的语句在代码里面拼接好,在配置文件中直接通过 #{xxx}
或 ${xxx}
当作字符串直接使用即可。
测试
在分析 foreach
源码之前,先构造个数据来看看它们的区别有多大。
建表语句:
CREATE TABLE person
(
id int(11) PRIMARY KEY NOT NULL,
name varchar(50),
age int(11),
job varchar(50)
);
插入 1W 条数据:
POJO 类:
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Person implements Serializable {
private int id;
private String name;
private String job;
private int age;
}
方式一
通过原始的方式,使用 foreach 语句:
1. 在 dao 里面定义方法:
List<Person> queryPersonByIds(@Param("ids") List<Integer> ids);
2. 配置文件SQL:
<select id="queryPersonByIds" parameterType="list" resultMap="queryPersonMap">
select * from person where 1=1
<if test="ids != null and ids.size() > 0">
and id in
<foreach collection="ids" item="item" index="index" separator="," open="(" close=")">
#{item}
</foreach>
</if>
</select>
3. 执行 main 方法:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:spring-mybatis.xml" })
public class MainTest {
@Autowired
private IPersonService personService;
@Test
public void test(){
// 构造 1W 个 ID
List<Integer> ids = new ArrayList<>();
for (int i = 1; i <= 10000; i++) {
ids.add(i);
}
long start = Syst