• 陈星宇(2.11)
    2024-10-11 来自四川
    这些策略分别是 pullout、duplicate weedout、first match、loose scan、materialization。 这些是8.0才有吗?在5.7里很少看到。

    作者回复: MySQL 5.6就开始支持semijoin了,不过8.0中支持的场景更多。 5.6,5.7不会自动转换exists,8.0开始支持exists的转换。 这是5.7 semijoin的官方文档:https://dev.mysql.com/doc/refman/5.7/en/semijoins.html

    
    
  • 陈星宇(2.11)
    2024-10-11 来自四川
    mysql> explain select item_id, sum(sold) as sold from stat_item_detail where item_id in ( select item_id from stat_item_detail where Gmt_create >= '2026-04-26 10:30:00') group by item_id; +----+--------------------+------------------+----------------+----------------------------+-------------+---------+------+---------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+--------------------+------------------+----------------+----------------------------+-------------+---------+------+---------+-------------+ | 1 | PRIMARY | stat_item_detail | index | NULL | idx_item_id | 4 | NULL | 1000029 | Using where | | 2 | DEPENDENT SUBQUERY | stat_item_detail | index_subquery | idx_item_id,idx_gmt_create | idx_item_id | 4 | func | 1 | Using where | +----+--------------------+------------------+----------------+----------------------------+-------------+---------+------+---------+-------------+ 执行不是按id从大到小执行吗?感觉子查询应该走时间字段的索引,这里是不是有问题,有点没理解。 “从上面的这个执行计划可以看到,这个 SQL 在执行时,先全量扫描索引 idx_item_id,每得到一个 item_id 后,执行相关子查询(DEPENDENT SUBQUERY)select 1 from stat_item_detail where gmt_create >= ‘2026-04-26 10:30:00’ and item_id = primary.item_id。当主查询中表中的数据量很大的时候,子查询执行的次数也会很多,因此 SQL 的性能非常差。”
    展开

    作者回复: 这个执行计划是先从 ID为1的获取一行数据,再驱动相关子查询。 使用explain extended + show warnings,能看到转换后的SQL: select `rep`.`stat_item_detail`.`item_id` AS `item_id`,sum(`rep`.`stat_item_detail`.`sold`) AS `sold` from `rep`.`stat_item_detail` where <in_optimizer>( `rep`.`stat_item_detail`.`item_id`, <exists>(<index_lookup>(<cache>(`rep`.`stat_item_detail`.`item_id`) in stat_item_detail on idx_item_id where ((`rep`.`stat_item_detail`.`gmt_create` >= '2026-04-26 10:30:00') and (<cache>(`rep`.`stat_item_detail`.`item_id`) = `rep`.`stat_item_detail`.`item_id`))))) group by `rep`.`stat_item_detail`.`item_id`。 这是5.5中的执行计划。在8.0里,我试了下,如果关闭semijoin优化,执行计划有点不一样, mysql> explain select item_id, sum(sold) as sold from stat_item_detail where item_id in ( select item_id from stat_item_detail where Gmt_create >= '2026-04-26 10:30:00') group by item_id; +----+-------------+------------------+-------+----------------------------+----------------+---------+--------+ | id | select_type | table | type | possible_keys | key | key_len | rows | +----+-------------+------------------+-------+----------------------------+----------------+---------+--------+ | 1 | PRIMARY | stat_item_detail | index | idx_item_id | idx_item_id | 4 | 903690 | | 2 | SUBQUERY | stat_item_detail | range | idx_item_id,idx_gmt_create | idx_gmt_create | 5 | 10 | +----+-------------+------------------+-------+----------------------------+----------------+---------+--------+

    
    