查询太慢?是不是应该考虑重构一下查询语句!

133 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第19天,点击查看活动详情

重构查询的方式

就是我们之前提到的一种优化方式,我们的SQL查询太慢,有时候是因为我们写的这个SQL太糟糕了。我们需要换一种方式去写SQL,但还是要返回一样的结果。

一个复杂的查询还是多个简单的查询

就如标题所言,我们在设计查询的时候,需要考虑的一个重要问题是:是否需要将一个复杂的查询变为多个简单的查询。

切分查询

有时候啊,我们需要把一个大查询“分而治之”,将大查询变为小查询,每个查询功能完全一样,只完成一小部分,每次只返回一小部分的结果。

你比如说,想要删除旧的数据,,如果说你用一个大的语句一次性完成的话 ,则可能需要一次性锁住很多数据,占满整个事务日志,耗尽系统资源,阻塞很多小的,但是重要的查询。所以将一个大的delete语句切分为多个较小的查询可以尽可能小地影响MySQL性能,同时还可以减少MySQL复制的延迟。

🍨你比如说下面这个例子:

DELETE FROM messages WHERE created < DATE_SUB(NOW(),INTERVAL 3 MONTH);

我们就可以使用下面同样的方法来解决:

rows_affected = 0
do {
rows_affected = do_query(
"DELETE FROM messages WHERE created < DATE_SUB(NOW(),INTERVAL 3 MONTH) LIMIT 10000")    
} while rows_affected > 0

我们通过上面的语句,就实现了一次删除啊10000行数据这样一个限制,一次删除10000行数据是一个比较高效而且对服务器影响也是最小的做法。如果,每次删除数据后,都暂停一会再做下一次删除,这样也可以将服务器上原本一次性的压力分散到一个很长的时间段,也就大大的降低了对服务器的影响。

分解关联查询

很多高性能的应用都会对关联查询进行分解。简而言之就是,可以对每一个表进行一次单表查询,然后将结果在应用程序中进行关联。

你比如说说下面这个查询。

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';

我们可以分解成这样的查询来代替:

mysql> SELECT * FROM tag WHERE tag='mysql';
mysql> SELECT * FROM tag_post WHERE tag_id=1234;
mysql> SELECT * FROM post WHERE post.id in (123,456,567,9098,8904);

返回的结果一模一样,那么这样做有什么好处呢?

  • 让缓存的效率更高。许多应用程序可以方便的缓存单表查询对应的结果对象。你比如说呀,如果第一行的tag已经被缓存了,那么应用就可以跳过第一个查询了,
  • 我们分解之后,执行单个查询可以减少锁的竞争
  • 在应用层做关联,可以更加容易对数据库进行拆分,更加容易做到高性能和可扩展
  • 查询本身的效率也会提升,我们使用in代替关联查询,这比随机的关联是要更加高效的。
  • 可以减少冗余记录的查询。在应用层做关联査询,意味着对于某条记录应用只需要査询一次,而在数据库中做关联査询,则可能需要重复地访问一部分数据。从这点看,这样的重构还可能会减少网络和内存的消耗。