一.第一个解答来源于《高性能Mysql》中的回答
很多高性能的应用都会对关联查询进行分解。简单地,可以对每个表进行一次单表查询,然后将结果在应用程序中进行关联。
例如,下面这个查询:
select * from tag
join tag_post on tag_post.tag_id=tag.id
join post on tag_post.post_id=post.id
where tag.tag=’mysql’;
可以分解成下面这些查询来代替:
Select * from tag where tag=’mysql’;
Select * from tag_post where tag_id=1234;
Select * from post where id in(123,456,567,9989,8909);
事实上,用分解关联查询的方式重构查询具有如下优势:(高并发、高性能的应用中,一般建议使用单表查询)
- 让缓存的效率更高。
许多应用程序可以方便地缓存单表查询对应的结果对象。另外对于MySQL的查询缓存来说,如果关联中的某个表发生了变化,那么就无法使用查询缓存了,而拆分后,如果某个表很少改变,那么基于该表的查询就可以重复利用查询缓存结果了。
-
将查询分解后,执行单个查询可以减少锁的竞争。
-
在应用层做关联,可以更容易对数据库进行拆分,更容易做到高性能和可扩展。
-
查询本身效率也可能会有所提升。
-
可以减少冗余记录的查询。
-
更进一步,这样做相当于在应用中实现了哈希关联,而不是使用MySQL的嵌套环关联,某些场景哈希关联的效率更高很多。
-
单表查询有利于后期数据量大了分库分表,如果联合查询的话,一旦分库,原来的sql都需要改动。
-
上次看到某个CTO技术分享,公司规定底层禁止用join联合查询。数据大的时候确实慢。
-
联合查询或许确实快,但是mysql的资源通常比程序代码的资源紧张的多。
二.单表多次查询和多表联合查询效果对比
在同等条件下,以用户、角色、及用户角色关联表为例。
select * from sys_user where user_name='admin';
select * from sys_user_role where user_id ='1';
select * from sys_role where role_id ='1';
select * from sys_user su LEFT JOIN sys_user_role sur ON su.user_id = sur.user_id
LEFT JOIN sys_role sr ON sur.role_id = sr.role_id
where user_name='admin';
>>>>>>>>>
[SQL]select * from sys_user where user_name='admin';
受影响的行: 0
时间: 0.004s
[SQL]
select * from sys_user_role where user_id ='1';
受影响的行: 0
时间: 0.002s
[SQL]
select * from sys_role where role_id ='1';
受影响的行: 0
时间: 0.004s
[SQL]
select * from sys_user su LEFT JOIN sys_user_role sur ON su.user_id = sur.user_id
LEFT JOIN sys_role sr ON sur.role_id = sr.role_id
where user_name='admin';
受影响的行: 0
时间: 0.001s
查询包装单位
select unit_name from erp_unit where unit_id=1;
select unit_name from erp_unit where unit_id=2;
select unit_name from erp_unit where unit_id=3;
SELECT
eus.unit_name AS smallName,
eum.unit_name AS middleName,
eub.unit_name AS bigName
FROM
erp_goods_detail egd
LEFT JOIN erp_unit eus ON egd.small_unit_id = eus.unit_id
LEFT JOIN erp_unit eum ON egd.middle_unit_id = eum.unit_id
LEFT JOIN erp_unit eub ON egd.big_unit_id = eub.unit_id
WHERE
egd.goods_id = '1357597885043163138';
>>>>>>>>>>>>>>>>>>>>>
[SQL]select unit_name from erp_unit where unit_id=1;
受影响的行: 0
时间: 0.002s
[SQL]
select unit_name from erp_unit where unit_id=2;
受影响的行: 0
时间: 0.002s
[SQL]
select unit_name from erp_unit where unit_id=3;
受影响的行: 0
时间: 0.002s
[SQL]
SELECT
eus.unit_name AS smallName,
eum.unit_name AS middleName,
eub.unit_name AS bigName
FROM
erp_goods_detail egd
LEFT JOIN erp_unit eus ON egd.small_unit_id = eus.unit_id
LEFT JOIN erp_unit eum ON egd.middle_unit_id = eum.unit_id
LEFT JOIN erp_unit eub ON egd.big_unit_id = eub.unit_id
WHERE
egd.goods_id = '1357597885043163138';
受影响的行: 0
时间: 0.002s
看一下代码中的运行时间
@Service
public class AServiceImpl implements AService {
@Autowired
private UnitMapper unitMapper;
@Override
public void testA() {
singleSearch();
allSearch();
}
private void singleSearch() {
Long start = System.currentTimeMillis();
for (int i = 0; i < 10; i++) {
unitMapper.selectNameByUnitId((long) 1);
unitMapper.selectNameByUnitId((long) 2);
unitMapper.selectNameByUnitId((long) 3);
}
Long end = System.currentTimeMillis();
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>" + (end - start));
}
private void allSearch() {
Long start = System.currentTimeMillis();
for (int i = 0; i < 10; i++) {
unitMapper.searchAll();
}
Long end = System.currentTimeMillis();
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>" + (end - start));
}
}
singleSearch方法的总耗时4533,allSearch方法的总耗时1378
在遍历中每个查询方法的用时对比
singleSearch(单位ms):[4、4、3、4、4、3、5、3、3、4、3、7、4、5、19、4、4、3、3、3、4、4、11、4、5、4、4、4、3、220]
allSearch(单位ms):[5、4、4、4、5、4、4、4、5、9]
还是上边的方法,我们将遍历次数调整到1000
@Service
public class AServiceImpl implements AService {
@Autowired
private UnitMapper unitMapper;
@Override
public Map<String, Long> testA() {
Long single = singleSearch();
Long all = allSearch();
Map map = new HashMap();
map.put("single", single);
map.put("all", all);
return map;
}
private Long singleSearch() {
Long start = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
unitMapper.selectNameByUnitId((long) 1);
unitMapper.selectNameByUnitId((long) 2);
unitMapper.selectNameByUnitId((long) 3);
}
Long end = System.currentTimeMillis();
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>" + (end - start));
return end - start;
}
private Long allSearch() {
Long start = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
unitMapper.searchAll();
}
Long end = System.currentTimeMillis();
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>" + (end - start));
return end - start;
}
}
输出结果
{"code":200,"success":true,"data":{"single":362416,"all":7388},"msg":"操作成功"}
网友评论