高斯性能分析【第一天】单表执行计划分析
问题概述
看如下 SQL 执行计划,回答:
- 是否是 Seq Scan?
- 扫描了多少行?
- 返回了多少行?
- Filter 过滤掉了多少数据?
sql
testdb=> explain analyze select * from lab_order where user_id = 10001;
id | operation | A-time | A-rows | E-rows | Peak Memory | A-width | E-width | E-costs
----+---------------------------+--------+--------+--------+-------------+---------+---------+-----------------
1 | -> Seq Scan on lab_order | 55.754 | 6 | 6 | 53KB | | 65 | 0.000..7170.000
(1 row)
Predicate Information (identified by plan id)
-----------------------------------------------
1 --Seq Scan on lab_order
Filter: (user_id = 10001)
Rows Removed by Filter: 299994
(3 rows)
====== Query Summary =====
----------------------------------------
Datanode executor start time: 0.070 ms
Datanode executor run time: 56.977 ms
Datanode executor end time: 0.014 ms
Planner runtime: 0.721 ms
Query Id: 1948651263768123007
Total runtime: 57.076 ms
(6 rows)
testdb=>
分析
根据 EXPLAIN ANALYZE 输出:
-
是否是 Seq Scan?
是的,操作行显示:
Seq Scan on lab_order,说明对表lab_order进行了顺序扫描。 -
扫描了多少行?
Seq Scan 实际扫描了 6 + 299,994 = 300,000 行(返回的行 + 被过滤掉的行)。
更直接地看
Rows Removed by Filter: 299994,说明原始表中有 300,000 行被扫描。 -
返回了多少行?
返回了 6 行,
A-rows | 6。 -
Filter 过滤掉了多少数据?
Filter 过滤掉了 299,994 行,正如
Rows Removed by Filter: 299994所示。
简单总结:数据库做了全表扫描(Seq Scan),扫描了 30 万行,只返回 6 行,过滤掉了 299,994 行。
结论(优化建议)
这个查询 有很大优化空间。原因是:
- 表
lab_order有大约 30 万行,你只根据user_id = 10001查询 6 行。 - 现在是 Seq Scan(全表扫描),这意味着 数据库 会把整张表读一遍,然后过滤掉 299,994 行,效率低下。
1️⃣ 优化思路
使用索引
最直接的方法是为 user_id 建立索引,这样查询就可以直接定位到符合条件的行,而不需要全表扫描。
sql
CREATE INDEX idx_lab_order_user_id ON lab_order(user_id);
效果:
- 查询会使用 Index Scan 或 Bitmap Index Scan ,只扫描匹配
user_id = 10001的行,而不是整个表。 - 对于这种返回行数非常少的情况,性能提升非常明显。
组合索引(可选)
实际业务上,如果查询除了 user_id 还有其他筛选条件或排序,可以创建 复合索引:
sql
CREATE INDEX idx_lab_order_user_id_status ON lab_order(user_id, status);
- 适用于经常按
user_id+status查询的场景。 - 可以覆盖查询,减少回表操作。
考虑表分区(可选)
如果 lab_order 表非常大且 user_id 分布稀疏,可以考虑 按 user_id 分区。
- 对于查询某个用户的所有订单,这样可以只扫描对应分区。
- 适合表超过几百万行或更多的场景。
检查统计信息
确保数据库中的表统计信息是最新的:
sql
ANALYZE lab_order;
- 数据库用统计信息决定是否使用索引,如果统计信息过旧,可能仍然选择 Seq Scan。
2️⃣ 预期效果
- 使用索引后,查询 6 行的时间可能从 50ms 以上降到几毫秒。
- 避免了 299,994 行的无效扫描,节省了 CPU 和 I/O。
💡 小结:
- 对
user_id建索引 → 最直接有效。 - 对经常联合查询的列可建复合索引 → 提升更多查询。
- 保持统计信息最新 → 优化器选择最优执行计划。
验证
添加索引后的执行计划如下,
- 添加索引前,耗时:Total runtime: 57.076 ms
- 添加索引后,耗时:Total runtime: 0.284 ms
sql
testdb=> CREATE INDEX idx_lab_order_user_id ON lab_order(user_id);
CREATE INDEX
testdb=>
testdb=>
testdb=>
testdb=>
testdb=> explain analyze select * from lab_order where user_id = 10001;
id | operation | A-time | A-rows | E-rows | Peak Memory | A-width | E-width | E-costs
----+---------------------------------------------------------+--------+--------+--------+-------------+---------+---------+--------------
1 | -> Index Scan using idx_lab_order_user_id on lab_order | 0.173 | 6 | 6 | 85KB | | 65 | 0.000..7.909
(1 row)
Predicate Information (identified by plan id)
-----------------------------------------------------------
1 --Index Scan using idx_lab_order_user_id on lab_order
Index Cond: (user_id = 10001)
(2 rows)
====== Query Summary =====
----------------------------------------
Datanode executor start time: 0.040 ms
Datanode executor run time: 0.226 ms
Datanode executor end time: 0.009 ms
Planner runtime: 1.365 ms
Query Id: 1948651263768126756
Total runtime: 0.284 ms
(6 rows)
====== Query Others =====
---------------------------
Bypass: Yes
(1 row)
testdb=>
若有转载,请标明出处:https://blog.csdn.net/CharlesYuangc/article/details/161320999