MySQL:将大表拆分为分区或单独的表?

Posted

技术标签:

【中文标题】MySQL:将大表拆分为分区或单独的表?【英文标题】:MySQL: Splitting a large table into partitions or separate tables? 【发布时间】:2018-03-01 05:20:18 【问题描述】:

我有一个包含 20 多个表的 mysql 数据库,但其中一个非常大,因为它从不同的传感器收集测量数据。它的磁盘大小约为 145 GB,包含超过 10 亿条记录。所有这些数据也被复制到另一个 MySQL 服务器。

我想将数据分成更小的“碎片”,所以我的问题是以下哪种解决方案更好。我会使用记录的“时间戳”将数据按年划分。几乎所有在该表上执行的 SELECT 查询都在查询的“where”部分包含“timestamp”字段。

以下是我无法决定的解决方案:

    使用 MySQL 分区并按年份划分数据(例如 partition1 - 2010、partition2 - 2011 等) 创建单独的表格并将数据按年份划分(例如,measurement_2010、measurement_2011 等表格)

还有其他我不知道的(较新的)可能选项吗?

我知道,在第一种情况下,MySQL 本身会从“分片”中获取数据,而在第二种情况下,我必须为它编写一种包装器并自己完成。对于第二种情况,是否有任何其他方法可以使所有单独的表都被视为“一个大表”来从中获取数据?

我知道过去已经有人问过这个问题,但也许有人想出了一些新的解决方案(我不知道),或者最佳实践解决方案现在已经改变。 :)

非常感谢您的帮助。

编辑:

架构类似于这样:

device_id (INT)
timestamp (DATETIME)
sensor_1_temp (FLOAT)
sensor_2_temp (FLOAT)
etc. (30 more for instance)

所有传感器温度每分钟同时写入一次。请注意,大约有 30 个不同的传感器测量值连续写入。这些数据主要用于显示图表和其他一些统计目的。

【问题讨论】:

【参考方案1】:

好吧,如果您希望得到新的答案,那意味着您可能已经阅读了我的答案,而且我听起来像是破纪录。请参阅Partitioning blog,了解分区有助于提高性能的少数用例。您的听起来像这 4 种情况中的任何一种。

收缩device_idINT 是 4 个字节;你真的拥有数百万台设备吗? TINYINT UNSIGNED 为 1 个字节,范围为 0..255。 SMALLINT UNSIGNED 为 2 个字节,范围为 0..64K。这会使表格缩小一点。

如果您的真正问题是关于如何管理这么多数据,那么让我们“跳出框框思考”。继续阅读。

绘图...您要绘制哪些日期范围?

“最后”小时/天/周/月/年? 任意小时/天/周/月/年? 任意范围,不受日/周/月/年界限的约束?

你在画什么?

一天的平均值? 一天的最大值/最小值? 烛台(等)一天或一周或其他什么?

无论哪种情况,您都应该构建(并逐步维护)包含数据的汇总表。一行将包含一小时的摘要信息。我会建议

CREATE TABLE Summary (
    device_id SMALLINT UNSIGNED NOT NULL,
    sensor_id TINYINT UNSIGNED NOT NULL,
    hr TIMESTAMP NOT NULL,
    avg_val FLOAT NOT NULL,
    min_val FLOAT NOT NULL,
    max_val FLOAT NOT NULL
    PRIMARY KEY (device_id, sensor_id, hr)
) ENGINE=InnoDB;

一个汇总表可能是 9GB(对于当前数据量)。

SELECT hr,
       avg_val,
       min_val,
       max_val
    FROM Summary
    WHERE device_id = ?
      AND sensor_id = ?
      AND hr >= ?
      AND hr  < ? + INTERVAL 20 DAY;

会给你 480 小时的 hi/lo/avg 值;足以绘制图表?从汇总表中抓取 480 行比从原始数据表中抓取 60*480 行快很多。

获取一年的类似数据可能会阻塞图形包,因此可能值得构建摘要的摘要 - 以一天为分辨率。大约是 0.4GB。

有几种不同的方法来构建汇总表;在您思考它的美丽并阅读Summary tables blog之后,我们可以讨论它。收集一小时的数据,然后扩充汇总表可能是最好的方法。这有点像my Staging table blog 讨论的触发器。

而且,如果您有每小时的摘要,您真的需要每分钟的数据吗?考虑把它扔掉。或者,也许是一个月后的数据。这导致使用分区,但只是为了删除旧数据的好处,如Partitioning blog 的“案例1”中所述。也就是说,您将有每日分区,每晚使用DROPREORGANIZE 来移动“事实”表的时间。这将导致减少 145GB 的占用空间,但不会丢失太多数据。新占用空间:约 12GB(每小时汇总 + 过去 30 天的每分钟详细信息)

PS:Summary Table blog 显示了如何获得标准差。

【讨论】:

感谢您的回答。汇总/平均数据目前不是一种选择,因为当前(和未来)算法需要数据,而客户不想听到它。另外,我们想要对表进行分区的原因是因为复制在打开那个大文件并写入它时存在很大的问题(IOWait 时间正在增加)。我会在晚上阅读你关于分区的博客。 嗯...我从来没有听说过“打开”一个大文件很慢。至于写作,让我们看看SHOW CREATE TABLE,这样我就可以看到索引和其他细节。另外,让我们看一个示例INSERT【参考方案2】:

你没有说太多关于你如何使用/查询数据或架构是什么样的,但我试着编造一些东西。

    拆分表的方式之一是基于实体 (不同的传感器是不同的实体)。这很有用,如果 不同的传感器需要不同的列。所以你不需要 强迫他们进入一个适合所有人的模式(最不常见的 多)。虽然添加或删除传感器并不好 动态,因为您必须在运行时添加表。 另一种方法是根据时间拆分表。这是 如果一段时间后数据可以“历史化”并且不用于 不再是实际的业务逻辑,但用于统计目的。

这两种方法也可以结合使用。此外,请确保根据您的查询需求正确索引该表。 我强烈反对任何经常需要在一段时间后添加表格或类似的方法。与往常一样,在出现性能问题之前我不会拆分任何东西。

编辑: 我会清楚地将表格重组为以下表格,而不是拆分它:

device_id (INT)
timestamp (DATETIME)
sensor_id (INT) -- could be unique or not. if sensor_id is not unique make a 
                -- composite key from device_id and sensor_id given that you 
                -- need it for queries
sensor_temp (FLOAT)

如果数据增长很快,并且您希望很快生成 TB 级的数据,那么您最好使用 NoSQL 方法。但那是另一回事。

【讨论】:

感谢您的回答。我已经对我的答案进行了一些编辑,所以也许它现在可以让大局更好一些。 如果你朝那个方向发展,请使用TINYINT UNSIGNED 代替sensor_idINT 将是 1 个字节而不是 4 个字节。

以上是关于MySQL:将大表拆分为分区或单独的表?的主要内容,如果未能解决你的问题,请参考以下文章

mysql 分区

mysql 分区

将大表从 MySql 迁移到 MsSql

mysql分区及实例演示

mysql分区及实例演示

针对MySQL大表优化方案