【问题标题】:IN clause not using indexIN 子句不使用索引
【发布时间】:2016-11-01 23:55:18
【问题描述】:

这是表定义

CREATE TABLE `dt_prdtime` (
  `TCompany` varchar(3) NOT NULL DEFAULT '',
  `TPerCode` varchar(8) NOT NULL,
  `TBegDateTime` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT 'วันที่',
  `TQPay` int(1) NOT NULL DEFAULT '2',
  `TYear` int(4) NOT NULL,
  `TMonth` int(2) NOT NULL,
  PRIMARY KEY (`TCompany`,`TPerCode`,`TBegDateTime`),
  KEY `TMonth` (`TMonth`) USING BTREE,
  KEY `TPerCode` (`TPerCode`,`TYear`,`TMonth`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8

这是数据样本。此表有 10000+ 条记录,TMonth 字段中的值各不相同

+----------+----------+---------------------+-------+-------+--------+
| TCompany | TPerCode | TBegDateTime        | TQPay | TYear | TMonth |
+----------+----------+---------------------+-------+-------+--------+
| S10      | 000001   | 2016-01-02 17:33:00 |     1 |  2016 |      1 |
| S10      | 000001   | 2016-01-02 07:48:00 |     1 |  2016 |      1 |
| S10      | 000001   | 2016-01-03 17:39:00 |     1 |  2016 |      1 |
| S10      | 000001   | 2016-01-03 07:30:00 |     1 |  2016 |      1 |
| S10      | 000001   | 2016-01-04 17:49:00 |     1 |  2016 |      1 |
| S10      | 000001   | 2016-01-04 07:54:00 |     1 |  2016 |      1 |
| S10      | 000001   | 2016-01-05 17:50:00 |     1 |  2016 |      1 |
| S10      | 000001   | 2016-01-05 07:36:00 |     1 |  2016 |      1 |
| S10      | 000001   | 2016-01-06 17:37:00 |     1 |  2016 |      1 |
| S10      | 000001   | 2016-01-06 07:35:00 |     1 |  2016 |      1 |
+----------+----------+---------------------+-------+-------+--------+

使用EXPLAIN,此查询使用TMonth 索引:

SELECT * FROM dt_prdtime WHERE TMonth = 5

虽然这个拒绝使用索引:

SELECT * FROM dt_prdtime WHERE TMonth IN (5,6)

我用另一个简单的表测试,

CREATE TABLE `table1` (
  `id` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1

SELECT * FROM table2 WHERE id IN (5,6)

并且使用了该表的索引

谁能解释一下? dt_prdtime 表有问题吗?

【问题讨论】:

  • 这可能是由于优化器造成的。有时优化器估计不使用索引,即使索引可用。你试过 FORCE 索引吗?
  • SELECT * FROM dt_prdtime FORCE INDEX (TMonth) WHERE TMonth IN (5, 6)
  • 你不应该强制索引。现在是收拾行李而不使用 rdbms 的好时机
  • 同意,因为如果不使用索引,我们必须相信优化器是有道理的
  • 问五月是不是故意忽略了年份?如果没有,有更好的方法来完成你的任务,即使是 (5,6)。

标签: mysql indexing


【解决方案1】:

我会说这是因为您使用的是 MyISAM 引擎。

在我的Answer 中可以看出,它与 INNODB 完美配合。

我会尽量在这件事上找至少 1 个值得尊敬的参考资料。

这里,The range Join Type,显然是 INNODB 的焦点,因为它是默认引擎。并且当某些文档层次结构的手册中没有明确提及时,它是假定的。

注意,我的示例链接中的 id 没有任何连续性。意思是,不要在其 EXPLAIN 输出中过度关注type=range。速度是通过优化器(CBO)得出的。

在我的示例中,cardinality 非常高(430 万)。目标 id 计数相对较低(1000)。使用索引。

您的情况可能正好相反:您的基数可能非常低,例如 3,而优化器决定放弃使用索引。

要检查您的索引cardinality,请参阅手册页SHOW INDEX Syntax

一个简单的调用如:

show index from ratings;

+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table   | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| ratings |          0 | PRIMARY  |            1 | id          | A         |     4313544 |     NULL | NULL   |      | BTREE      |         |               |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

【讨论】:

  • 这是否意味着我必须将表更改为 InnoDB?我根本看不到 EXPLAIN 有任何变化。
  • 您需要音量。忽略小表的解释(因为它与增长时的现实有关)。让我在 1 分钟内使用 url 编辑此评论。编辑:阅读此Answer
  • 所以据我了解,它没有利用索引,因为表很大,这个查询需要处理所有行?
  • 如果那是你的现实,那就对了。如果目标计数数量稀少且表很大并且索引运行状况良好(统计/分析),则它会使用它。如果它必须使用一张大桌子的大部分份额,它就不会使用它。如果表格有 20 行,则不会使用它。
  • 如果通过低cardinality判断没有意义,可以放弃使用。 Show Index 显示基数。我的示例具有极高的基数和稀疏的目标。很高兴使用我的索引。
【解决方案2】:

MyISAM 和 InnoDB 在需要获取“太多”表时都不太可能使用索引。

IN (5,6) 可能意味着需要扫描表的 2/12?或者数据可能存在偏差,以至于这两个月的行数超过了他们的份额?

优化器可能在这种情况下避开索引的原因...

在使用这样的索引时,需要花费大量时间在索引(一个BTree)和数据之间来回弹跳。

不使用索引时,它只是浏览数据,忽略 10/12 的行。这可能实际上更快。

【讨论】:

  • 如果SELECT * FROM dt_prdtime WHERE TMonth = 5 使用索引,为什么IN (5,6) 不使用?它几乎是一样的,只是比TMonth = 5 多一个数字。它有什么不同?为什么在索引中查找这些数字要快得多时,它会扫描整个表?
  • 截止值不准确。通常是 20% 左右。一个月是8%(如果平均分配);两个月是17%。在您的情况下,也许统计数据决定将其降至 17% 以下。再扯远一点,IN(1,2,3,4,5,6,7,9,10,11)有意义吗?
猜你喜欢
  • 1970-01-01
  • 2010-10-09
  • 2014-07-15
  • 2010-09-07
  • 1970-01-01
  • 2013-02-03
  • 2015-06-29
  • 2019-11-29
  • 1970-01-01
相关资源
最近更新 更多