接口慢查询优化方案
本文针对系统接口查询性能优化提出解决方案。随着数据量增长,慢SQL导致数据库连接数增加、事务堆积等问题,影响用户体验。优化方案包括:程序层面合并循环查询、减少数据库访问次数、按需查询、优化索引使用和缓存数据;数据库层面分析连接状态、锁和事务情况,通过EXPLAIN优化查询计划,并考虑数据分区。以订单查询为例,通过多维度优化提升接口响应速度。
目录
一、业务场景
随着系统数据和用户使用数量的增加,接口查询速率提升工作也将同步进行,以解决接口慢查询的问题。
二、影响范围
接口查询慢原因之一是数据库存在着慢SQL,SQL查询过慢会给数据库造成性能压力,比如数据库连接数增加,数据库事务或锁堆积,服务器CPU或内存超负载等,同时系统响应时间增加,用户体验下降。
1.系统接口响应超时

2.当前运行的sql查询

3.数据库连接状态

4.服务器负载情况

三、优化方案
以订单查询接口为例,从程序和数据库两方面进行优化,订单表存在上百万条数据,随着数据量的增加,接口查询速率也随之增加。
系统中,待发货订单、已发货订单、已完成订单、已取消订单、全部订单等订单管理菜单都会用到订单列表查询接口,订单列表查询接口中,需要附带很多信息,比如订单所属机构数据字典转码、订单物流信息、商品信息、渠道信息、买家信息等信息查询都会影响接口的查询速度。
3.1 程序优化
3.1.1.合并for循环
减少或禁止在公共查询方法中使用for循环,订单表是大数据表,把for循环改到具体业务调用的controller层


3.1.2.减少数据库查询次数
把循环查询数据的方法改为一次性查出,从 O(n) 降到 O(1),存入map集合,通过map集合进行匹配

3.1.3.按需查询
前端传送是否查询的标识来决定是否查询机构信息。


3.1.4.查询条件字段索引命中与添加
1.索引命中
前缀匹配(LIKE 'xxx%')可以利用索引,但后缀匹配(LIKE '%xxx')或全模糊(LIKE '%xxx%')不行。

2.索引添加
由于 create_time 上的范围查询(>= 和 <=)通常能显著减少扫描行数,在 create_time 上创建单列索引。
范围查询通常会过滤掉大量数据,create_time 索引可以让 MySQL 快速定位符合时间范围的行,减少扫描行数,尤其是orders 表数据量大且时间跨度广。

然后前端查询条件默认固定为当前年的数据。

3.1.5.缓存数据
查询耗时且更新频率较低的数据存入缓存,定时更新缓存。


3.2 数据库优化
借助Mysql系统数据库performance_schema 和 information_schema两个系统库中的性能分析表或元数据表对数据库性能进行分析,或使用EXPLAIN关键字对索引进行分析,添加相应的字段索引。
3.2.1.数据库连接分析
1.查看最大连接数:
SHOW VARIABLES LIKE 'max_connections';
这将返回一个结果集,其中包含了 MySQL 当前的最大连接数设置。默认情况下,MySQL 的最大连接数为 151。
2.查看正在使用的连接数:
SHOW STATUS LIKE 'Threads_connected';
这将返回一个结果集,其中包含了当前正在使用的连接数。要注意的是,并不是所有的连接都在活动状态,有些连接可能处于闲置状态。druid连接池中的initialSize初始化大小,影响该值
3.查询总连接数:
SHOW GLOBAL STATUS LIKE 'Connections';
4.查看连接超时:
wait_timeout: 这个参数指定了一个连接在服务器端闲置多长时间后会被自动关闭。默认情况下,wait_timeout 的值为 28800 秒(8 小时)。如果一个连接在这段时间内没有执行任何查询或操作,服务器会将其关闭。你可以使用以下 SQL 查询来查看当前的 wait_timeout 设置:SHOW VARIABLES LIKE 'wait_timeout';
主要用于处理通过程序或脚本(如 PHP、Python 应用)建立的连接,这些连接通常是持续时间较短、但频繁的数据库访问。interactive_timeout主要用于命令行客户端(如 mysql 命令行工具)建立的连接,这些连接通常需要用户手动输入命令并且可能存在较长时间的空闲。
5.查询重置:
SHOW VARIABLES LIKE 'mysql_reset_connection';
mysql_reset_connection 是 MySQL 中的一个系统变量,用于控制客户端连接是否在每次执行查询后重置。
当 mysql_reset_connection 设置为 ON 时,每次执行查询后,MySQL 将重置连接状态,这意味着会话变量、用户变量、临时表等都会被重置。这可以确保每次查询的环境都是一致的,不受之前查询的影响。
当 mysql_reset_connection 设置为 OFF 时,连接状态不会被重置。这意味着之前设置的会话变量、用户变量等在后续查询中仍然保持有效。
默认情况下,mysql_reset_connection 的值为 OFF,这意味着连接状态不会在每次查询后被重置。可以使用以下 SQL 查询来查看当前 mysql_reset_connection 的设置:
SHOW VARIABLES LIKE 'mysql_reset_connection';
3.2.2.数据库锁分析
1.查看死锁日志设置:
show variables like 'innodb_print_all_deadlocks';
设置通过SET GLOBAL innodb_print_all_deadlocks = 1;
2.查看死锁日志位置:
SHOW VARIABLES LIKE '%log_error%';
SHOW VARIABLES LIKE '%log%';
3.查看当前所有的锁:
SELECT * FROM information_schema.INNODB_LOCKS;
mysql8及以上:SELECT * FROM performance_schema.data_locks;
4.查看锁等待的对应的关系:
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
3.2.3.数据库事务分析
1.当前所运行的所有事务:
SELECT * FROM information_schema.INNODB_TRX;
SELECT * FROM performance_schema.events_transactions_current;
2.查看事务等待锁的最大时间,以秒为单位,超时自动关闭未完成的事务:
SHOW VARIABLES LIKE 'innodb_lock_wait_timeout';
3.查询当前正在等待的事件
SELECT * FROM performance_schema.events_waits_current;
4.查看事务隔离级别:
Mysql8以下:SHOW GLOBAL VARIABLES LIKE 'tx_isolation';
Mysql8以上:SHOW GLOBAL VARIABLES LIKE 'transaction_isolation';
5.查看 InnoDB 引擎的详细状态,包括锁和事务的信息:
SHOW ENGINE INNODB STATUS
3.2.4.当前运行SQL分析
1.前运行的sql查询和连接的信息:
SHOW PROCESSLIST;
SHOW FULL PROCESSLIST;查看完整的查询语句
2.使用events_statements_current表:
SELECT * FROM performance_schema.events_statements_current;
3.查询当前所有连接数:
SELECT COUNT(*) AS total_connections FROM INFORMATION_SCHEMA.PROCESSLIST;
4.查询当前正在执行查询的连接数:
SELECT COUNT(*) AS running_queries FROM INFORMATION_SCHEMA.PROCESSLIST WHERE STATE = 'Query';
3.2.5.数据库线程分析
1.查看线程信息:
SHOW GLOBAL STATUS LIKE 'Threads_%';
3.2.6.性能摘要分析
events_statements_summary_by_digest 表是 PERFORMANCE_SCHEMA库 的一部分,用于存储按查询摘要(digest)聚合的 SQL 语句性能统计信息。
例如:查询 2025 年 7 月 15 日执行的、未使用索引或未使用高效索引的 SQL 语句,并按总耗时从高到低排序。找出可能导致性能瓶颈的查询,优化索引或查询逻辑。
SELECT *
FROM PERFORMANCE_SCHEMA.events_statements_summary_by_digest
WHERE LAST_SEEN LIKE '2025-07-15%'
AND (SUM_NO_INDEX_USED > 0 OR SUM_NO_GOOD_INDEX_USED > 0)
ORDER BY SUM_TIMER_WAIT DESC;
3.2.7.事务、锁与线程综合查询SQL
SELECT
trx.trx_id 事务的唯一标识符,
trx.trx_state 事务的状态,
trx.trx_started 事务开始的时间,
trx.trx_mysql_thread_id 事务关联的MySQL线程ID,
trx.trx_isolation_level 事物隔离级别,
t.name 线程名称,
pl.Id 线程ID,
pl.USER 线程所属的用户,
pl.HOST 线程连接的主机,
pl.Command 线程的当前命令,
pl.TIME 线程的运行时间,
pl.State 线程的状态,
pl.Info 正在执行的SQL语句,
es.THREAD_ID ,
es.SQL_TEXT 当前执行的SQL语句,
es.CURRENT_SCHEMA 当前使用的数据库,
es.TIMER_WAIT/1000000000 语句的执行时间,
il.lock_id 锁的唯一标识符,
il.lock_mode 锁的模式,
il.lock_type 锁的类型,
il.lock_table 被锁定的表,
il.lock_index 被锁定的索引,
il.lock_space 被锁定的表空间,
il.lock_page 被锁定的页,
il.lock_rec 被锁定的记录
FROM
information_schema.INNODB_TRX trx
LEFT JOIN information_schema.PROCESSLIST pl ON trx.trx_mysql_thread_id = pl.Id
LEFT JOIN information_schema.INNODB_LOCKS il ON trx.trx_id = il.lock_trx_id
LEFT JOIN PERFORMANCE_SCHEMA.threads t ON t.processlist_id = pl.id
LEFT JOIN PERFORMANCE_SCHEMA.events_statements_current es ON es.THREAD_ID = t.thread_id
3.2.8.Explain分析查询
EXPLAIN 是 MySQL 中用于分析 SQL 查询的一个工具,通过它可以查看查询的执行计划,帮助开发者了解 MySQL 是如何优化和执行,例如EXPLAIN SELECT * FROM your_table WHERE condition;
输出字段解释
|
字段 |
解释 |
|
id |
查询中每个选择操作的标识符。复杂查询中,id 值越大优先级越高。 |
|
select_type |
表示查询的类型,如 SIMPLE(简单查询)、PRIMARY(主查询)、SUBQUERY(子查询)、DERIVED(派生表)等。 |
|
table |
查询涉及的表名或别名。 |
|
partitions |
显示使用的分区信息(如果有)。 |
|
type |
连接类型。性能从好到差的顺序为:system > const > eq_ref > ref > range > index > ALL。 ALL 表示全表扫描,通常性能最差。 |
|
possible_keys |
查询可能使用的索引列表。 |
|
key: |
实际使用的索引。如果没有使用索引,显示为 NULL。 |
|
key_len |
使用的索引的长度。 |
|
ref |
显示索引的哪一列被使用以及与之比较的常量(如果有)。 |
|
rows |
估计的扫描行数。这个值是基于统计信息的估算,不是精确值。 |
|
filtered |
表示经过查询条件过滤后返回的行数百分比。 |
|
Extra |
提供额外信息。例如: Using index:只使用了索引中的信息,没有访问表的数据行。 Using where:使用了 WHERE 子句进行过滤。 Using temporary:使用了临时表存储中间结果。 Using filesort:使用了文件排序(非内存排序),通常表示需要优化的查询。 |
3.2.9.数据分区
与Hive相同,在 MySQL 中,也支持分区,分区列是指在创建表时通过 PARTITION BY 子句指定的用于分区的列。分区列决定数据如何被分割到不同的分区中。MySQL 支持分区是为了提高查询性能、管理大规模数据以及便于数据维护。
1.支持的分区类型
MySQL 支持以下几种分区方式:
RANGE 分区:基于分区列值的范围。例如,按年份范围分区。
LIST 分区:基于分区列的离散值列表。例如,按地区值分区。
HASH 分区:基于分区列值的哈希值分配到固定数量的分区。
KEY 分区:类似 HASH,但使用 MySQL 内部的哈希函数。
COLUMNS 分区(MySQL 5.5+):支持非整数列(如字符串、日期)进行 RANGE 或 LIST 分区。
2.分区列的要求
分区列必须是表的主键或唯一键的一部分(对于 KEY 和 HASH 分区)。
分区列可以是整数、字符串、日期等类型(COLUMNS 分区支持非整数)。
分区列的值不能是表达式或函数的结果(除非使用 MySQL 8.0+ 的表达式分区)。
更多推荐


所有评论(0)