3

这是对我的问题“有效存储 7.300.000.000 行”(有效存储 7.300.000.000 行)的跟进。

我决定将 MySQL 与分区一起使用,初步架构如下所示:

CREATE TABLE entity_values (
  entity_id MEDIUMINT UNSIGNED DEFAULT 0 NOT NULL, # 3 bytes = [0 .. 16.777.215]
  date_id SMALLINT UNSIGNED DEFAULT 0 NOT NULL, # 2 bytes = [0 .. 65.535]
  value_1 MEDIUMINT UNSIGNED DEFAULT 0 NOT NULL, # 3 bytes = [0 .. 16.777.215]
  value_2 MEDIUMINT UNSIGNED DEFAULT 0 NOT NULL, # 3 bytes = [0 .. 16.777.215]
  UNIQUE KEY (entity_id, date_id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 PARTITION BY HASH(entity_id) PARTITIONS 25;

这给出了:

  • 行 = 7.300.000.000 行(根据上一篇文章中所述的要求)
  • 大小/行 = 11 字节 (3+2+3+3)
  • 总大小 = 7.300.000.000 行 * 11 字节 = 80.300.000.000 字节 = 80.3 GB
  • Partitions = 25(3.2 GB/分区,分区大小有些随意)

请注意,我已经从原始设计中删除了主键,因为不会使用“id”列。

现在问我的问题 - 鉴于我之前的帖子中概述的要求和上面的架构,您对可以进行的进一步优化/调整有什么建议吗?或者考虑到我决定使用 MySQL,上述模式是否“最佳”?

更新:我尝试将当前数据集加载到上面的模式中,8.570.532 行占用了 212.000.000 字节的磁盘空间,每行大约 24.7 字节。

更新:请注意,覆盖 entity_id+date_id 的索引也将用于仅针对 entity_id 的查询。

4

3 回答 3

2

我不太明白的一件事是您计划如何修剪数据。您每天有 200 万行,但您没有指定计划保留多少数据。在某些时候,您会希望按年龄(很可能)使数据过期。

此时,您将希望通过删除分区来做到这一点,而不是通过执行将每个分区锁定很长时间的删除(因为它必须进行全表扫描才能找到要删除的行),然后让你的桌子不小,因为分区上到处都是洞。

通过 entity_id 的散列进行分区对于搜索来说似乎是明智的,但按时间分区可以缓解您修剪旧数据时的争用,这绝对是一件好事。

MyISAM 有一个称为“并发插入”的功能,您几乎肯定需要一直使用它来实现并发和性能;这要求“禁止删除”规则,这意味着您只能通过删除分区来进行删除。

但是删除分区也很好,因为您可以取回磁盘空间。

说了这么多,80G 并没有那么大,我可能很想把它全部存储在一个表中,并使用 InnoDB 来启用并发访问。

哦,是的,如果你确实使用了 InnoDB,你可以有一个 entity_id 的主键 date_id,这意味着它将聚集具有相同 entity_id 的行。您可能需要 date_id 上的二级索引来启用有效的修剪。

请使用您的生产数据大小对此进行测试,并让我们知道您发现了什么!

于 2009-03-23T14:43:44.167 回答
1

如果您通常检索一个实体 ID 的所有(或大部分)数据,则应考虑将索引设为实体 ID 而不是 (entity_id,date_id) ——除非您需要数据库进行唯一检查。

效果是使索引更小,以便您在内存中获得更多。您的目标应该是让索引在内存中。即使您需要执行 SELECT..ORDER BY DATE,您会发现 MySQL 可以在几分之一秒内即时订购 3650 个值(没有索引)。这个问题是从磁盘读取行的时间。

但是,您的主要性能问题是插入导致一个实体的数据分布在磁盘上,需要对每个(实体,日期)进行磁盘访问,这将使您的查询以每秒几百行的速度运行。您的分区不会对此有所帮助,因为每个实体都在一个分区中,并且行分布在其磁盘上。(磁盘上的 RAID0 会有所帮助)。

为了获得有效的检索,您需要让实体的数据在磁盘上是连续的,这意味着从 INSERT 顺序重新排序数据。您可以使用 MySQL ALTER TABLE.. ORDER BY ... 执行此操作,但这需要很长时间。我有一个 182M 的行表在执行 ALTER TABLE.. ORDER BY 运行了过去 2 周,但它还没有完成。

这就是我编写自定义存储引擎的原因!

顺便说一句,我不确定你是否通过分区获得任何好处,除非你在多个服务器上进行分区——或者至少是多个磁盘。MySQL 必须做的艰苦工作并没有通过分区变得更容易。这都是关于磁盘访问时间的。

将每个分区放在不同的磁盘上可能会有所帮助。我的分区数不会超过物理磁盘的两倍。2 次,而不是 1 次,会带来一些排队的好处,但我怀疑这会产生多大的影响。我怀疑您是否比使用 RAID0 在尽可能多的磁盘上的单个非分区表好得多。

此应用程序的性能取决于磁盘寻道次数,因此如果您每秒可以进行更多寻道,则会有所帮助。

您通过分区获得了一些处理并行性(假设您有多个处理器),但您的系统将受 I/O 限制,而不是处理器限制。如果您的处理器利用率高达 2%,那么您可能正在做一些您不需要做的事情(或者不是您的应用程序)。

九年来,我一直在使用 MySQL 编写、优化和操作这种应用程序……而且我的经历中你可能会想到的所有伤疤。一旦你的数据远远大于你的内存大小(这是我对“巨大”的定义),整个性能问题就是磁盘 I/O,这意味着磁盘搜索的数量。祝你好运!!

于 2009-03-21T14:05:15.413 回答
0

您在上一个问题中指出您将检索entity_id;的所有行 但是,如果您计划检索特定实体的日期范围,则可以使用子分区(也称为复合分区)。根据您的使用情况,您可以将主分区设置为 entity_id,将子分区设置为年份或其他日期范围。如果它在您的系统中有意义,您也可以反转它。

于 2009-03-20T14:57:27.013 回答