【问题标题】:Simple MySQL indexing issue简单的 MySQL 索引问题
【发布时间】:2012-01-30 01:16:47
【问题描述】:

我有这张桌子:

CREATE TABLE IF NOT EXISTS `test1_nopart` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `idAccount` int(10) unsigned NOT NULL,
  `data` mediumint(8) unsigned NOT NULL,
  `date` date NOT NULL,
  PRIMARY KEY (`id`),
  KEY `date` (`date`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

我用 10 000 000 行填充此表。 按日期重新分区是同质的

EXPLAIN SELECT * FROM `test1_nopart` WHERE date = "2014-03-04" 

这是结果

id  select_type   table        type     possible_keys   key     key_len     ref     rows        Extra
1   SIMPLE     test1_nopart     ALL     NULL            NULL    NULL        NULL    7875981     Using where

=> 3000 行的结果(大约)没有解释 3.6 秒

如您所见,该索引未使用,并且它不是 possible_keys 列的一部分!

同样的请求,带有覆盖索引的方式

EXPLAIN SELECT date FROM `test1_nopart` WHERE date = "2014-03-04"

结果:

id  select_type     table      type     possible_keys   key     key_len     ref     rows        Extra
1   SIMPLE       test1_nopart   index   NULL            date       3        NULL    7875981     Using where; Using index

=> 3000 行的结果(大约)没有解释 2.8 秒

为什么 MySQL 不能正确使用这个索引(DATE)???

信息: - VM Server(我们的开发环境,不知道是什么硬件组成) - MySQL 5.5.8

SHOW INDEX FROM test1_nopart

结果:

Table   Non_unique  Key_name    Seq_in_index    Column_name     Collation   Cardinality     Sub_part    Packed  Null    Index_type  Comment     Index_comment
test1_nopart    0   PRIMARY     1   id  A   7875981     NULL    NULL        BTREE        
test1_nopart    1   date    1   date    A   6077    NULL    NULL        BTREE        
  • 对于日期 2014-03-04 => 3134 行
  • 总计(汇总)=> 7 875 488
  • 表格中有 2556 个不同的“日期”值

【问题讨论】:

  • SHOW INDEX FROM test1_nopart 的输出是什么,尤其是索引基数?另外,为什么要将列命名为 MySQL 的保留字?
  • 哎呀,看来6077很低了……
  • 基数不是真正的问题。当您运行查询 SELECT COUNT(1) datecount,date` FROM test1_nopart GROUP BY date WITH ROLLUP;` 您将看到真正的基数。您还将看到 2014-03-14 占用了多少行。

标签: mysql date indexing explain covering-index


【解决方案1】:

这不是基数问题。

我做了很多测试,我又发了一篇描述问题的帖子。

https://stackoverflow.com/questions/8679940/primary-key-index-with-a-datetime-as-first-part-of-the-compound-key-is-never-use

只有当第一个键是日期时间时才会出现问题...

【讨论】:

    【解决方案2】:

    这是我的想法。

    在第一种情况下,当我们尝试通过date 获取data 时,由于基数非常低,MySQL 不会在date 上使用索引。优化器使用以下内容: - 二级索引 - 聚集以访问行 - 获取数据的表格。

    在第二种情况下,当我们尝试通过date 获取date 时,使用索引更容易通过表,因为 MySQL 也可以从索引中检索选择数据(我的意思是 MySQL 可以只扫描索引而不是整个表来获取相同的数据)。使用以下内容: - 二级索引

    【讨论】:

      【解决方案3】:

      MySQL 查询优化器发现日期索引的索引遍历包括对聚集索引的深入了解(内部称为gen_clust_index)。鉴于此,MySQL 查询优化器认为在第一个查询中执行全表扫描,在第二个查询中执行全索引扫描更容易。

      您可能还需要查看索引的基数以及每个不同值有多少行。

      执行以下操作:

      SELECT COUNT(1) datecount,`date` FROM test1_nopart GROUP BY `date` WITH ROLLUP;
      

      根据您的评论,您将获得 6077 个不同的行。您还说大约有 10,000,000 行。改为运行此查询:

      SELECT COUNT(1) datecount FROM test1_nopart WHERE `date` = '2014-03-14';
      

      请注意计数和总数。

      10,000,000 的 5% 是 500,000

      如果日期为 '2014-03-14' 的行数超过 500,000 行,则 MySQL 将永远不会为该特定值正确使用索引。

      我不信任SHOW INDEXES FROM test1_nopart;,因为该表是 InnoDB。 MyISAM 将显示确切的数字。 InnoDB 根据 Dives into the Index 生成数字。

      如果任何日期的日期计数超过总行数的 5%,MySQL 查询优化器将举手进行全面扫描。

      更新

      好的,5% 的经验法则已经不存在了。尝试通过创建不同的覆盖索引来欺骗 MySQL 查询优化器:

      ALTER TABLE test1_nopart ADD INDEX date_id_ndx (`date`,id);
      

      并再次尝试查询。

      【讨论】:

      • 我认为我属于这种情况,因为我的基数非常低 (6077)
      • 对于日期 2014-03-04 => 总计 3134(汇总)7 875 488
      • 你认为这是一件坏事吗?
      • 如果你能忍受 7,875,488 行的运行时间,那也不是坏事。
      【解决方案4】:

      只是一种预感 - 也许它与 date 这个词有关。

      尝试给 MySQL 一些提示,你想使用该字段,而不是保留字:

      SELECT date FROM `test1_nopart` WHERE `test1_nopart`.`date` = "2014-03-04"
      

      【讨论】:

      • 跟日期这个词没关系,我用`试了一下,结果一样
      猜你喜欢
      • 1970-01-01
      • 2011-03-10
      • 1970-01-01
      • 1970-01-01
      • 2012-04-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多