You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

优化基于GROUP BY结果的查询:获取全Done状态流水线任务列表

优化流水线任务查询:获取全任务完成的流水线任务列表

问题背景

我有一张存储流水线任务数据的表,一个流水线包含多个独立运行的任务,每个任务可按自身节奏完成。流水线完成后,会通过将archived列设为1进行归档。需要获取**所有任务状态均为"Done"**的流水线的任务列表。

示例数据

mysql> select id, pipeline, archived, state from jobs where archived=0 limit 4;
+---------+-----------+----------+-------+
| id      | pipeline  | archived | state |
+---------+-----------+----------+-------+
| 8572387 | pipeline1 |        0 | Done  |
| 8572388 | pipeline1 |        0 | Done  |
| 8572389 | pipeline2 |        0 | Done  |
| 8572390 | pipeline2 |        0 | Fail  |
+---------+-----------+----------+-------+
4 rows in set (0.00 sec)

现有查询及性能问题

已经能快速查询出存在失败任务的流水线列表(耗时0.01秒):

mysql> select distinct(pipeline) from jobs where archived=0 group by pipeline, state having state!='Done';
+-----------+
| pipeline  |
+-----------+
| pipeline2 |
+-----------+
1 row in set (0.01 sec)

但将该子查询嵌套到最终查询后,耗时骤增至18.77秒(真实数据中4个流水线仅2个失败,每个流水线约500个任务):

select j1.id 
from jobs j1 
where j1.archived=0 
and j1.pipeline not in ( select distinct(j2.pipeline) 
                         from jobs j2 
                         where j2.archived=0 
                         group by j2.pipeline, j2.state having j2.state!='Done'
                        );

+---------+
| id      |
+---------+
| 8583200 |
| 8583201 |
| 8583202 |
| 8583203 |
.
.
.
| 8584305 |
| 8584306 |
+---------+
1107 rows in set (18.77 sec)

查询计划显示子查询被标记为DEPENDENT SUBQUERY,意味着会对主查询的每一行都执行一次子查询,这是性能瓶颈:

mysql> describe select j1.id from jobs j1 where j1.archived=0 and j1.pipeline not in (select distinct(j2.pipeline) from jobs j2 where j2.archived=0 group by j2.pipeline, j2.state having j2.state!='Done');
+----+--------------------+-------+------+---------------+----------+---------+-------+------+----------------------------------------------+
| id | select_type        | table | type | possible_keys | key      | key_len | ref   | rows | Extra                                        |
+----+--------------------+-------+------+---------------+----------+---------+-------+------+----------------------------------------------+
|  1 | PRIMARY            | j1    | ref  | archived      | archived | 2       | const | 2306 | Using where                                  |
|  2 | DEPENDENT SUBQUERY | j2    | ref  | archived      | archived | 2       | const | 2306 | Using where; Using temporary; Using filesort |
+----+--------------------+-------+------+---------------+----------+---------+-------+------+----------------------------------------------+
2 rows in set (0.00 sec)

优化方案

方案1:用JOIN替代NOT IN,消除相关子查询

先预计算出所有无失败任务的流水线,再关联主表获取任务ID:

SELECT j.id
FROM jobs j
INNER JOIN (
    -- 找出所有任务都为Done的流水线
    SELECT pipeline
    FROM jobs
    WHERE archived = 0
    GROUP BY pipeline
    HAVING COUNT(CASE WHEN state != 'Done' THEN 1 END) = 0
) valid_pipelines ON j.pipeline = valid_pipelines.pipeline
WHERE j.archived = 0;

方案2:临时表存储失败流水线,减少重复计算

如果失败流水线数量少,可以先将结果存入临时表,再做排除:

-- 创建临时表存储失败流水线
CREATE TEMPORARY TABLE failed_pipelines AS
SELECT DISTINCT pipeline
FROM jobs
WHERE archived = 0
GROUP BY pipeline, state
HAVING state != 'Done';

-- 查询排除失败流水线的任务
SELECT id
FROM jobs
WHERE archived = 0
AND pipeline NOT IN (SELECT pipeline FROM failed_pipelines);

方案3:添加复合索引,提升过滤与分组效率

创建针对(archived, pipeline, state)的复合索引,让数据库直接通过索引完成过滤、分组和状态检查,无需全表扫描:

CREATE INDEX idx_archived_pipeline_state ON jobs(archived, pipeline, state);

性能瓶颈原因

原查询中的子查询被识别为相关子查询,MySQL会对主查询的每一行(约2306行)重复执行一次子查询,相当于累计执行了2306次耗时0.01秒的查询,总耗时自然飙升。优化后的查询将子查询转为独立预计算,仅执行一次,再关联主表,性能会大幅提升。

内容的提问来源于stack exchange,提问作者Poshi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 12:05:27