MySQL时间序列数据分区

问题描述 投票:2回答:2

我正在尝试实现一个新的数据库模式模板来有效地存储/检索“公园”的时间序列数据。 公园有多个设备(和子设备),每个设备都有信号。在公园里可以有2-5k设备,在某些情况下甚至更多。通常,公园的信号时间分辨率相同,通常为5-10-15分钟或1小时。

由于每个公园可以拥有不同数量的设备,每个设备具有不同数量的具有不同时间分辨率的数据信号,因此我必须创建一个在所有情况下都能很好地工作的数据库模板。

在我们的系统中,有一个API经常读取最近的数据(最近一周),而只是偶尔读取历史数据(当最终用户通过接口请求它时)。这对于聚合新数据的后端进程(例如从5分钟到1小时的分辨率等)有效,并且仅在手动请求时才对历史数据执行此操作。历史数据还将用于使用专用软件对公园进行一些离线分析。

重要的是能够迁移数据库/表并在出现问题时快速恢复它们。

我想到两个选择:

  1. 根据日期使用MySQL分区。
  2. 有一个“当前数据”表,其中存储所有信号数据以便快速访问,然后定期将“旧”数据移动到每日,每月,每年表(块)。这个块可以是自适应的,使得所有表具有相同的大小(就所使用的磁盘空间而言)。这是因为可能会发生一些设备或整个公园离线一段时间并且会出现数据漏洞。

您是否还有其他想法可以更好地适应目的,并强调不同方法的所有优点和缺点?

这里有一些关于如何存储设备的信息:

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个模式。
  • 一个公园每年平均有80万行数据。历史可以达到最多20年,但平均而言,我们已经/将达到5年。考虑到这一点,考虑到Rick估计的50b /行,我们每年将达到~5GB,因此历史可达到约50GB。考虑到AWS Aurora MySQL可以达到64TB,所有公园都可以放入数据库中。在未来最糟糕的情况下,我们可以将一个客户的公园分成不同的 数据库 碎片所以这不是问题。
  • 对于数据库,我们将在AWS上使用MySQL Aurora,主数据库将具有16GB的RAM和4个vCPU(我们也可以增加,针对后台进程/数据插入进行优化),并且将有一个只读的副本具有不同硬件规格的数据库针对API侧的操作进行了优化。
  • 所有历史记录都必须存储和可用(对于较不频繁的操作,如历史分析,数据下载等)。

还有哪些其他信息可以帮助您理解/确定更好的选择?感谢您的时间。

mysql database time-series partitioning
2个回答
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分钟,等等)

浆纱

这是对数据量的正确分析吗?

  • 500个公园
  • 300M行/年/公园 - 150B行/年 - 插入5000行/秒
  • 10年以上保留 - 1.5T行
  • 17个字节/行(假设50个带有开销) - 75TB
  • 7天'热'75/52/10 = 150GB

需要付出一些代价。

  • 可能需要进行分片(多个服务器,每个服务器处理一个公园的子集)。
  • 5000 INSERTs / sec(如果在一台机器上)是可能的,但我们需要讨论如何做到这一点。 (我使用经验法则:“开箱即用,MySQL可以处理100次插入/秒;超过这需要一些讨论”。)
  • 您需要“在线”多少(1周到10年之间)?
  • 在上面的数字中,我已经淘汰了id(在INT上太小了,没有用)和modified
  • 为了有效地删除“旧”数据:PARTITION白天如果只保持一周在线;如果保持10年(或更长)年,则按年份计算。
  • 使用BY RANGE可以改变分区大小,但是在重新排列大小时它有一个缺点:组合,比如说4个月制作一个月,在REORGANIZE完成时将桌子绑起来。

0
投票

您是否考虑过为您的数据使用时间序列数据库?

您提出的模式是通用类型(度量标准名称存储在signal_id列中),在读取和写入数据时,每个time:value应该有30-70个字节,并且具有相应的I / O负载。对于现代时间序列数据库(例如Axibase TSD(我的所属关系)),将其与少于2个字节进行比较。这是压缩tests。随意发布一小部分数据,如其他人建议的那样,可以获得更具体的反馈。

© www.soinside.com 2019 - 2024. All rights reserved.