多个表还是使用分区?

时间:2011-09-05 14:45:40

标签: mysql database-design partitioning

我已经看到这个问题在很多线程上几乎都得到了答案,但没有考虑到对这个特定领域的影响:

我希望在MySQL中存储时间序列数据,用于大量的仪表(500和增长),每个仪表以5分钟的间隔提供单个浮点值。最简单的结构是: - gauge_id - 时间戳 - 值

(其中gauge_id和timestamp组合为主键)

这意味着每年每个规格大约105120行 - 所有这些都需要存储10年或20年。对于1000个仪表,我们每年将关注1亿个记录。

数据是分批编写的,通常每个通道的值都会从远程源聚合到XML文件中,并按小时或每天读入数据库。最多,每小时的插入数量与我们的量规数一样多。

根据时间范围,对数据的读取操作将是每个规格(因此没有规格之间的数据连接操作)。所以例如获取两个日期之间的仪表X的所有值。 通常,这还将包括某种形式的聚合/插值函数 - 因此用户可能希望查看任意范围的每日平均值或每周最大值等。 同样,读取次数相对较少,但这些读取需要在1秒内从MySQL获得响应。

在这个阶段,我倾向于每个表1个表,而不是在gauge_id上​​将MySQL中的一个巨大的表分区。 逻辑是,这将使分片更容易,简化备份,并且如果在任何阶段存在数据错误,基本上可以使仪表更容易删除/重建。 成本是写入和读取操作都稍微复杂一些。

对此有何想法?

-------- UPDATE --------

我在我的MacBook 2.4gHz核心2二重奏,4演出的ram上进行了一些测试。

设置下表:

CREATE TABLE `test` (
  `channel_id` int(10) NOT NULL,
  `time` datetime NOT NULL,
  `value` int(10) NOT NULL,
  KEY `channel_id` (`channel_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

填充了存储过程:

CREATE PROCEDURE `addTestData`(IN ID INT, IN RECORDS INT)
    BEGIN
        DECLARE i INT DEFAULT 1;
        DECLARE j DATETIME DEFAULT '1970-01-01 00:00:00';
           WHILE (i<=RECORDS) DO
              INSERT INTO test VALUES(ID,j,999);
              SET i=i+1;
              SET j= j + INTERVAL 15 MINUTE;
           END WHILE;
    END $$

然后我打电话来创建前100万条记录

call addTestData(1,1000000);

插入执行47秒

SELECT * FROM `test` WHERE channel_id = 1 and YEAR(time) = '1970';

以0.0006秒执行

SELECT AVG(value) as value, DATE(time) as date FROM `test` 
WHERE channel_id = 1 and YEAR(time) = '1970' group by date;

在4.6秒内执行(MAX,SUM功能同时执行)。

再添加4个仪表后:

call addTestData(2,1000000);
call addTestData(3,1000000);
call addTestData(4,1000000);
call addTestData(5,1000000);

每次执行插入47秒,78兆字节用于表

我运行了相同的两个查询 - 并且获得了与表中100万条记录完全相同的执行时间(更大的查询为4.6秒)。

因此,禁止将分片,备份和未来硬件驱动的更改用于任何单个仪表的表(即多个读数,数据间隔的变化),似乎没有必要为可预见的分成多表。甚至没有尝试使用分区运行查询,似乎没有任何理由。

-------- -------------无论其

由于查询的4.6秒不理想,我们显然需要做一些优化。 作为第一步,我重新构建了查询:

SELECT 
    AVG(value) as value, 
    DATE(time) as date 
FROM 
    (SELECT * FROM test 
    WHERE channel_id = 1 and YEAR(time) = '1970') 
    as temp 
group by date;

在包含500万条记录(超过5个channel_id)的表上运行,查询需要4.3秒。 如果我在一个包含1个频道,100万条记录的桌面上运行它,它会在0.36秒内运行!! 抓住我的头稍微过了一点......

对500万条记录的表进行分区

ALTER TABLE test PARTITION BY HASH(channel_id) PARTITIONS 5;

随后在0.35秒内完成上面的复合查询,性能相同。

1 个答案:

答案 0 :(得分:3)

对于我来说,在你的场景中没有任何东西可以证明按照规范进行分区是合理的,如果你有一个关于gauge_id的索引,性能不会成为问题,因为MySql会立即通过使用索引找到与某个规格相关的行,之后其他操作就像处理每个仪表的专用表格一样。

分区可能合理的唯一情况是,如果您访问最近的计量数据(比如说最新的10%),那么旧数据(剩余的90%)就会被分成两个“最近”和“存档“表可能会给你带来很多性能优势。

如果您对单个表的操作不涉及索引,那么相同的操作不应该在合并表上花费更长的时间,因为MySql首先使用gauge_id上​​的索引将结果缩小到特定的量表行,如果操作涉及一个索引,你应该使索引成为合并表的多列索引,以'gauge_id'开头,例如单个表上的INDEX( timestamp )应该变为INDEX( gauge_id, timestamp ),然后在大多数情况下,操作将与单个表同时进行。另外,不要被像“5亿行”这样的数字推迟,数据库被设计用于处理大量数据。

我的言论几乎都是基于经验,几乎每次我都处于你的情况,并决定使用单独的表,由于某种原因我最终将表合并为一个,因为大多数时候发生在项目已经成熟,这是一个痛苦的过程。我确实经历过“关系数据库的设计并不像那样”。

我真的很想听到其他人的意见,顺便说一下,在做任何一种方式之前做了很多测试,MySql有很多unexpected behaviors