为啥 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。谢谢。
根据我的小经验,使用IN
比JOIN
慢很多,比如INNER JOIN, LEFT JOIN or RIGHT JOIN
。但我确实发现有些人体验JOIN
比IN
慢。
当您询问有关查询优化的问题时,您应该在查询中包含每个表的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)
将此SELECT
与JOIN
视为以具有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_trx
和t_deposit
显示你的表结构,猜测索引中的内容和名称的类型是不可靠的,会延迟你得到完整的答案。以上是关于为啥 Mysql JOIN Query - Full SCAN 不使用索引的主要内容,如果未能解决你的问题,请参考以下文章
mysql update + ( where + join) 语法