MySQL时间序列数据分区

时间:2018-05-29 14:13:19

标签: mysql database time-series partitioning

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

由于每个公园可以拥有不同数量的设备,每个设备具有不同数量的具有不同时间分辨率的数据信号,因此我必须创建一个适用于所有情况的数据库模板。

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

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

我想到了两个选择:

  1. 根据日期使用MySQL分区。
  2. 有一个“当前数据”表,其中存储所有信号数据以便快速访问,然后定期将“旧”数据移动到每日,每月,每年表(块)。这个块可以是自适应的,使得所有表具有相同的大小(就所使用的磁盘空间而言)。这是因为有些设备或整个公园可能会在一段时间内处于脱机状态,并且会出现数据漏洞。
  3. 您是否有其他想法更符合目的,并强调不同方法的所有优点和缺点?

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

    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方面的操作进行了优化。

    • 所有历史记录都必须存储和可用(对于历史分析,数据下载等不常用的操作......)。

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

2 个答案:

答案 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分钟等。)

<强>浆纱

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

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

需要付出一些代价。

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

答案 1 :(得分:0)

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

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