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

设计查询的时候,一个需要考虑的重要问题是,是否需要将一个复杂的查询分成多个简单的查询

在传统实现中,总是强调需要数据库层完成尽可能多的工作,这样做的逻辑在于以前人们总是认为网络通信、查询解析和优化是一件代价很高的事情

但是这样的想法对于 MySQL 并不适用,因为:

  • MySQL 从设计上让连接和断开连接都很轻量,在返回一个小的查询结果方面很高效

  • 现代的网络速度比以前要快很多,能在很大程度上降低延迟。

  • 在某些版本的 MySQL 中,即使在一台通用服务器上,也能够运行每秒超过 10 万次的简单查询,即使是一个千兆网卡也能轻松满足每秒超过 2000 次的查询。

所以运行多个小查询现在已经不是大问题了。

在 MySQL 内部,每秒能够扫描内存中上百万行的数据,相比之下,MySQL 响应数据给客户端就慢得多了。在其他条件都相同的时候,使用尽可能少的查询当然是更好的。但是有时候,将一个大查询分解为多个小查询是很有必要的。别害怕这样做,好好衡量一下这样做是不是会减少工作量。

切分查询

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

删除旧的数据就是一个很好的例子。定期清除大量数据时,如果用一个大的语句一次性完成的话,则可能需要一次锁住很多数据、占满整个事务日志、耗尽系统资源、阻塞很多小的但重要的查询。将一个大的 DELETE 语句切分成多个较小的查询可以尽可能小地影响 MySQL 的性能,同时还可以降低 MySQL 复制的延迟。

一次删除一万行数据一般来说是一个比较高效而且对服务器影响最小的做法(如果是事务型引擎,很多时候小事务能够更高效)

同时,如果每次删除数据后,都暂停一会儿再做下一次删除,也可以将服务器上原本一次性的压力分散到一个很长的时间段中,可以大大降低对服务器的影响,还可以大大减少删除时锁的持有时间。

分解联接查询

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

用分解联接查询的方式重构查询有如下优势:

  • 让缓存的效率更高。许多应用程序可以方便地缓存单表查询对应的结果对象。

  • 将查询分解后,执行单个查询可以减少锁的竞争

  • 查询本身的效率也可能会有所提升。例如,使用 IN() 代替联接查询,可以让 MySQL 按照 ID 顺序进行查询,这可能比随机的联接要更高效。

  • 可以减少对冗余记录的访问。在应用层做联接查询,意味着对于某条记录应用只需要查询一次,而在数据库中做联接查询,则可能需要重复地访问一部分数据。从这点看,这样的重构还可能会减少网络和内存的消耗。

最后更新于