Mysql InnoDB聚集复合索引的行为

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_频道组合要求唯一,因此复合主键。现在,由于某些不

我们正在使用下表运行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_频道
组合要求唯一,因此复合主键。现在,由于某些不相关的原因,数据库将迁移到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;