最近在知乎上发起了一个SQL优化挑战赛,本文为题目1的解析。其中涉及索引失效,修饰子查询重写等优化知识点,希望对大家在学习优化SQL的过程中有所帮助。
本文所使用的执行计划可视化工具为 PawSQL Explain Visualizer , 支持MySQLPostgreSQLopenGauss等数据库,用户可以免登录使用。
问题描述
题目:下面的SQL如何优化性能最佳
代码语言:javascript复制select * from customer
where c_custkey = ( select max(o_custkey) from orders
where subdate(o_orderdate, interval '1' DAY) < '2022-12-20')
表定义如下:
代码语言:javascript复制create table customer (c_custkey int not null,c_name varchar(25),c_address varchar(40),c_nationkey int,c_phone char(15),c_acctbal decimal(15,2),c_mktsegment char(10),c_comment varchar(117), primary key pk_idx1614428511 (c_custkey)
) engine=innodb default charset=utf8mb4 collate=utf8mb4_bin;
-- tpch.orders definition
create table orders (o_orderkey int,o_custkey int,o_orderstatus char(1),o_totalprice decimal(15,2),o_orderdate date,o_orderpriority char(15),o_clerk char(15),o_shippriority int,o_comment varchar(79), primary key (o_orderkey)
) engine=innodb default charset=utf8mb4 collate=utf8mb4_0900_ai_ci;
优化过程
1. 原SQL分析
- 首先这是一个非相关标量子查询,只需要计算一次,且c_cuskey上有唯一性索引,所以对于外表来说,已无优化空间。
2. 优化第一步
- 目标: 子查询使用 o_orderdate 作为过滤条件,可以基于这个字段创建索引,同时需要把 subdate 函数放到表达式右边,否则会导致索引失效。
- 优化方法:在
orders
表的字段上添加一个覆盖索引,并重写SQL CREATE INDEX date_custkey_idx ON public.orders USING btree (o_orderdate, o_custkey); 重写后的SQL: select * from customer where c_custkey = ( select max(o_custkey) from orders where o_orderdate < date('2022-12-20') interval'1 DAY'); - 优化效果: 全表扫描变成了索引扫描,且避免了回表,执行时间变为0.961ms,性能提升387.02%
3. 优化第二步
- 目标:利用索引的有序性,只取所需的第一条记录
- 方法:在
orders
表上新增不同顺序的一个覆盖索引,同时通过order by limit 1重写max函数 CREATE INDEX date_custkey_idx2 ON public.orders USING btree (o_custkey, o_orderdate); 重写后的SQL: select * from customer where c_custkey = ( select o_custkey from orders where o_orderdate < date('2022-12-20') interval'1 DAY' order by o_custkey desc limit 1); - 优化效果: 可以看到全表扫描变为全索引扫描,避免了回表,执行时间变为0.09ms,性能提升1000%。
过程总结
优化第一步选择 (o_orderdate, o_custkey)索引,通过覆盖索引获取数据时,需要找出 o_orderdate 小于 2022-12-21 的所有索引节点,然后遍历其中的 o_custkey,找出最大的值。优化第二部选择 (o_custkey, o_orderdate),通过覆盖索引获取数据时,需要按照 o_custkey 从大到小查找索引,找出其中 o_orderdate 小于 2022-12-21 的第一个索引节点即可。
PawSQL的自动优化
我们将待优化SQL直接提交到PawSQL,让其给我们做自动优化。从输出的优化详情页面我们可以看到,PawSQL自动帮我们进行了以下两个重写优化
- MAX/MIN子查询重写
- 索引列上的运算导致索引失效
并且根据重写后的SQL推荐了对应的索引。使用PawSQL,真正做到了一键优化!
PawSQL Cloud关于题目1的优化详情:
关于PawSQL
PawSQL专注数据库性能优化的自动化和智能化,支持MySQL,PostgreSQL,Opengauss等,提供的SQL优化产品包括
- PawSQL Cloud,在线自动化SQL优化工具,支持SQL审查,智能查询重写、基于代价的索引推荐,适用于数据库管理员及数据应用开发人员,
- PawSQL Advisor,IntelliJ 插件, 适用于数据应用开发人员,可以IDEA/DataGrip应用市场通过名称搜索“PawSQL Advisor”安装。
- PawSQL Engine, 是PawSQL系列产品的后端优化引擎,可以以docker镜像的方式独立安装部署,并通过http/json的接口提供SQL优化服务。