Skip to content

MySQL 查询执行过程

当希望 MySQL 能够以更高的性能运行查询时,最好的办法就是弄清楚 MySQL 是如何优化和执行查询的。

当向 MySQL 发送一个请求的时候,MySQL 到底做了些什么:

  1. 客户端发送一条查询给服务器。

  2. 服务器先检查查询缓存,如果命中了缓存,则立刻返回存储在缓存中的结果。否则进入下一阶段。

  3. 服务器端进行 SQL 解析、预处理,再由优化器生成对应的执行计划。

    • 首先,MySQL 通过关键字将 SQL 语句进行解析,并生成一颗对应的“解析树”。MySQL 解析器将使用 MySQL 语法规则验证和解析查询。例如,它将验证是否使用错误的关键字,或者使用关键字的顺序是否正确等,再或者它还会验证引号是否能前后正确匹配。
    • 预处理器则根据一些 MySQL 规则进一步检查解析树是否合法,例如,这里将检查数据表和数据列是否存在,还会解析名字和别名,看看它们是否有歧义。下一步预处理器会验证权限。
    • 现在语法树被认为是合法的了,并且由优化器将其转化成执行计划。一条查询可以有很多种执行计划,最后都返回相同的结果。优化器的作用就是找到这其中最好的执行计划。
  4. MySQL 根据优化器生成的执行计划,调用存储引擎的 API 来执行查询。

  5. 将结果返回给客户端。如果查询可以被缓存,那么 MySQL 在这个阶段也会将结果存放到查询缓存中。

扩展:

1. 分解关联查询的好处?

  • 对 MySQL 的查询缓存来说,如果关联中的某个表发生了变化,那么就无法使用查询缓存了,而拆分后,如果某个表很少改变,那么基于该表的查询就可以重复利用查询缓存结果了。
  • 将查询分解后,执行单个查询可以减少锁的竞争。

2. 优化 LIMIT 分页。

  • 在偏移量非常大的时候(翻页到非常靠后的页面),例如可能是 LIMIT 10000,20 这样的查询,这时 MySQL 需要查询 10020 条记录然后只返回最后 20 条,前面 10000 条记录都将被抛弃。
  • LIMIT 和 OFFSET 的问题,其实是 OFFSET 的问题,它会导致 MySQL 扫描大量不需要的行然后再抛弃掉。如果可以记录上次取数据的位置,那么下次就可以直接从该书签记录的位置开始扫描,这样就可以避免使用 OFFSET。

    mysql> SELECT * FROM sakila.rental ORDER BY rental_id DESC LIMIT 20;
    
    // 假设上面的查询返回的是主键为 16049 到 16030 的记录,那么下一页查询就可以从 16030 这个点开始:        
    mysql> SELECT * FROM sakila.rental WHERE rental_id < 16030 ORDER BY rental_id DESC LIMIT 20;
    
    * 该技术的好处是无论翻页到多么后面,其性能都会很好。 
    

Post a Comment

Your email is never published nor shared. Required fields are marked *
*
*