Mysql InnoDB聚集复合索引的行为
我们正在使用下表运行MySQL/ISAM数据库:Mysql InnoDB聚集复合索引的行为,mysql,performance,indexing,innodb,clustered-index,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_戳记-fk_频道组合要求唯一,因此复合主键。现在,由于某些不
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_戳记
-fk_频道
组合要求唯一,因此复合主键。现在,由于某些不相关的原因,数据库将迁移到InnoDB引擎。通过谷歌搜索,我发现这个键将决定磁盘上数据的物理顺序。目前90%的查询如下所示:
SELECT value FROM measurements
WHERE fk_channel=A AND tm_stamp>=B and tm_stamp<=C
ORDER BY tm_stamp ASC
从测量值中选择值
如果fk_channel=A和tm_stamp>=B和tm_stamp您在WHERE
子句中的参数顺序在这里是不相关的,那么优化器将选择最佳键选项(通常在索引字段上直接比较>或<比较)。在您最初的示例中,最好的选择是tm_stamp
比较,它不是直接的相等性检查,因此低于标准
然而,聚集键的顺序并不重要。。。。如果确切的比较总是在fk_通道列上,我会将PK更改为:
PRIMARY KEY (`fk_channel`,`tm_stamp`)
现在您有了一个索引,它将受益于where子句中的fk_channel=A
另外,虽然存储引擎在某种程度上发挥了作用,但我认为这里的问题不在innodb和myisam之间
最后,我认为,orderby
子句与您的问题没有多大关系,这是在查询后完成的。团队成员可能会影响您的绩效 盯着查询
SELECT value FROM measurements
WHERE fk_channel=A AND tm_stamp>=B and tm_stamp<=C
ORDER BY tm_stamp ASC
至于第一个问题,存储引擎决定缓存什么
- MyISAM仅在密钥缓存中缓存索引页(按大小)
- InnoDB在缓冲池中缓存数据和索引(按大小)
如果您仍然使用MyISAM,则可以更改主键以包含值
列:
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_频道
,tm_stamp
将两次加载到RAM中
- 一次从InnoDB数据页
- 一次从InnoDB索引页
编辑1:
将主键从
PRIMARY KEY (`tm_stamp`,`fk_channel`)
PRIMARY KEY (`tm_stamp`,`fk_channel`)
到
对于MyISAM和InnoDB来说,这总是有意义的。看看这是什么证据
原始答复:
确定是否要更改
到
为了提高查询的性能,您需要确定哪个字段的值基数更高(哪个字段的值变化更大)。运行
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;
返回32768和256。32768除以256等于128。这告诉我们,tm_stamp
对于fk_通道的每个值都有128个唯一值
因此,如果B
和C
之间的差值通常小于128,则将tm_stamp
保留为主键中的第一个字段。如果为128或更大,则将fk_频道
设为第一个字段
另一个问题:fk_频道
是否需要为INT
(40亿个唯一值,其中一半为负值)?如果没有,则将fk_频道
更改为TINYINT UNSIGNED
(如果有256个唯一值),或SMALLINT UNSIGNED
(65536个唯一值)将节省大量时间和空间
例如,假设您有256个最大可能的fk_通道
值和65536个可能的值
s,那么您可以通过以下方式更改模式:
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;
这将以主键
的顺序将现有数据存储在新表中,这将在一定程度上提高性能。对于向键添加值
的新想法,您可能会发现以下有趣的答案+1。我们将尝试一下。接受原始答案和修改后的sqlfiddle证明中的可见努力的答案。虽然B
和C
之间的差异大致在1-10.000之间(即,对于一个频道),但我理解DISTINCT
查询是指整个表。在那里,我们每个fk\u频道获得了数千万张tm\u邮票。因此,交换主键顺序的有用性更为合理。而fk_频道实际上是一个小玩意,我只想让描述变得简单。
SELECT COUNT(DISTINCT tm_stamp), COUNT(DISTINCT fk_channel) FROM measurements;
SELECT COUNT(DISTINCT tm_stamp), COUNT(DISTINCT fk_channel) FROM measurements;
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;