我正在尝试实现一个新的数据库模式模板来有效地存储/检索“公园”的时间序列数据。 公园有多个设备(和子设备),每个设备都有信号。在公园里可以有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
其他信息:
还有哪些其他信息可以帮助您理解/确定更好的选择?感谢您的时间。
仅当您打算删除“旧”数据时,分区才有用。更多讨论: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
完成时将桌子绑起来。您是否考虑过为您的数据使用时间序列数据库?
您提出的模式是通用类型(度量标准名称存储在signal_id
列中),在读取和写入数据时,每个time:value
应该有30-70个字节,并且具有相应的I / O负载。对于现代时间序列数据库(例如Axibase TSD(我的所属关系)),将其与少于2个字节进行比较。这是压缩tests。随意发布一小部分数据,如其他人建议的那样,可以获得更具体的反馈。