mysql数据库查询,为什么第一次查询比较慢(走了索引),后面查询都比较快呢?

102 阅读2分钟

知其然要知其所以然,探索每一个知识点背后的意义,你知道的越多,你不知道的越多,一起学习,一起进步,如果文章感觉对您有用的话,关注、收藏、点赞,有困惑的地方请评论,我们一起交流!

一、核心原因分析

1. 索引页未命中 Buffer Pool

  • 首次查询

    • 索引页(Index Page)可能未加载到内存,需从磁盘读取(随机 I/O,速度较慢)。
    • 即使走索引,仍需回表读取数据页,同样可能触发磁盘 I/O。
  • 后续查询

    • 索引页和数据页均被缓存到 InnoDB Buffer Pool,直接从内存读取(速度提升 100 倍以上)。
  • 验证方法

    -- 查看索引页缓存命中率
    SELECT 
      index_name,
      rows_read,
      rows_index_first
    FROM information_schema.table_statistics 
    WHERE table_schema = 'your_database' AND table_name = 'your_table';
    

2. 执行计划生成开销

  • 首次查询

    • MySQL 需解析 SQL、优化执行计划(如索引选择、连接方式),这一过程可能耗时。
  • 后续查询

    • 相同 SQL 的执行计划被缓存(通过 Prepared Statements 或 Statement Cache),减少重复优化。

3. 操作系统缓存预热

  • 首次查询

    • 磁盘文件(如 .ibd 数据文件)未被操作系统的 Page Cache 缓存,需从物理磁盘读取。
  • 后续查询

    • 操作系统已缓存部分文件,减少磁盘 I/O。

二、其他可能原因

1. 锁竞争

  • 首次查询

    • 若涉及锁(如行锁、表锁),可能因等待锁释放而延迟。
  • 后续查询

    • 锁已释放或竞争减少。

2. 网络与连接开销

  • 首次查询

    • 需建立数据库连接、验证权限等。
  • 后续查询

    • 复用已建立的连接(如长连接),减少网络延迟。

三、优化建议

1. 扩大 Buffer Pool 内存

  • 配置

    # my.cnf
    innodb_buffer_pool_size = 4G  # 建议占总内存的 70%
    
  • 效果:确保索引和常用数据页常驻内存。

2. 使用覆盖索引

  • 场景:查询字段全部包含在索引中,避免回表。

  • 示例

    CREATE INDEX idx_covering ON table (col1, col2, col3);
    SELECT col1, col2, col3 FROM table WHERE col1 = 1;
    

3. 优化执行计划

  • 分析工具

    EXPLAIN SELECT ...;  -- 查看索引使用情况
    SHOW PROFILES;       -- 分析查询各阶段耗时
    
  • 优化方向

    • 避免 Using filesortUsing temporary 等低效操作。

4. 监控缓存命中率

  • 关键指标

    SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read_requests';
    SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_reads';
    
    • 命中率 = Innodb_buffer_pool_read_requests / (Innodb_buffer_pool_read_requests + Innodb_buffer_pool_reads),建议高于 95%。

四、总结

首次查询慢的核心原因是索引页和数据页未命中缓存执行计划生成开销。优化重点在于:

  1. 提升 Buffer Pool 容量,确保索引和数据常驻内存。
  2. 设计覆盖索引,减少回表次数。
  3. 监控执行计划,避免不必要的磁盘访问。