多个表或使用分区?
作者:互联网
我已经在多个线程上看到了几乎可以回答的问题,但没有考虑此特定域的含义:
我正在寻找将时间序列数据存储在MySQL中的大量量表(500个和不断增长的数据),每个量表每隔5分钟提供一个浮点值.最简单的结构是:
-gauge_id
-时间戳
-价值
(其中gauge_id和timestamp组合为主键)
这意味着每年每条规约有105120行-所有这些都需要存储10或20年.届时,对于1000个量规,我们每年将查看1亿条记录.
数据是成批写入的,通常每个通道的值都会从远程源聚合到XML文件中,并每小时或每天读入数据库.因此,每小时最多可以有我们的量规.
数据的读取操作将基于时间范围按规范进行(因此,规范之间不会进行数据的联接操作).所以例如获取两个日期之间的量规X的所有值.
通常,这还将包括某种形式的聚合/插值功能-因此用户可能希望查看任意范围的每日平均值或每周最大值等.
同样,读取次数相对较少,但是这些读取需要MySQL不到1秒的响应.
在这个阶段,我倾向于每个表1个表,而不是在MySQL上对gauge_id分区一个大表.
这样做的逻辑是,这样可以使分片更容易进行,简化备份,并且在任何阶段都存在数据错误时,实质上可以使量规更易于拆卸/重建.
代价是写和读操作都比较复杂.
有什么想法吗?
——–更新——–
我在MacBook 2.4gHz核心2 duo,4 gig 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 $$
然后我打电话来创建前一百万条记录
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 MB
我运行相同的两个查询-并获得与表中一百万条记录完全相同的执行时间(较大的查询为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秒内完成上述复合查询,同样的性能提升.
解决方法:
对我来说,您的方案中没有任何东西可以证明按量规进行分区,如果在gauge_id上有索引,性能就不会成为问题,因为MySql会通过使用索引立即找到与特定量规相关的行,之后其他操作将是就像处理每个量规的专用表一样.
唯一可以进行分区的情况是,如果访问最新的量规数据(例如,最新的10%),则比旧数据(剩余的90%)更频繁地访问(如果是这样的话),则分为两个“最近”和“存档”表可能会给您带来很多性能优势.
如果您对单个表的操作不涉及索引,则对合并表的相同操作不应花费更长的时间,因为如果操作涉及索引,则MySql首先会使用gauge_id上的索引将结果缩小到某些表行您应该使索引成为合并表上以“ gauge_id”开头的多列索引,例如单个表上的INDEX(timestamp)应该变为INDEX(gauge_id,timestamp),然后在大多数情况下,该操作将与单个表花费相同的时间.同样,不要被“ 5亿行”之类的数字所困扰,数据库旨在处理该数量的数据.
我的言论几乎都是基于我每次遇到您的情况时的经验,并决定使用单个表,出于某种原因,我最终将表合并为一个表,因为在大多数情况下,项目已经成熟这是一个痛苦的过程.我确实经历过“关系数据库并非旨在像那样使用”.
我真的很想听听其他人对此的意见,顺便说一下,在进行任何一种测试之前,都要进行大量测试,MySql的工作量很多(unexpected behaviors).
标签:database-design,partitioning,mysql 来源: https://codeday.me/bug/20191208/2087923.html