MariaDB / MySQL:table 的索引结构,只有一个值列

MariaDB / MySQL: index structure for table with one value column

我有一个包含三列的 MariaDB MyISAM table:

`time` int not null, 
`sensor` tinyint not null, 
`value` decimal (6,4) not null

此外,我在 timesensor

列上有一个主键 (BTREE)

table 有 2.5 亿行,每 10 秒添加 20 条新记录。 table 上的所有 SELECTS 在 timetimesensor.

上都有一个 WHERE 子句

这在性能方面效果很好,但索引使用的磁盘 space 比 table 本身更多(2.2GB 用于数据,2.7GB 用于索引)。这对我来说似乎有点傻,因为索引基本上是整个 table,这意味着 MariaDB 基本上将所有数据翻了一番。

这个table有更好的结构吗?

数据库索引的全部意义在于用space换取时间。是的,索引占用 space 和 table 大致一样多是正常的,尤其是当 table 像您的那样有短行时。

如果您切换到 InnoDB 存储引擎,您的主键将变成所谓的 clustered index 也就是说,整个 table 将是包含在主键的索引中。那可以节省很多磁盘space.

你应该切换到 InnoDB:MyISAM 是一个遗留的存储引擎,没有得到 MariaDB 开发人员的太多关注。如果出于某种原因 InnoDB 不适合您,请切换到更现代的 Aria 存储引擎。它类似于 MyISAM。与 MyISAM 一样,它不使用聚簇 PK 索引。

请注意:您的主键在 (time, sensor) 上。这意味着它最适合像这样的 where 子句:

 WHERE time BETWEEN start AND finish

如果您重新制作主键,使其位于 (sensor, time) 上,它将是 suitable for

 WHERE sensor=somesensor AND time BETWEEN start AND finish

为什么? MySQL 随机访问 BTREE 索引到第一个符合条件的行,然后按顺序扫描到最后一个符合条件的行。您可以阅读有关多列索引的内容 here and here

因此,您对主键列顺序的选择应基于这两个 WHERE 模式中哪一个对性能更关键:仅时间范围或传感器和时间范围。

如果这是我的 table 我会这样定义它:

CREATE TABLE series (
    time TIMESTAMP NOT NULL DEFAULT current_timestamp(),
    sensor SMALLINT(6) NOT NULL DEFAULT '0',
    value DECIMAL(6,4) NOT NULL DEFAULT '0.0000',
    PRIMARY KEY (sensor, time) USING BTREE,
    INDEX time_covering (time, sensor, value) USING BTREE
) COLLATE='utf8mb4_general_ci' ENGINE=InnoDB;

这个 table 定义提供了一个 clustered primary key optimized for filtering by sensor then time range. And I have added a covering index (basically a copy of the table) optimized for filtering by time range only. For information about covering indexes see the last section of this.

它使用 TIMESTAMP 数据类型。它们在将时间表示为整数方面同样高效,并且您可以从时间算术中获益。这会获取昨天传感器 3 的读数。

WHERE time >= CURDATE() - INTERVAL 1 DAY
  AND time < CURDATE()
  AND sensor = 3

它使用 SMALLINT 而不是 TINYINT 作为传感器编号。您不太可能 运行 超出传感器数量,并且 TINYINT 数据仅在每行中有多个传感器时才有助于节省 space。