Bug #116534 Another Performance of TPC-H Query 16
Submitted: 3 Nov 2024 10:40 Modified: 6 Nov 2024 12:58
Reporter: JINSHENG BA Email Updates:
Status: Verified Impact on me:
None 
Category:MySQL Server: Optimizer Severity:S5 (Performance)
Version:596f0d23 (9.0.0) OS:Any
Assigned to: CPU Architecture:Any

[3 Nov 2024 10:40] JINSHENG BA
Description:
Another suboptimal query plan is chosen for executing query 16 in TPC-H benchmark. 

$ mysql tpch < 16.sql
EXPLAIN
-> Sort: supplier_cnt DESC, PART.P_BRAND, PART.P_TYPE, PART.P_SIZE  (actual time=1698..1699 rows=18333 loops=1)
    -> Stream results  (cost=2.59e+9 rows=1.58e+6) (actual time=154..1682 rows=18333 loops=1)
        -> Group aggregate: count(distinct PARTSUPP.PS_SUPPKEY)  (cost=2.59e+9 rows=1.58e+6) (actual time=154..1677 rows=18333 loops=1)
            -> Nested loop antijoin  (cost=785e+6 rows=7.85e+9) (actual time=154..1633 rows=119078 loops=1)
                -> Nested loop inner join  (cost=149457 rows=785940) (actual time=148..1583 rows=119136 loops=1)
                    -> Sort: PART.P_BRAND, PART.P_TYPE, PART.P_SIZE  (cost=20430 rows=198301) (actual time=148..151 rows=29784 loops=1)
                        -> Filter: ((PART.P_BRAND <> 'Brand#34') and (not((PART.P_TYPE like 'LARGE BRUSHED%'))) and (PART.P_SIZE in (48,19,12,4,41,7,21,39)))  (cost=20430 rows=198301) (actual time=0.132..113 rows=29784 loops=1)
                            -> Table scan on PART  (cost=20430 rows=198301) (actual time=0.126..66.8 rows=200000 loops=1)
                    -> Covering index lookup on PARTSUPP using PRIMARY (PS_PARTKEY=PART.P_PARTKEY)  (cost=0.636 rows=3.96) (actual time=0.0472..0.0478 rows=4 loops=29784)
                -> Single-row index lookup on <subquery2> using <auto_distinct_key> (s_suppkey=PARTSUPP.PS_SUPPKEY)  (cost=3342..3342 rows=1) (actual time=311e-6..311e-6 rows=487e-6 loops=119136)
                    -> Materialize with deduplication  (cost=3338..3338 rows=9982) (actual time=5.93..5.93 rows=4 loops=1)
                        -> Filter: (SUPPLIER.S_SUPPKEY is not null)  (cost=1038 rows=9982) (actual time=0.231..5.91 rows=4 loops=1)
                            -> Filter: (SUPPLIER.S_COMMENT like '%Customer%Complaints%')  (cost=1038 rows=9982) (actual time=0.23..5.91 rows=4 loops=1)
                                -> Table scan on SUPPLIER  (cost=1038 rows=9982) (actual time=0.02..1.87 rows=10000 loops=1)

0.01s user 0.00s system 0% cpu 2.152 total

If we apply this patch, we can see a performance improvement:

diff --git a/sql/sql_optimizer.cc b/sql/sql_optimizer.cc
index 2b8f6c523aa..3a45074151d 100644
--- a/sql/sql_optimizer.cc
+++ b/sql/sql_optimizer.cc
@@ -3575,10 +3575,6 @@ static bool setup_join_buffering(JOIN_TAB *tab, JOIN *join,
         We allow join buffering for the remaining tables of the materialized
         semi-join nest.
       */
-      if (tab->first_sj_inner() == tab->idx()) {
-        assert(tab->use_join_cache() == JOIN_CACHE::ALG_NONE);
-        goto no_join_cache;
-      }
       break;
 
     case SJ_OPT_DUPS_WEEDOUT:

EXPLAIN
-> Sort: supplier_cnt DESC, PART.P_BRAND, PART.P_TYPE, PART.P_SIZE  (actual time=798..800 rows=18333 loops=1)
    -> Stream results  (actual time=738..785 rows=18333 loops=1)
        -> Group aggregate: count(distinct PARTSUPP.ps_suppkey)  (actual time=738..782 rows=18333 loops=1)
            -> Sort: PART.P_BRAND, PART.P_TYPE, PART.P_SIZE  (actual time=738..746 rows=119078 loops=1)
                -> Stream results  (cost=314e+6 rows=3.14e+9) (actual time=12.3..631 rows=119078 loops=1)
                    -> Nested loop antijoin  (cost=314e+6 rows=3.14e+9) (actual time=12.3..611 rows=119078 loops=1)
                        -> Nested loop inner join  (cost=90448 rows=314380) (actual time=0.393..558 rows=119136 loops=1)
                            -> Filter: ((PART.P_BRAND <> 'Brand#34') and (not((PART.P_TYPE like 'LARGE BRUSHED%'))) and (PART.P_SIZE in (48,19,12,4,41,7,21,39)))  (cost=20931 rows=79321) (actual time=0.259..214 rows=29784 loops=1)
                                -> Table scan on PART  (cost=20931 rows=198301) (actual time=0.252..174 rows=200000 loops=1)
                            -> Covering index lookup on PARTSUPP using PRIMARY (PS_PARTKEY=PART.P_PARTKEY)  (cost=0.48 rows=3.96) (actual time=0.0107..0.0113 rows=4 loops=29784)
                        -> Single-row index lookup on <subquery2> using <auto_distinct_key> (s_suppkey=PARTSUPP.PS_SUPPKEY)  (cost=3387..3387 rows=1) (actual time=336e-6..336e-6 rows=487e-6 loops=119136)
                            -> Materialize with deduplication  (cost=3383..3383 rows=9982) (actual time=11.9..11.9 rows=4 loops=1)
                                -> Filter: (SUPPLIER.S_SUPPKEY is not null)  (cost=1083 rows=9982) (actual time=1.03..11.9 rows=4 loops=1)
                                    -> Filter: (SUPPLIER.S_COMMENT like '%Customer%Complaints%')  (cost=1083 rows=9982) (actual time=1.03..11.9 rows=4 loops=1)
                                        -> Table scan on SUPPLIER  (cost=1083 rows=9982) (actual time=0.331..6.9 rows=10000 loops=1)

0.00s user 0.01s system 0% cpu 0.851 total

The output result remains the same, but the execution time is reduced by more than 60%. In the query plan, it seems the operation `SORT` is executed in different locations.

Query 16:
EXPLAIN ANALYZE
select
    p_brand,
    p_type,
    p_size,
    count(distinct ps_suppkey) as supplier_cnt
from
    PARTSUPP,
    PART
where
    p_partkey = ps_partkey
    and p_brand <> 'Brand#34'
    and p_type not like 'LARGE BRUSHED%'
    and p_size in (48, 19, 12, 4, 41, 7, 21, 39)
    and ps_suppkey not in (
        select
            s_suppkey
        from
            SUPPLIER
        where
            s_comment like '%Customer%Complaints%'
    )
group by
    p_brand,
    p_type,
    p_size
order by
    supplier_cnt desc,
    p_brand,
    p_type,
    p_size;

TPC-H data scale: 1GB

How to repeat:
The data is exactly the same as the one used in https://bugs.mysql.com/bug.php?id=116309
[6 Nov 2024 12:58] MySQL Verification Team
Hello Jinsheng Ba,

Thank you for the report and feedback.

regards,
Umesh