2

我们正在使用下表运行 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_stampfk_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 是否重要?这个博客提出了一些类似的建议。
  3. 由于聚集索引的性质,我们是否可以省略 ORDER BY 子句并获得一些性能?
4

3 回答 3

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;

会给你列的基数。

因此,要正确回答您的问题,我们首先需要知道: 和 之间的常见值范围是B什么C?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对于 的每个值都有 128 个唯一值fk_channel

所以如果和之间的差值B通常C小于128,那么就留下tm_stamp作为主键中的第一个字段。如果 128 或更大,则创建fk_channel第一个字段。

另一个问题:是否fk_channel需要是一个INT(40 亿个唯一值,其中一半是负数)?如果没有,那么更改fk_channelTINYINT 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,这将在一定程度上提高性能。

于 2012-10-24T23:17:28.230 回答
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;

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

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

  • 一次来自 InnoDB 数据页面
  • 一次来自 InnoDB 索引页面
于 2012-10-24T20:36:32.083 回答
0

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

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

   PRIMARY KEY (`fk_channel`,`tm_stamp`)

现在你有了一个索引,它可以从fk_channel=A你的 where 子句中受益。

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

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

于 2012-10-24T20:29:19.857 回答