【问题标题】:MySQL not using index with selective INT keyMySQL 不使用带有选择性 INT 键的索引
【发布时间】:2015-08-31 19:03:12
【问题描述】:

我创建了如下所列的表格测量值。 该表是定期写入的,几天后将迅速增长到包含数百万行。 读取时:我只需要测量的精确时间及其值(unix_epoch 和值)。

为了提高性能,我添加了 date_from_epoch 列,它是从 unix_epoch(测量精确时间)中提取的日期,格式为:yyyymmdd。它应该具有良好的选择性(在将多天的测量值写入表之后),我将它用作索引的键。我希望只扫描我想要读取测量值的日期,而不是表中存在的所有日期(例如:10 天后,如果每天添加 1,000,000 行,我希望只扫描 1,000,000 行,如果我需要一天内包含的数据,而不是 10,000,000)。

我也有:

  • 引擎使用了innoDB
  • 通过哈希将表划分为 10 个文件以帮助处理 I/O
  • 确保我的查询中使用的类型与列类型相同(还是我的验证有误?)。

问题: 在测量表中的测量结果持续 2 天后,我进行了测试。 使用 EXPLAIN,我看到我的读取查询不使用索引。为什么查询不使用索引?

表是通过以下方式创建的:

CREATE TABLE measurements(
date_from_epoch INT UNSIGNED,
unix_epoch INT UNSIGNED,
application_name varchar(255),
environment varchar(255),
metric_name varchar(255),
host_name varchar(1024),
value FLOAT(38,3)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
PARTITION BY HASH(unix_epoch)
PARTITIONS 10;

CREATE TRIGGER write_epoch_day 
BEFORE INSERT ON measurements
FOR EACH ROW
SET NEW.date_from_epoch = FROM_UNIXTIME(NEW.unix_epoch, '%Y%m%d');

ALTER TABLE measurements ADD INDEX (date_from_epoch);

查询是:

EXPLAIN SELECT * FROM measurements
WHERE date_from_epoch >= 20150615 AND date_from_epoch <= 20150615
AND unix_epoch >= 1434423478 AND unix_epoch <= 1434430678
AND BINARY application_name = 'all'
AND BINARY environment = 'prod'
AND BINARY metric_name = 'Internet availability'
AND (BINARY host_name = 'kitkat' )
ORDER BY unix_epoch ASC;

解释给出:

id  select_type         table           type        possible_keys       key         key_len         ref         rows       Extra     
-------------------------------------------------------------------------------------------------------------------------------------------------------
1   SIMPLE              measurements    ALL         date_from_epoch                                             118011     Using where; Using filesort 

感谢您的阅读和头疼!

【问题讨论】:

    标签: mysql performance indexing


    【解决方案1】:

    有一个选项可以在 MYSQL 中使用 FORCE INDEX

    请参阅this 以获得更好的理解。

    【讨论】:

    • 我分享的链接,还提到了其他使用索引的方法。你可以检查一下
    【解决方案2】:

    谢谢萨西!

    我已将查询修改为

    EXPLAIN SELECT * FROM measurements FORCE INDEX (date_from_epoch)
    WHERE date_from_epoch >= 20150615 AND date_from_epoch <= 20150615
    AND unix_epoch >= 1434423478 AND unix_epoch <= 1434430678
    AND BINARY application_name = 'all'
    AND BINARY environment = 'prod'
    AND BINARY metric_name = 'Internet availability'
    AND BINARY host_name = 'kitkat'
    ORDER BY unix_epoch ASC; 
    

    解释说仍然是“使用位置;使用文件排序”,但现在扫描的行数已降至 67,906,而最初扫描的行数为 118,011(这很棒)。

    虽然 date_from_epoch = 20150615 的行数是 113,182。我现在想知道为什么扫描的行数不是 113,182(不是我希望它上升,但我想了解 mysql 为进一步优化执行做了什么)。

    【讨论】:

      【解决方案3】:

      很多事情需要修复:

      • 不要使用PARTITION BY HASH;它没有帮助。
      • 由于分区键有一个范围,它必须触及所有分区。见EXPLAIN PARTITIONS SELECT ...
      • 不用担心额外的 epoch_from_date 和 Trigger;只需在 unix_epoch 上进行比较。 (请参阅有关所需转换例程的手册。)
      • 不要使用BINARY。而是将列指定为COLLATION utf8_bin。性能会好很多。
      • 规范化(或变成 ENUM)这些字段:application_name、environment、metric_name、host_name。对于数百万行而言,您拥有的内容不必要地庞大。 (我假设这些字段只有几个不同的值。)节省空间将使SELECT 运行得更快。
      • FLOAT(38, 3) 有一个额外的(不必要的?)四舍五入。只需使用FLOAT
      • (进行上述更改后)INDEX(application_name, environment, metric_name, host_name, unix_epoch) 会很有帮助,至少对于那个查询而言。而且会比你问的INDEX好很多。

      【讨论】:

        猜你喜欢
        • 2018-05-10
        • 2012-08-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-10-11
        • 1970-01-01
        • 2011-10-16
        • 1970-01-01
        相关资源
        最近更新 更多