冗余数据JOIN导致的慢SQL优化一例

本文涉及的产品
云原生数据库 PolarDB 分布式版,标准版 2核8GB
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
简介: CASE 一个这样的查询,每个表都只有几千条数据,但是查询非常慢,几十秒不出结果。 select distinct abc.pro_col1, abc.col3 from t0 p INNER JOIN t1 abc on p.id=abc.par_col2

CASE

一个这样的查询,每个表都只有几千条数据,但是查询非常慢,几十秒不出结果。

select  
distinct abc.pro_col1, abc.col3  
from  
t0 p  
INNER JOIN t1 abc 
  on p.id=abc.par_col2
inner join t2 s 
  on  s.col3=abc.col3  
inner join t3 po 
  on  po.id=s.col4 
where p.state=2 and po.state=3 
order by abc.pro_col1, abc.col3; 

优化方法

从语义来看,这条SQL是在经过几个JOIN后取其中一个表的两个字段的唯一值。

但是每一次关联,都可能产生冗余的值,所以导致了结果集越来越庞大。

修改建议,每一次JOIN都输出唯一值,减少冗余。

select 
distinct pro_col1, col3 from
(
select 
distinct t1.pro_col1, t1.col3, s.col4 from 
(
select 
distinct abc.pro_col1, abc.col3 from 
t1 abc INNER JOIN t0 p    
on (p.id = abc.par_col2 and p.state=2)
) t1
inner join t2 s 
on (s.col3 = t1.col3)
) t2
inner join t3 po   
on (po.id = t2.col4 and po.state=3)
order by t2.pro_col1, t2.col3  ;

修改后几十毫秒可以输出结果。

重现

postgres=# create table rt1(id int, info text);
CREATE TABLE
postgres=# create table rt2(id int, info text);
CREATE TABLE
postgres=# create table rt3(id int, info text);
CREATE TABLE
postgres=# create table rt4(id int, info text);
CREATE TABLE

postgres=# insert into rt1 select generate_series(1,1000),'test';
INSERT 0 1000
postgres=# insert into rt2 select 1,'test' from generate_series(1,1000);
INSERT 0 1000
postgres=# insert into rt3 select 1,'test' from generate_series(1,1000);
INSERT 0 1000
postgres=# insert into rt4 select 1,'test' from generate_series(1,1000);
INSERT 0 1000

以下查询,每次JOIN都产生大量的冗余数据,越到后面的JOIN,冗余越多,导致的查询非常漫长。

postgres=# explain select distinct rt1.id from rt1 join rt2 on rt1.id=rt2.id join rt3 on rt2.id=rt3.id join rt4 on rt3.id=rt4.id;
                                           QUERY PLAN                                            
-------------------------------------------------------------------------------------------------
 HashAggregate  (cost=145.25..155.25 rows=1000 width=4)
   Group Key: rt1.id
   ->  Hash Join  (cost=113.00..142.75 rows=1000 width=4)
         Hash Cond: (rt4.id = rt1.id)
         ->  Seq Scan on rt4  (cost=0.00..16.00 rows=1000 width=4)
         ->  Hash  (cost=100.50..100.50 rows=1000 width=12)
               ->  Hash Join  (cost=70.75..100.50 rows=1000 width=12)
                     Hash Cond: (rt3.id = rt1.id)
                     ->  Seq Scan on rt3  (cost=0.00..16.00 rows=1000 width=4)
                     ->  Hash  (cost=58.25..58.25 rows=1000 width=8)
                           ->  Hash Join  (cost=28.50..58.25 rows=1000 width=8)
                                 Hash Cond: (rt2.id = rt1.id)
                                 ->  Seq Scan on rt2  (cost=0.00..16.00 rows=1000 width=4)
                                 ->  Hash  (cost=16.00..16.00 rows=1000 width=4)
                                       ->  Seq Scan on rt1  (cost=0.00..16.00 rows=1000 width=4)
(15 rows)

修改如下,可以很快的得到结果

postgres=# select distinct t2.id from 
(
select distinct t1.id from 
(select distinct rt1.id from rt1 join rt2 on rt1.id=rt2.id) t1
join 
rt3 on t1.id=rt3.id
) t2
join rt4 on t2.id=rt4.id
;
 id 
----
  1
(1 row)
Time: 2.052 ms

postgres=# explain select distinct t2.id from 
postgres-# (
postgres(# select distinct t1.id from 
postgres(# (select distinct rt1.id from rt1 join rt2 on rt1.id=rt2.id) t1
postgres(# join 
postgres(# rt3 on t1.id=rt3.id
postgres(# ) t2
postgres-# join rt4 on t2.id=rt4.id
postgres-# ;
                                                 QUERY PLAN                                                  
-------------------------------------------------------------------------------------------------------------
 HashAggregate  (cost=190.25..200.25 rows=1000 width=4)
   Group Key: rt1.id
   ->  Hash Join  (cost=158.00..187.75 rows=1000 width=4)
         Hash Cond: (rt4.id = rt1.id)
         ->  Seq Scan on rt4  (cost=0.00..16.00 rows=1000 width=4)
         ->  Hash  (cost=145.50..145.50 rows=1000 width=4)
               ->  HashAggregate  (cost=125.50..135.50 rows=1000 width=4)
                     Group Key: rt1.id
                     ->  Hash Join  (cost=93.25..123.00 rows=1000 width=4)
                           Hash Cond: (rt3.id = rt1.id)
                           ->  Seq Scan on rt3  (cost=0.00..16.00 rows=1000 width=4)
                           ->  Hash  (cost=80.75..80.75 rows=1000 width=4)
                                 ->  HashAggregate  (cost=60.75..70.75 rows=1000 width=4)
                                       Group Key: rt1.id
                                       ->  Hash Join  (cost=28.50..58.25 rows=1000 width=4)
                                             Hash Cond: (rt2.id = rt1.id)
                                             ->  Seq Scan on rt2  (cost=0.00..16.00 rows=1000 width=4)
                                             ->  Hash  (cost=16.00..16.00 rows=1000 width=4)
                                                   ->  Seq Scan on rt1  (cost=0.00..16.00 rows=1000 width=4)
(19 rows)

Time: 0.750 ms

小结

这种SQL,如果要改内核的话,可以对统计信息进行分析(每个字段都有n_distinct),并对其进行query rewrite,得到同样的结果。

postgres=# \d pg_stats
          View "pg_catalog.pg_stats"
         Column         |   Type   | Modifiers 
------------------------+----------+-----------
 schemaname             | name     | 
 tablename              | name     | 
 attname                | name     | 
 inherited              | boolean  | 
 null_frac              | real     | 
 avg_width              | integer  | 
 n_distinct             | real     | 
 most_common_vals       | anyarray | 
 most_common_freqs      | real[]   | 
 histogram_bounds       | anyarray | 
 correlation            | real     | 
 most_common_elems      | anyarray | 
 most_common_elem_freqs | real[]   | 
 elem_count_histogram   | real[]   | 

祝大家玩得开心,欢迎随时来 阿里云促膝长谈业务需求 ,恭候光临

阿里云的小伙伴们加油,努力 做好内核与服务,打造最贴地气的云数据库

目录
相关文章
|
27天前
|
SQL 安全 数据处理
揭秘数据脱敏神器:Flink SQL的神秘力量,守护你的数据宝藏!
【9月更文挑战第7天】在大数据时代,数据管理和处理尤为重要,尤其在保障数据安全与隐私方面。本文探讨如何利用Flink SQL实现数据脱敏,为实时数据处理提供有效的隐私保护方案。数据脱敏涉及在处理、存储或传输前对敏感数据进行加密、遮蔽或替换,以遵守数据保护法规(如GDPR)。Flink SQL通过内置函数和表达式支持这一过程。
56 2
|
2月前
|
Java 网络架构 数据格式
Struts 2 携手 RESTful:颠覆传统,重塑Web服务新纪元的史诗级组合!
【8月更文挑战第31天】《Struts 2 与 RESTful 设计:构建现代 Web 服务》介绍如何结合 Struts 2 框架与 RESTful 设计理念,构建高效、可扩展的 Web 服务。Struts 2 的 REST 插件提供简洁的 API 和约定,使开发者能快速创建符合 REST 规范的服务接口。通过在 `struts.xml` 中配置 `<rest>` 命名空间并使用注解如 `@Action`、`@GET` 等,可轻松定义服务路径及 HTTP 方法。
37 0
|
2月前
|
测试技术 Java
全面保障Struts 2应用质量:掌握单元测试与集成测试的关键策略
【8月更文挑战第31天】Struts 2 的测试策略结合了单元测试与集成测试。单元测试聚焦于单个组件(如 Action 类)的功能验证,常用 Mockito 模拟依赖项;集成测试则关注组件间的交互,利用 Cactus 等框架确保框架拦截器和 Action 映射等按预期工作。通过确保高测试覆盖率并定期更新测试用例,可以提升应用的整体稳定性和质量。
59 0
|
2月前
|
数据库 Java 监控
Struts 2 日志管理化身神秘魔法师,洞察应用运行乾坤,演绎奇幻篇章!
【8月更文挑战第31天】在软件开发中,了解应用运行状况至关重要。日志管理作为 Struts 2 应用的关键组件,记录着每个动作和决策,如同监控摄像头,帮助我们迅速定位问题、分析性能和使用情况,为优化提供依据。Struts 2 支持多种日志框架(如 Log4j、Logback),便于配置日志级别、格式和输出位置。通过在 Action 类中添加日志记录,我们能在开发过程中获取详细信息,及时发现并解决问题。合理配置日志不仅有助于调试,还能分析用户行为,提升应用性能和稳定性。
38 0
|
2月前
|
Java XML Maven
跨越时代的飞跃:Struts 2 升级秘籍——从旧版本无缝迁移到最新版,焕发应用新生!
【8月更文挑战第31天】随着软件技术的发展,Struts 2 框架也在不断更新。本文通过具体案例指导开发者如何从旧版平滑升级到 Struts 2.6.x。首先更新 `pom.xml` 中的依赖版本,并执行 `mvn clean install`。接着检查 `struts.xml` 配置,确保符合新版本要求,调整包扫描器等设置。审查 Action 类及其注解,检查配置文件中的弃用项及插件。更新自定义拦截器实现,并验证日志配置。最后,通过一系列测试确保升级后的系统正常运行。通过这些步骤,可以顺利完成 Struts 2 的版本升级,提升应用的安全性和性能。
94 0
|
2月前
|
Java 测试技术 容器
从零到英雄:Struts 2 最佳实践——你的Web应用开发超级变身指南!
【8月更文挑战第31天】《Struts 2 最佳实践:从设计到部署的全流程指南》深入介绍如何利用 Struts 2 框架从项目设计到部署的全流程。从初始化配置到采用 MVC 设计模式,再到性能优化与测试,本书详细讲解了如何构建高效、稳定的 Web 应用。通过最佳实践和代码示例,帮助读者掌握 Struts 2 的核心功能,并确保应用的安全性和可维护性。无论是在项目初期还是后期运维,本书都是不可或缺的参考指南。
32 0
|
2月前
|
测试技术 Java
揭秘Struts 2测试的秘密:如何打造无懈可击的Web应用?
【8月更文挑战第31天】在软件开发中,确保代码质量的关键在于全面测试。对于基于Struts 2框架的应用,结合单元测试与集成测试是一种有效的策略。单元测试聚焦于独立组件的功能验证,如Action类的执行逻辑;而集成测试则关注组件间的交互,确保框架各部分协同工作。使用JUnit进行单元测试,可通过简单示例验证Action类的返回值;利用Struts 2 Testing插件进行集成测试,则可模拟HTTP请求,确保Action方法正确处理请求并返回预期结果。这种结合测试的方法不仅提高了代码质量和可靠性,还保证了系统各部分按需协作。
11 0
|
2月前
|
SQL 存储 数据库
|
2月前
|
SQL 数据管理 数据库
SQL中外键:维护数据完整性的关键
【8月更文挑战第31天】
42 0
下一篇
无影云桌面