我们正在使用下表运行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%,它是数据记录器网络的存储空间。该表有数百万行,但稳步增长。问题是
答案 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_channel
,tm_stamp
被加载到RAM中两次
答案 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;
将为您提供列的基数。
因此,要正确回答您的问题,我们首先需要知道:B
和C
之间的常见值范围是多少? 60? 3600? 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个唯一值。
因此,如果B
和C
之间的差异通常小于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
子句对你的问题有多大影响,这是在查询后完成的。分组可能会影响您的表现....