分析慢查询

分析慢查询是数据库性能优化的关键一环,一个慢查询可能导致应用响应延迟,用户体验下降,甚至拖垮整个系统。

开启慢查询日志

  • Mysql

    • my.cnf (或 my.ini) 配置中设置:

      slow_query_log = 1 # 开启慢查询日志
      slow_query_log_file = /var/log/mysql/mysql-slow.log # 日志文件路径
      long_query_time = 1 # 慢查询阈值,单位秒。表示查询时间超过1秒的记录。
      log_queries_not_using_indexes = 1 # 记录没有使用索引的查询 (可选,但非常有用)
      
    • 修改配置后重启 MySQL 服务。

    • 也可以在运行时通过 SQL 命令设置,但重启后会失效:

      SET GLOBAL slow_query_log = 'ON';
      SET GLOBAL slow_query_log_file = '/var/log/mysql/mysql-slow.log';
      SET GLOBAL long_query_time = 1;
      SET GLOBAL log_queries_not_using_indexes = 'ON';
      

定位慢查询语句

读取和分析慢查询日志是第一步

  • 手动查看日志
    • 直接打开日志文件查看,可以按时间或者执行时间排序
  • 使用日志分析工具
    • MySQL: mysqldumpslow 是 MySQL 官方提供的一个命令行工具,用于汇总和分析慢查询日志。
      • mysqldumpslow -s t -t 10 /var/log/mysql/mysql-slow.log:按总耗时排序,显示前10条慢查询。
      • mysqldumpslow -s c -t 10 /var/log/mysql/mysql-slow.log:按查询次数排序。
      • mysqldumpslow -s r -t 10 /var/log/mysql/mysql-slow.log:按返回行数排序。
    • Percona Toolkit (pt-query-digest): 更强大、更专业的慢查询日志分析工具,可以生成详细的报告,包括查询模板、执行次数、平均/最大/最小执行时间、锁等待时间、扫描行数等。
      • pt-query-digest /var/log/mysql/mysql-slow.log

理解EXPLAIN计划

一旦定位到具体的慢查询语句,下一步就是使用数据库的 EXPLAIN (或 EXPLAIN ANALYZEDESCRIBE) 命令来分析其执行计划。这是分析慢查询的核心步骤。

  • 什么是执行计划? 执行计划是数据库优化器为 SQL 查询生成的执行步骤蓝图。它展示了数据库如何访问表(全表扫描、索引扫描)、如何连接表(嵌套循环、哈希连接、合并连接)、如何排序、如何聚合等。

  • 如何使用EXPLAIN

    • 在慢查询语句前加上EXPLAIN关键字
    • mysql: explain select * from users where age > 30;
  • 执行计划的关键指标和解读

    MySQL EXPLAIN 输出的重要列:

    • id: select 查询的序列号,越大越优先执行。
    • select_type: 查询类型(SIMPLE, PRIMARY, SUBQUERY, UNION等)。
    • table: 正在访问的表。
    • partitions: 匹配记录所在的分区。
    • type: 连接类型/访问类型,这是最重要的指标之一,反映了表是如何被访问的。
      • system: 表只有一行,是 const 类型的一个特例。
      • const: 通过主键或唯一索引访问一行,非常快。
      • eq_ref: 主键或唯一索引等值查找,用于多表连接,非常快。
      • ref: 非唯一索引查找,返回匹配多行。
      • range: 索引范围扫描,比全索引扫描好。
      • index: 全索引扫描,比全表扫描快,但仍要扫描整个索引。
      • all: 全表扫描,通常是性能瓶颈,应尽量避免。
    • possible_keys: 可能使用的索引。
    • key: 实际使用的索引。
    • key_len: 使用的索引的长度。
    • ref: 哪些列或常量被用于查找索引列上的值。
    • rows: 估算的扫描行数,越少越好。
    • filtered: 表条件过滤出的行百分比。
    • Extra: 额外信息,非常重要,指示了优化器执行查询的额外细节。
      • Using filesort: 需要额外排序,通常可以优化。
      • Using temporary: 需要使用临时表,通常可以优化。
      • Using index: 覆盖索引,非常高效。
      • Using where: 使用了 WHERE 子句过滤数据。
      • Using join buffer: 使用了连接缓冲区。
      • Using index condition: 索引条件下推,MySQL 5.6+ 特性。

优化策略

  • sql优化
  • 索引优化
  • 数据库结构优化
    • 字段类型优化:选择最合适,最小的数据类型。如果一个字段只存储0-255,用 TINYINTINT 更好。
    • 范式与反范式: 适当进行反范式设计(数据冗余)以减少 JOIN 操作,但要权衡数据一致性。
    • 分区表 (Partitioning): 将大表分成更小的、可管理的部分,提高查询效率,尤其是在涉及时间范围查询时。
  • 硬件和系统配置优化:
    • 增加内存:提升缓存命中率,减少磁盘 I/O。
    • 使用更快的存储设备:SSD 替代 HDD。
    • 优化数据库配置参数:调整缓存大小 (如 MySQL 的 innodb_buffer_pool_size)、线程池、连接数等。
    • 升级 CPU。

慢查询分析流程图:

+-------------------+      +---------------------+      +---------------------+
| 1. 开启慢查询日志 |----->| 2. 定位慢查询语句   |----->| 3. 分析 EXPLAIN 计划|
|   - 设置阈值      |      |   - 查看日志        |      |   - 关键指标解读    |
|   - 配置持久化    |      |   - 使用分析工具    |      |   - 查找瓶颈        |
+-------------------+      +---------------------+      +---------------------+
         |                                                           |
         |                                                           V
         |                           +---------------------+      +-------------------+
         +--------------------------->| 5. 持续监控和迭代   |<-----| 4. 应用优化策略   |
                                     |   - 性能监控        |      |   - 索引优化      |
                                     |   - 定期审查        |      |   - SQL语句优化   |
                                     |   - 灰度/测试       |      |   - 结构/配置优化 |
                                     +---------------------+      +-------------------+

mysql优化,如何优化索引

对索引使用左或者左右模糊匹配

在mysql中,like模糊查询可能会导致性能问题,提别是当使用通配符%开头时,因为通常会导致全表扫描,也就是like %xx 或者 like %xxxx%这两种方式会造成索引失效

  • 前缀匹配:一般使用`like 'prefix%' 的形式,这种情况mysql能够利用索引

  • 对于一定需要匹配后缀的情况(即%sufix),可以创建一个辅助列存储反转字符串,并基于此列进行前缀匹配

    ALTER TABLE users ADD reversed_username VARCHAR(255); 
    UPDATE users SET reversed_username = REVERSE(username); 
    CREATE INDEX idx_reversed_username ON users(reversed_username);
    

计算(使用函数)

索引保存的是索引字段的原始值,而不是经过计算后的值,所以不能走索引

函数计算和表达式计算都不能走索引

//函数计算
select * from t_user where length(name)=6;

//表达式计算
select * from t_user where id + 1 = 10;

类型转换

数值类型禁止加引号,字符串类型必须加引号

  • 如果索引字段是字符串类型,但是在条件查询中,输入的参数是整型的话,那么这条语句会走全表扫描
  • 但是如果索引字段是整型类型,查询条件中的输入参数即是字符串,是不会导致索引失效,还是可以走索引扫描

原因是,在遇到字符串和数字比较的时候,会自动把字符串转为数字,然后再进行比较;如果索引字段是整型类型,查询条件中的输入参数是字符串,会自动转换成整型,所以索引不会失效。而索引字段是字符串,而输入的是整型,由于是字符串转数字,而索引不是整型类型,所以索引失效了。

联合索引非最左匹配

联合索引要能正确使用需要遵循最左匹配原则,也就是按照最左优先的方式进行索引匹配,否则就会导致索引失效

原因是,在联合索引的情况下,数据是按照索引第一列排序,第一列数据相同时才会按照第二列排序。

比如,如果创建了一个 (a, b, c) 联合索引,如果查询条件是以下这几种,就可以匹配上联合索引:

  • where a=1;
  • where a=1 and b=2 and c=3;
  • where a=1 and b=2;

因为有查询优化器,所以 a 字段在 where 子句的顺序并不重要。

但是,如果查询条件是以下这几种,因为不符合最左匹配原则,所以就无法匹配上联合索引,联合索引就会失效:

  • where b=2;
  • where c=3;
  • where b=2 and c=3;

对于where a = 1 and c = 0 这个语句,前面的a = 1是会走索引的,后面的c不走索引。

where条件的顺序影响索引使用吗

where条件的顺序可能会影响索引使用,但是,这种影响主要取决于mysql查询优化器的工作方式,而不是直接由where子句中条件的书写顺序决定的。

  • mysql的查询优化器通常会尝试重新排序where条件以最优化索引的使用
  • 对于符合索引,需要遵循最左前缀原则
  • 虽然where条件的顺序不影响索引的使用,但是将索引列的条件放在前面会使查询计划更易读和理解

不应使用or

如果在or前的条件列是索引列,而在or后的条件列不是索引列,那么索引列会失效。

只有一个条件列是索引列是没有意义的,只要有条件列不是索引列,就会进行全表扫描

in

尽量使用IN代替OR,但是IN包含的值不应过多,应少于1000个

IN通常是走索引的,当in后面的数据在数据表中超过30%的匹配时是全表扫描,不会走索引

其实就是 Mysql优化器会根据当前表的情况选择最优解。 Mysql优化器认为走全表扫描 比 走索引+回表快 那就不会走索引

不等于、不包含不能用到索引的快速搜索

在索引上,避免使用NOT、!=、<>、!<、!>、NOT EXISTS、NOT IN、NOT LIKE等

not in一定不走索引吗?

答案是不一定。Mysql优化器会根据当前表的情况选择最优解。

主要在于如果 MySQL 认为 全表扫描 比 走索引+回表效率高, 那么他会选择全表扫描。

大表查询慢怎么优化?

某个表有近千万条数据,查询比较慢,如何优化?

当mysql单表记录数过大时,数据库性能会明显下降,一些常见的优化措施如下:

  • 合理建立索引:在合适的字段上建立索引,例如在where和order by命令上涉及的列建立索引,可根据EXPLAIN来查看是否用了索引还是全表扫描
  • 索引优化:sql优化,索引要符合最左匹配原则
  • 建立分区:对关键字段建立水平分区,比如时间字段,若查询条件往往通过时间范围来进行查询,能提升不少性能
  • 利用缓存:利用Redis缓存热点数据,提高查询效率
  • 限定数据的范围。比如:用户在查询历史信息的时候,可以控制在一个月的时间范围内
  • 读写分离。经典的数据库拆分方案,主库负责写,从库负责读
  • 通过分库分表的方式进行优化,主要有垂直拆分和水平拆分
  • 数据异构到es
  • 冷热数据分离。几个月之前不常用的数据放到冷库中,最新的数据比较新的数据放到热库中
  • 升级数据库类型,换一种能兼容MySQL的数据库(OceanBase、TiDB等)

什么时候索引失效反而提升效率

  • 小表查询: 对于非常小的表,MySQL可能会选择全表扫描(忽略索引),因为全表扫描的开销可能比通过索引逐行查找还要低。在这种情况下,索引失效不会损害性能,反而简化了查询。
  • 读取大部分或所有行: 当一个查询返回表中很大比例的行(如 30% 或更多)时,使用索引查找可能会耗时更多,因为数据库必须跳回主数据页以读取完整记录。全表扫描可能更有效,因为它可以逐行顺序读取数据。
  • 低选择性索引: 如果索引列的选择性非常低,例如一个布尔型字段,许多行有相同的值,那么依赖索引可能会产生不必要的开销。全表扫描可以避免索引的搜索和回表开销。
  • 频繁更新的表: 对于包含大量更新操作的表,索引的维护成本可能相对较高。尤其是在频繁更新索引列时,通过避免使用或减少复杂的索引可以减轻写操作的负担。
  • 复杂查询的优化选择: 对于复杂的多表联接查询,优化器有时可以选择执行计划中不使用某个索引(或部分失效)以提高整体联接和计算效率。
  • 数据分布与优化器误判: 在某些特定情况下,如果索引导致MySQL错误地估计数据分布或行数,手动禁用索引或提示优化器使用不同策略可能提升性能。
posted @ 2025-08-19 22:38  小郑[努力版]  阅读(9)  评论(0)    收藏  举报