为啥 Mysql JOIN Query - Full SCAN 不使用索引

Posted

技术标签:

【中文标题】为啥 Mysql JOIN Query - Full SCAN 不使用索引【英文标题】:Why Mysql JOIN Query - Full SCAN not using Index为什么 Mysql JOIN Query - Full SCAN 不使用索引 【发布时间】:2019-08-25 15:56:38 【问题描述】:

当我只在一个表中选择查询时,我们得到了使用索引的响应,但是当在查询中使用连接语句时,mysql对表进行全扫描?

第一次查询

select *
    from t_deposit_trx
    where remit_tx_id = '3a33ff14-8d31-45d0-b64f-8a251c4b19a5'

1 简单的 t_deposit_trx 参考 t_deposit_trx_remit_tx_id_index t_deposit_trx_remit_tx_id_index 110 const 1 使用索引条件

第二次查询

select tx_id
from t_settle_trx
where report_date >= '2019-03-01'
  and report_date <= '2019-03-16'
  and tx_type = 'CANCEL'

1 SIMPLE t_settle_trx range t_settle_trx_report_date_tx_type_index t_settle_trx_report_date_tx_type_index 196 5263 使用索引条件

2 查询运行良好。 使用索引和速度很好。

但是加入两个表,很慢。

select * from t_deposit_trx
force index (t_deposit_trx_remit_tx_id_index)
where remit_tx_id in (
  select tx_id
  from t_settle_trx
  where report_date >= '2019-03-01'
    and report_date <= '2019-03-02'
    and tx_type = 'CANCEL'
)

1 主要 t_deposit_trx 全部 55724 1 PRIMARY t_settle_trx range t_settle_trx_report_date_tx_type_index t_settle_trx_report_date_tx_type_index 196 299 使用索引条件;使用哪里; FirstMatch(t_deposit_trx);使用连接缓冲区(flat, BNL join)

我们可以看到上面的结果.. t_settle_trx 使用范围扫描并获取 tx_id,接下来我希望查询使用索引 “t_settle_trx_report_date_tx_type_index” 但它使用全扫描..

我不知道为什么?

【问题讨论】:

使用 JOIN 比使用 IN (SELECT 是否有区别。什么 MySQL/MariaDB 版本?什么是表结构(SHOW CREATE TABLE t_deposit_trx)?使 t_settle_trx_report_date_tx_type_index 在报告日期之前具有 tx_type 会很好这些查询,请查看说明中使用的长度。 我已经加入查询,然后两个表都使用全扫描。 mariadb 版本是 10.1.29-MariaDB-1~xenial。谢谢。 根据我的小经验,使用INJOIN 慢很多,比如INNER JOIN, LEFT JOIN or RIGHT JOIN。但我确实发现有些人体验JOININ 慢。 当您询问有关查询优化的问题时,您应该在查询中包含每个表的SHOW CREATE TABLE。否则我们只能猜测您当前拥有的索引,以及其他表选项。例如。在这种情况下,我想知道连接是否正在比较具有不同排序规则的字符串,因此不能使用索引进行比较。 非常感谢比尔·卡尔文。我很欣赏你的评论。我检查了 2 个表排序规则,发现它有所不同。一个没有设置。另一个是 utf8 .. 【参考方案1】:

查询 2 不是最优的。翻转索引:

INDEX(tx_type, report_date)

也就是说,无论基数如何,都将使用= 测试的列放在首位。

IN ( SELECT ... ) 不是 JOIN。 (在较新的版本中,它可能被转换为JOIN。)

试试这个:

SELECT  d.*
    FROM  t_settle_trx AS s
    JOIN  t_deposit_trx AS d  ON s.tx_id = d.remit_tx_id
    WHERE  s.tx_type = 'CANCEL'
      AND  s.report_date >= '2019-03-01'
      AND  s.report_date <  '2019-03-01' + INTERVAL 2 DAY

s: INDEX(tx_type, report_date, tx_id)
d: INDEX(remit_tx_id)

将此SELECTJOIN 视为以具有WHERE 子句的表开头。

(注意:我安排了表格和 where 子句的顺序,以便您了解优化器的想法。表格的顺序和 where 子句的顺序无关;优化器会根据需要重新排列.)

我的配方应该

充分利用两个表上的索引。 避免全表扫描 对它查看的第一个表进行所有过滤

全表扫描不是不一定是坏事。如果表的大部分会被触及,那么实际上更快简单地扫描表而不是在索引的 BTree 和数据的 BTree 之间跳转。 (您的具体案例还有其他缺陷;我专注于它们。)

INDEX(tx_type, report_date, tx_id)

是“覆盖”,因为查询所需的所有列都在一个索引中找到。 “覆盖”为您提供了轻微的额外性能提升。它在EXPLAIN 中由Using index 表示(不是Using index condition)。

使用 tx_seq,这可能是最优的:

INDEX(tx_type, report_date, tx_seq)

【讨论】:

【参考方案2】:

伙计们.. 感谢您对我的问题感兴趣。

以上2个表在暂存环境和生产环境中具有相同的索引结构。 并使用 galera 集群。

我真正想知道的是为什么他们使用不同的“执行计划”? 尤其是在生产环境中,JOIN查询很慢。

我昨天做的是..

select * from t_deposit_trx
where remit_tx_id in (
  select tx_id
  from t_settle_trx
  where report_date >= '2019-03-01'
    and report_date <= '2019-03-02'
    and tx_type = 'CANCEL'
)

我稍微改变一下这个查询

select * from t_deposit_trx
where tx_seq in (
  select tx_seq 
  from t_settle_trx
  where report_date >= '2019-03-01'
    and report_date <= '2019-03-02'
    and tx_type = 'CANCEL'
)

然后是第一个 t_settle_trx “范围扫描”和第二个 t_deposit_trx “索引扫描”.. 但是两个 tx_seq 值是不同的值。它没有任何关系船。 只是测试他们在join查询时是否可以使用索引。

你知道我的意思吗? 这意味着他们可以在加入时使用索引。

那么是否有任何环境变量问题或密钥长度限制或任何问题?

感谢您阅读我。

【讨论】:

现在您需要不同的索引,可能在两个表上,因为tx_seq 而不是tx_id【参考方案3】:

我希望这不会比您的第一个查询慢:

SELECT t1.* FROM t_deposit_trx t1 
INNER JOIN t_settle_trx t2
ON t1.remit_tx_id=t2.tx_id
WHERE t2.report_date >= '2019-03-01'
AND t2.report_date <= '2019-03-02'
AND t2.tx_type='CANCEL';

【讨论】:

是的,您的查询比以前更快,但仍然是 t_deposit_trx 表全扫描...这就是问题所在。谢谢 好的,现在我明白你的担心了。它不是关于哪种方法更快。我在这里无能为力;) t_settle_trx(tx_type,report_date,tx_id) 上有一个索引。 t_deposit_trx(remit_tx_id) 也应该有一个索引。下次请用SHOW CREATE TABLE t_settle_trxt_deposit 显示你的表结构,猜测索引中的内容和名称的类型是不可靠的,会延迟你得到完整的答案。

以上是关于为啥 Mysql JOIN Query - Full SCAN 不使用索引的主要内容,如果未能解决你的问题,请参考以下文章

Mysql Join Query需要很长时间才能执行

MySQL Query Join 从两个表中选择不匹配的行

mysql update + ( where + join) 语法

为啥 MySQL 不使用我的索引进行 JOIN?

为啥 MySQL 在使用 JOIN 而不是 WHERE 时很慢?

mysql中多表连接(不使用join )为啥 超过三张表就报错?