InnoDB聚类复合索引的行为

时间:2012-10-24 20:22:44

标签: mysql performance indexing innodb clustered-index

我们正在使用下表运行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

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

  1. 存储引擎的唯一变化是否会导致任何重大的性能变化,无论好坏?
  2. 索引中列的顺序是否与最流行的SELECT有关? This blog提出了一些建议。
  3. 由于聚集索引的性质,我们可能会忽略ORDER BY子句并获得一些性能吗?

3 个答案:

答案 0 :(得分:1)

盯着查询

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;

这样,您的Query的数据检索严格来自一个文件,即MyISAM表的.MYI。该表格根本无需阅读。

如果你切换到InnoDB,fk_channeltm_stamp被加载到RAM中两次

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

答案 1 :(得分:1)

修改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? 3600? 86,400?更?

例如,让我们说

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

返回32,768和256. 32,768除以256是128.这告诉我们tm_stampfk_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个value个,那么您可以通过以下方式更改架构:

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顺序将现有数据存储在新表中,这将在某种程度上提高性能。

答案 2 :(得分:0)

WHERE子句中参数的顺序在这里是irrellavent,优化器将选择最佳密钥选项(通常是对&gt;或&lt;比较的索引字段进行直接比较)。在您的初始示例中,最佳选择是tm_stamp&lt;&gt;比较不是直接的平等检查,因此低于标准。

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

   PRIMARY KEY (`fk_channel`,`tm_stamp`)

现在,您的索引将从where子句中的fk_channel=A中受益。

此外,虽然存储引擎在某种程度上发挥了作用,但我认为这里的问题不在于innodb&amp; MyISAM数据。

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