【问题标题】:MySql ORDER BY slow with join, fast with 2 queriesMySql ORDER BY 连接慢,2 个查询快
【发布时间】:2021-04-07 11:46:58
【问题描述】:

当我提供内联值时,为什么下面的查询慢而快?

select u.* from user u
join user_group g on u.group_id = g.id
where g.account_id = 1
order by u.id limit 10;
-- takes ~30ms
select id from user_group where account_id = 1;
-- which is (99,198,297,396,495,594,693,792,891,990)

select * from user
where group_id in (99,198,297,396,495,594,693,792,891,990)
order by id limit 10;
-- takes ~1ms

子查询也很慢。计划与加入相同。

select u.* from user u
where u.group_id in (select id from user_group where account_id = 1)
order by u.id limit 10;
-- ~30ms

所有查询都产生相同的结果。

98  0   99
197 0   198
296 0   297
395 0   396
494 0   495
593 0   594
692 0   693
791 0   792
890 0   891
989 0   990

架构

我有以下简单的表结构,有 100 个帐户 1k user_groups 和 1000 万用户。

create table account(
  id int primary key auto_increment
);

create table user_group(
  id int primary key auto_increment,
  account_id int not null,
  foreign key (account_id) references account(id)
);

create table user(
  id int primary key auto_increment,  
  deleted tinyint default 0,
  group_id int not null,
  foreign key (group_id) references user_group(id)
);

-- I've been trying with this index, but it doesnt seem to help.
create index user_1 on user(group_id, id, deleted);

计划

使用索引进行连接并进行临时文件排序的计划。

我不明白为什么 MySql 似乎认为它实际上需要执行完整的连接来过滤数据。我们显然没有读到来自user_group 的任何内容。

为了比较,我在 PostgreSQL 中尝试过同样的事情,两个查询都运行得很快。

为什么它很慢,有没有办法编写查询(单个查询!)可以快速完成?子选择不起作用。

这个 dbfiddle 显示了问题。 https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=8ed68310d8ca72e9daef389dc0469a6f

使用 MySql 8.0.23

谢谢

编辑

以下是完整的会话状态处理程序调试详细信息。看起来慢查询正在读取每个值,而不是快查询。

-- FLUSH STATUS;
-- select u.* from user u join user_group g on u.group_id = g.id where g.account_id = 1 order by u.id limit 10;
-- SHOW SESSION STATUS LIKE 'Handler%';

Handler_commit  1
Handler_delete  0
Handler_discover    0
Handler_external_lock   4
Handler_mrr_init    0
Handler_prepare 0
Handler_read_first  0
Handler_read_key    11
Handler_read_last   0
Handler_read_next   100110
Handler_read_prev   0
Handler_read_rnd    0
Handler_read_rnd_next   0
Handler_rollback    0
Handler_savepoint   0
Handler_savepoint_rollback  0
Handler_update  0
Handler_write   0
-- FLUSH STATUS;
-- set @uGroups := (select group_concat(id) from user_group where account_id = 1 group by account_id);
-- select * from user where group_id in (select @uGroups) order by id limit 10;
-- SHOW SESSION STATUS LIKE 'Handler%';

Handler_commit  2
Handler_delete  0
Handler_discover    0
Handler_external_lock   4
Handler_mrr_init    0
Handler_prepare 0
Handler_read_first  0
Handler_read_key    2
Handler_read_last   0
Handler_read_next   19
Handler_read_prev   0
Handler_read_rnd    0
Handler_read_rnd_next   0
Handler_rollback    0
Handler_savepoint   0
Handler_savepoint_rollback  0
Handler_update  0
Handler_write   0

【问题讨论】:

  • 请再次运行计时;这可能是一个缓存问题。 (30ms 几乎不值得一提)。同时获取处理程序计数:mysql.rjweb.org/doc.php/index_cookbook_mysql#handler_counts
  • 当涉及到 LIMIT 时,EXPLAIN 中的“行”是虚假的。 JOIN did 使用了索引——注意 user_1、test.g.id 和 Using 索引。但忽略 9982。
  • 谢谢@RickJames。在这个测试数据集中,30ms 差了 30 倍。在生产中,我看到查询从几分钟到几毫秒。处理程序状态显示read_next = 100110,这是整个过滤后的查询结果。因此,它似乎无法像在其他查询中那样使用索引进行排序和限制。
  • 每种方法返回了多少行?第二种方法似乎只检查一个选项,即单个项目“11,22,33,44”。它可能只返回 id=11 的行。
  • 谢谢@RickJames。每个查询产生相同的 10 个有序结果。变量查询中有一个错误,它不小心进行了字符串比较。我已经删除了。

标签: mysql sql-order-by query-optimization


【解决方案1】:

使用 straight_join 可以获得我预期的性能。

select straight_join u.* from user u
join user_group g on u.group_id = g.id
where g.account_id = 1
order by u.id limit 10;

感谢秋名https://dba.stackexchange.com/a/289710/227119

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-04-19
    • 1970-01-01
    • 2012-08-17
    • 1970-01-01
    • 2020-05-15
    • 2021-06-23
    • 1970-01-01
    相关资源
    最近更新 更多