我正在尝试实现一个新的数据库模式模板来有效地存储/检索“公园”的时间序列数据。
公园有多个设备(和子设备),每个都有它的信号。在公园里可以有2-5k设备,在某些情况下甚至更多。通常情况下,公园的信号时间分辨率相同,通常为5-10-15分钟或1小时。
由于每个公园可以拥有不同数量的设备,每个设备具有不同数量的具有不同时间分辨率的数据信号,因此我必须创建一个适用于所有情况的数据库模板。
在我们的系统中,有一个API经常读取最近的数据(最近一周),而只是偶尔读取历史数据(当最终用户通过接口请求它时)。这对于聚合新数据的后端进程(例如从5分钟到1小时的分辨率等)有效,并且仅在手动请求时才对历史数据执行此操作。 历史数据还将用于使用专用软件对公园进行一些离线分析。
重要的是能够迁移数据库/表并在出现问题时快速恢复它们。
我想到了两个选择:
您是否有其他想法更符合目的,并强调不同方法的所有优点和缺点?
这里有一些关于如何存储设备的信息:
CREATE TABLE `Device` (
`id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
`devicetype_id` smallint(5) unsigned NOT NULL,
`parent_id` smallint(5) unsigned DEFAULT NULL,
`name` varchar(50) NOT NULL,
`displayname` varchar(30) DEFAULT NULL,
`status` tinyint(4) NOT NULL DEFAULT '1',
PRIMARY KEY (`id`),
UNIQUE KEY `dev_par` (`name`,`parent_id`)
) ENGINE=InnoDB
以及如何存储数据:
CREATE TABLE `Data_raw` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`device_id` smallint(5) unsigned NOT NULL,
`datetime` datetime NOT NULL COMMENT '[UTC] beginning of timestep',
`value` float NOT NULL,
`signal_id` smallint(5) NOT NULL,
`modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB
其他信息:
公园数量:150,很快将在500左右。每个公园将有1个模式。
公园每年平均拥有80Mln行数据。历史可以达到最多20年,但平均而言,我们已经/将达到5年。考虑到这一点,考虑到Rick估计的50b /行,我们每年将达到~5GB,因此历史可达到约50GB。
考虑到AWS Aurora MySQL可以达到64TB,所有公园都可以放入数据库中。在未来最糟糕的情况下,我们可以将一个客户的公园分成不同的数据库分片,这不是问题。
对于数据库,我们将在AWS上使用MySQL Aurora,主数据库目前有16GB的RAM和4个vCPU(我们也可以增加,优化后台进程/插入数据),然后在那里将是该数据库的只读副本,其中不同的硬件规格针对API方面的操作进行了优化。
所有历史记录都必须存储和可用(对于历史分析,数据下载等不常用的操作......)。
还有哪些其他信息可以帮助您理解/确定更好的选择? 谢谢你的时间。
答案 0 :(得分:1)
仅当您打算删除“旧”数据时,分区才有用。更多讨论:http://mysql.rjweb.org/doc.php/partitionmaint
您可能需要Data_raw的索引。和/或您可能需要Summary tables。
如果它确实是UTC,请考虑使用TIMESTAMP
;这可以避免时区损坏。
如果您在同一秒内无法获得两个读数,请将PK更改为(device_id, datetime)
并抛弃无用的id
。
“”当前数据“表,其中存储所有信号数据以便快速访问” - 上述对PK的更改导致每个设备的“当前数据”聚集在一起;不需要单独的桌子;分区的好处不足以依赖它。
“定期移动” - 不值得编程。
“整个公园离线一段时间” - 很好。不,没有任何重要的“漏洞”。
modified
似乎毫无用处,浪费空间。
使用InnoDB。
给我们一些数字。 RAM大小。行数。保留时间。公园数量。等我在这个领域有经验;我想“运行数字”以查看是否还有其他问题需要指出。
更多强>
PRIMARY KEY(device_id, datetime)
- 如果可能存在重复,请考虑在新行到达时使用INSERT ... ON DUPLICATE KEY UPDATE ...
插入或替换。这是一步。
大表可以拥有索引。汇总表避免了对大表上的大多数索引的需要。
汇总表具有您需要决定“时间”粒度的限制。在商业应用中,“日”通常就足够了。对于传感器监测,“小时”可能更合适。目标是将平均10行或更多行的原始数据折叠到Summary表的一行中。
将多个表作为分区数据的方式通常是一个错误。它使代码复杂化而不一定提供任何好处。 PARTITION BY RANGE(TO_DAYS(...))
更好(虽然仍有一些笨拙)。 (注意:TO_DAYS()
可以替换为日期计算,例如,将TIMESTAMP
转换为最高时间边界 - 如果您想要解决小时数。同样适用于10分钟等。)
<强>浆纱强>
这是对数据量的正确分析吗?
需要付出一些代价。
INSERTs
/ sec(如果在一台机器上)是可能的,但我们需要讨论如何做到这一点。 (我使用经验法则:“开箱即用,MySQL可以处理100次插入/秒;超过这需要一些讨论”。)id
(INT
太小,没用),modified
。PARTITION
;如果保持10年(或更长)年,则按年份计算。BY RANGE
可以改变分区大小,但在重新排列大小时有一个缺点:组合,比如4个月制作一个月,在REORGANIZE
完成时占用桌面答案 1 :(得分:0)
您是否考虑过为您的数据使用时间序列数据库?
您提出的架构是通用类型(度量标准名称存储在signal_id
列中),在读取和写入数据时,每个time:value
应该有30-70个字节,并且具有相应的I / O负载。对于现代时间序列数据库(例如Axibase TSD(我的所属关系)),将其与少于2个字节进行比较。以下是压缩tests。随意发布一小部分数据,如其他人建议的那样,可以获得更具体的反馈。