【问题标题】:Behavior of InnoDB clustered compound indexInnoDB 聚集复合索引的行为
【发布时间】:2012-10-14 22:59:52
【问题描述】:

我们正在使用下表运行 MySQL/ISAM 数据库:

create table measurements (
  `tm_stamp` int(11) NOT NULL DEFAULT '0',
  `fk_channel` int(11) NOT NULL DEFAULT '0',
  `value` int(11) DEFAULT NULL,
  PRIMARY KEY (`tm_stamp`,`fk_channel`)
);

tm_stamp-fk_channel 组合必须是唯一的,因此是复合主键。现在,由于某些不相关的原因,数据库将迁移到 InnoDB 引擎。在谷歌搜索后,我发现密钥将决定磁盘上数据的物理顺序。目前 90% 的查询如下:

SELECT value FROM measurements
WHERE fk_channel=A AND tm_stamp>=B and tm_stamp<=C
ORDER BY tm_stamp ASC

插入是 99%,按 tm_stamp 的顺序排列,它是数据记录器网络的存储。该表有数百万行,但稳定增长。问题是

  1. 存储引擎的单独更改是否应该导致任何显着的性能变化,更好还是更差?
  2. 对于最流行的 SELECT,索引中的列顺序是否重要? This blog 提出类似的建议。
  3. 由于聚集索引的性质,我们是否可以省略 ORDER BY 子句并获得一些性能?

【问题讨论】:

标签: mysql performance indexing innodb clustered-index


【解决方案1】:

WHERE 子句中的参数顺序在这里无关紧要,优化器将选择最佳键选项(通常是在索引字段上直接比较 > 或 tm_stamp 比较,它不是直接的相等检查,因此低于标准。

但是,聚集键的顺序确实很重要....如果确切的比较总是在 fk_channel 列上,我会将 PK 更改为:

   PRIMARY KEY (`fk_channel`,`tm_stamp`)

现在您有了一个索引,它将受益于 where 子句中的 fk_channel=A

此外,虽然存储引擎在某种程度上发挥了作用,但我认为这里的问题不是 innodb 和 myisam 之间的问题。

最后,我认为ORDER BY 子句对您的问题没有太大影响,这是在查询后完成的。分组可能会影响您的表现....

【讨论】:

    【解决方案2】:

    盯着查询

    SELECT value FROM measurements
    WHERE fk_channel=A AND tm_stamp>=B and tm_stamp<=C
    ORDER BY tm_stamp ASC
    

    您的静态值为fk_channel,移动有序值为tm_stamp。这解决了您的第二个问题,这似乎是查询需求的核心。

    PRIMARY KEY 列颠倒过来会更好

    create table measurements (
      `tm_stamp` int(11) NOT NULL DEFAULT '0',
      `fk_channel` int(11) NOT NULL DEFAULT '0',
      `value` int(11) DEFAULT NULL,
      PRIMARY KEY (`fk_channel`,`tm_stamp`)
    );
    

    至于第一个问题,存储引擎决定了缓存的内容。

    如果您仍然使用 MyISAM,您可以更改主键以包含 value 列:

    create table measurements (
      `tm_stamp` int(11) NOT NULL DEFAULT '0',
      `fk_channel` int(11) NOT NULL DEFAULT '0',
      `value` int(11) DEFAULT NULL,
      PRIMARY KEY (`fk_channel`,`tm_stamp`,`value`)
    ) ENGINE=MyISAM;
    

    这样,您的查询的数据检索最多严格来自一个文件,即 MyISAM 表的 .MYI。该表根本不需要阅读。

    如果您切换到 InnoDB,fk_channel,tm_stamp 会两次加载到 RAM 中

    • 一次来自 InnoDB 数据页
    • 一次来自 InnoDB 索引页面

    【讨论】:

    • +1 表示将value 添加到密钥的新颖想法。我们会尝试的。
    【解决方案3】:

    编辑 1

    看来要更改主键

    PRIMARY KEY (`tm_stamp`,`fk_channel`)
    

    PRIMARY KEY (`fk_channel`,`tm_stamp`)
    

    对 MyISAM 和 InnoDB 来说总是有意义的。请参阅http://sqlfiddle.com/#!2/0aa08/1 以证明确实如此。

    原答案:

    判断是否改变

    PRIMARY KEY (`tm_stamp`,`fk_channel`)
    

    PRIMARY KEY (`fk_channel`,`tm_stamp`)
    

    会提高查询的性能,您需要确定哪个字段的值基数更高(哪个字段的值更多样化)。运行

    SELECT COUNT(DISTINCT tm_stamp), COUNT(DISTINCT fk_channel) FROM measurements;
    

    会给你列的基数。

    所以,要正确回答您的问题,我们首先需要知道:BC 之间的常见值范围是多少? 60? 3,600? 86,400?更多?

    比方说

    SELECT COUNT(DISTINCT tm_stamp), COUNT(DISTINCT fk_channel) FROM measurements;
    

    返回 32,768 和 256。32,768 除以 256 是 128。这告诉我们,tm_stamp 对于fk_channel 的每个值都有 128 个唯一值。

    所以如果BC之间的差值通常小于128,那么将tm_stamp作为主键中的第一个字段。如果为 128 或更大,则将 fk_channel 设为第一个字段。

    另一个问题:fk_channel 是否需要是 INT(40 亿个唯一值,其中一半是负数)?如果没有,那么将fk_channel 更改为TINYINT UNSIGNED(如果您有256 个唯一值)或SMALLINT UNSIGNED(65536 个唯一值)将节省大量时间和空间。

    例如,假设您有最多 256 个可能的 fk_channel 值和 65,536 个可能的 values,那么您可以通过以下方式更改架构:

    create table measurements_new (
      tm_stamp INT UNSIGNED NOT NULL DEFAULT '0',
      fk_channel TINYINT UNSIGNED NOT NULL DEFAULT '0', -- remove UNSIGNED if values can be negative
      value SMALLINT UNSIGNED DEFAULT NULL, -- remove UNSIGNED if values can be negative
      PRIMARY KEY (tm_stamp,fk_channel)
    ) ENGINE=InnoDB
    SELECT
      tm_stamp,
      fk_channel,
      value
    FROM
      measurements
    ORDER BY
      tm_stamp,
      fk_channel;
    
    RENAME TABLE measurements TO measurements_old, measurements_new TO measurements;
    

    这会将现有数据以PRIMARY KEY的顺序存储在新表中,这将在一定程度上提高性能。

    【讨论】:

    • 接受原始答案中的可见努力以及修订后的 sqlfiddle 证明的答案。虽然BC 之间的差异大致在1-10.000 左右(即对于一个频道),但我理解DISTINCT 查询指的是整个桌子。每fk_channel,我们得到数千万 tm_stamps。所以交换主键顺序的用处就更充分了。而fk_channel实际上是一个TINYINT,我只是想让描述简单一点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-04-30
    • 2016-11-23
    • 2011-03-13
    • 2015-04-16
    • 2015-05-13
    • 2019-06-24
    • 1970-01-01
    相关资源
    最近更新 更多