大家好,我是水滴~~
上一篇我们讲到通到优化数据访问,来解决慢查询问题,这是解决慢查询的基础。但有时我们的查询过于复杂,导致查询速度慢,我们不得不重构查询。今天就来讲下重构查询的几种方式。
🌲 一、分解复杂查询
重构查询时,有一个比较好的技巧是:将一个复杂的查询,分解成多个简单的查询。
在传统的实现中,总是想让数据库层完成尽可能多的工作,即通过一次查询得出想要的结果,这会让我们的查询变得复杂。之所有这样想逻辑,一般是认网络通信、查询解析和优化是一件代价很高的事情。
但是这样的想法对于 MySQL 并不适用,MySQL 从设计上让连接和断开连接都很轻量级,在返回一个小的查询结果方面很高效。
MySQL 内部每秒能够扫描内存中上百万行数据,相比之下,MySQL 响应数据给客户端就慢得多了。在条件相同的情况下,越少的查询性能就越好。所以将一个大查询分解为多个小查询是很有必要的。
🌲 二、切分大查询
有时候对于一个大查询我们需要“分而治之”,将大查询切分成小查询,每个小查询的功能完全一样,只完成一小部分,每次只返回一小部分查询结果。
批量删除旧的数据就是一个很好的例子。在定期清除大量数据时,如果用一个大的语句一次性完成的话,则可能需要一次锁住很多数据、占满整个事务日志、耗尽系统资源、阻塞很多小的但重要的查询。可以将一个大的delete
语句切分成多个较小的语句,可以尽可能小地影响 MySQL 的性能,同时还能减少 MySQL 复制的延迟。
🌲 三、分解关联查询
很多高性能的应用都会对关联查询进行分解。即对每一个表进行一次单表查询,然后将结果在应用程序中进行关联。
例如,下面语句是查出“软件工程2101班”的所有学生:
SELECT
s.*
FROM
class c
JOIN class_student_rel cs ON c.id= cs.class_id
JOIN student s ON s.id = cs.student_id
WHERE
c.name= '软件工程2101班';
可以将该关联查询分解成两个单表查询:
select id from class where name= '软件工程2101班';
select student_id from class_student_rel where class_id = 1;
select * from student where id in (1, 2, 3, 4, 5, 6);
为什么要这样做呢?乍一看,这样做并没有什么好处,本来是一条查询,这里却需要变成多条查询,返回的结果又是一模一样的。
事实上,分解关联查询有如下优势:
- 让缓存的效率更高。许多应用程序可以方便地缓存单表查询对应的结果对象。例如上面查询中的
class
已经被缓存了,那么应用程序就可以路过第一个查询。 - 将查询分解后,执行单个查询可以减少锁的竞争。
- 在应用层做关联,可以更容易对数据库进行拆分,更容易做到高性能和可扩展。
- 查询本身效率也可能会有所提升。使用了
in()
代替关联查询,可以让 MySQL 按照ID
顺序进行查询,这比随机的关联更高效。 - 可以减少冗余记录的查询。在应用层做关联查询,意味着对于某条记录应用只需要查询一次,而在数据库中做关联查询,则可能需要重复地访问一部分数据。
- 更进一步,这样做相当于在应用中实现了哈希关联,而不是使用 MySQL 的嵌套循环关联,会提高效率。