【问题标题】:Edited- MySQL. Large MyISAM table (40mln records) having index that is very slow and huge in size on disk编辑-MySQL。大型 MyISAM 表(4000 万条记录)的索引非常慢且磁盘上的大小很大
【发布时间】:2011-01-13 14:54:21
【问题描述】:

该表包含大约 40,000,000 条记录,其中:

CREATE TABLE `event` (
  `id` bigint(20) unsigned NOT NULL auto_increment,
  `some_other_id_not_fk` int(10) unsigned default NOT NULL,
  `event_time` datetime NOT NULL,
  `radius` float default NULL,
  `how_heavy` smallint(6) default NULL,
  PRIMARY KEY  (`id`),
  KEY `event_some_other_id_not_fk` (`some_other_id_not_fk`),
  KEY `event_event_time` (`event_time`)
) ENGINE=MyISAM AUTO_INCREMENT=6506226 DEFAULT CHARSET=utf8 

您应该知道some_other_id_not_fk 列并不大,它仅包含7 个不同的数字。真正的痛苦是event_time 日期时间列,因为它包含大量不同的日期时间,并且基本上所有内容都是允许的:重复以及无法预测的大时间间隔而没有记录来“覆盖”它们。您还应该知道 (some_other_id_not_fk,event_time) 对必须允许有重复项:(我知道这会导致更多问题:(

我在优化 MySQL 表方面有过一些经验,但我的视野中从未出现过如此巨大的痛苦:/

“事物”的当前状态是:

  • event_time 在 date1 和 date2 之间的选择(我需要这样做)的速度非常快。 :)
  • 我的插入很慢,我的意思是真的很慢!!!超过 30 秒,甚至更糟:临时 DISABLE 和 ENABLE KEYS 的 LOAD DATA 过程非常慢(几个小时),主要是 ENABLE 键操作。
  • 磁盘上的索引大小是数据大小的 7 倍

到目前为止,我会尝试几种不同的重新索引组合,但数据的大小确实让我无法随意尝试索引和列删除/创建。

请帮助任何人解决这个问题?应该使用时间戳而不是日期时间来解决我的问题吗?或者也许我应该为dayyear、...等添加额外的列并索引它们?

【问题讨论】:

  • (正常k)是什么意思?为什么 noone 只提供 SHOW CREATE TABLE 输出?每个人都试图用他们认为最能描述这张桌子的任何可怕的术语来描述一张桌子,但没有人知道他们在说什么。我讨厌它。
  • 编辑您的答案以包含 SHOW CREATE TABLE 输出。还提供一些您需要优化的示例查询,以便我们提供最佳帮助。
  • @hobodave:从模糊的描述中发现规范是程序员工作的重要组成部分。
  • @Mark:所以不会给我发薪水。如果客户/用户向我描述了一个问题,那么我将使用他们给我的东西。如果我正在面试的程序员不能使用通用词汇简洁而具体地描述一个问题,他们就会失业。甚至您也无法充分理解他的问题以提供答案,这可以通过询问“您的桌子上有哪些索引?”来证明。但是,这只是要求更加模糊。 SHOW CREATE TABLE 回答了关于表结构的所有相关问题。
  • @PatlaDJ:为什么你有 40,000,000 行但你的 auto id 只有大约 600 万?

标签: mysql database optimization indexing query-optimization


【解决方案1】:
`id` bigint(20) unsigned NOT NULL auto_increment,

你真的需要 BIGINT 吗?您可能可以逃脱 INT。如果您要每天 24 小时每秒插入 1,000 行,则需要 136 年才能用尽无符号 32 位整数中的所有值。

此更改会将 4000 万行的表大小减少 152.5 MB,并将主键索引的大小减少 4000 万行的 158.8 MB。

`some_other_id_not_fk` int(10) unsigned default NOT NULL,

您说这只有 7 个不同的值。那么它是否需要是 INT 类型?你可以改用 TINYINT 吗?这将大大减少索引大小。

这会将 4000 万行的表大小减少 114.4 MB,并将 some_other_id_not_fk 索引的大小减少大致相同。

`event_time` datetime NOT NULL,

您需要日期时间吗? DATETIME 占用 8 个字节,TIMESTAMP 占用 4 个字节。如果您可以使用 TIMESTAMP,那么这将大大减少数据和索引大小。请注意 TIMESTAMP 字段的限制,例如 Y2K38 以及它们在时区和复制方面的行为方式。

此更改会将 4000 万行的表大小减少 152.5 MB,并将主键索引的大小减少 4000 万行的 158.8 MB。

这三项更改将显着减小数据和索引的大小。

总空间节省

  • 表:152.5 + 152.5 + 114.4 = 419.4 MB
  • 索引:158.8 + 158.8 + ~115 = 432.6 MB

总计:852MB

正如其他人所建议的,您甚至可能不需要您定义的所有索引。由于some_other_id_not_fk 的选择性如此之低,查询优化器很有可能甚至不会使用该索引,而是选择全表扫描。完全删除此索引将为您的索引节省大量空间。

如果您能提供一些示例查询,我可以进一步帮助您。

另外,您是否在读取负载过重的情况下插入此表?请记住,MyISAM 中的 SELECT 会阻止 INSERT。

更新

大多数人建议将您的 some_other_id_not_fk 字段移动到 event_time 索引中,以便新索引位于 (event_time, some_other_id_not_fk) 上。我会推荐相同的,但有一个重要的警告。

此索引适用于您仅过滤 event_time 或同时过滤 event_timesome_other_id_not_fk 的查询。它不会仅用于some_other_id_not_fk 上的查询过滤 - 将进行全表扫描。

此外,如果您的查询总是event_timeevent_timesome_other_id_not_fk始终过滤,那么不要使用@的索引顺序987654336@。相反,您应该使用索引(some_other_id_not_fk, event_time)

首先具有选择性最少(重复次数最多)的字段将允许对索引进行更大程度的压缩,从而显着减少磁盘占用。

【讨论】:

  • 非常感谢! “纯阶级”:)
  • 我需要一些时间才能通过选择正确的答案来关闭此线程,可能需要几个小时。我需要等待重新索引我的表以查看结果并发布我自己的最终 cmets。
  • 我通常同意“优先选择最少的字段”;但这并不简单。 PatlaDJ 提到他使用 IN (x,y,...);在日期时间范围内。使用范围 first 可以更好地处理此类查询。但可能在这种非选择性的极端情况下(40mill 有 7 个值!),它可能是相反的。尽管如此,摆脱这样一个无用的索引(40mill 的 7 个值!)肯定会缩短 INSERT 时间。
【解决方案2】:

我认为你对什么是重什么不是重的直觉是倒退的:一个包含多个不同选项的多次重复的索引比一个包含许多不同值且每个重复很少的索引差。

我的建议:删除some_other_id_not_fk 上的索引并保留(some_other_id_not_fk, event_time)。这个复合索引应该是“几乎唯一的”,使得插入开销要低得多。如果可能,也删除 event_time 键,除非您的查询使用该字段而不使用 some_other_id_not_fk

编辑:你说你必须按时间间隔选择,然后保留(event_time, some_other_id_not_fk)并删除event_timesome_other_id_not_fk。如果您有使用some_other_id_not_fk 而不是event_time 的查询,则保留(event_time, some_other_id_not_fk)(some_other_id_not_fk, event_time)。关键是没有任何索引,选项很少。在右侧有一个未使用字段的索引是可以的。

【讨论】:

  • 您的回答非常好,也很有帮助。我接下来的几个小时将等着看如何通过删除所有索引重新索引,并且只放置 (event_time, some_other_id_not_fk) 将执行。我希望我唯一的问题是some_other_id_not_fk 上的独奏索引。是的,我的查询在event_timesome_other_id_not_fk 上都有,有时some_other_id_not_fk 会被IN(1,2,6,x,...) 函数过滤。我只是不认为在some_other_id_not_fk 上拥有单独的索引会对索引的大小和开销产生如此巨大的影响,只要它几乎没有不同的值。妈妈咪呀!
【解决方案3】:

我认为您不需要 some_other_id_not_fk 上的索引(正如您所说,只有 7 个不同的值,因此该索引的选择性是 40,000,000/7 )。您所需要的只是 (event_time + [也许] some_other_id_not_fk) 上的 1 个索引;

【讨论】:

  • 谢谢你,我会努力的。你认为只在event_time 上使用索引会大大减小索引的大小吗?它现在是 7Gb,而数据大小只有 1Gb。我简直不敢相信为什么需要如此庞大的索引?我的方法有问题吗?逻辑表明我不需要两页长长的索引文字来标记我的红色毛衣在抽屉 N1,蓝色毛衣在 N2,绿色在 N3。因此,如果我要查找我的红色毛衣#11,我需要阅读几页索引数据确实发现我只能在抽屉 N1 中搜索它!
  • 它肯定会减小索引的大小。我建议大约 7 GB 的 30%。当您在 field1 上创建索引时,DB Engine 会为表中的每条记录存储索引值 + 唯一记录标识符。
【解决方案4】:

我之前也遇到过类似的情况。我创建了一个具有相同结构的表,我们称之为存档表。我每天 3:00 将活动表中的数据复制到它,并删除了所有原始数据。

图表和其他统计数据来自存档表select,当前事件已记录到活动事件。

也许这不是最佳实践,但对我来说已经足够了。

按时间分区表:MySQL 5.1 中的日期分区(Robin Schumacher)

http://dev.mysql.com/tech-resources/articles/mysql_5.1_partitioning_with_dates.html

【讨论】:

  • 您也可以尝试每年或每月打开新表。 Oracle 有一些类似的特性,称为基于时间的分区。
  • 是的 Notinlist,谢谢!作为最后的手段,我会求助于这个解决方案,因为这将花费我大量的应用程序重新设计工作。
【解决方案5】:

我已删除所有索引并在 (event_time, some_other_id_not_fk) 上建立了索引。我得到以下性能指标:

  • 磁盘上的数据大小为 1Gb,磁盘上的索引大小为 1.2Gb。

  • event 中删除event.event_time>STR_TO_DATE('20091201000000','%Y%m%d%H%i%s') 和event.some_other_id_not_fk= 4 |受影响的行数:353543 时间:65.173 秒

  • select * from event where event.event_time>STR_TO_DATE('20090401000000','%Y%m%d%H%i%s') 和event.event_time event.some_other_id_not_fk in (22,4,1,3) |集合916行,查询时间:0.030秒

  • 索引启用了使用以下格式插入 350,000 条新记录:insert into event VALUES(...),(...),... |在大约 30 秒内完成,Yeahaaaaaa :))

  • 索引禁用-插入-索引启用-使用相同格式的350,000条新记录:插入eventVALUES(...),(...),... |在大约 40 分钟内完成。 :) 看起来像 mysql 默认转储格式,在插入之前禁用索引并在之后重新启用它,并不总是对性能有好处,尤其是在存在大尺寸索引时:)

目前我对这种表现感到满意。

昨晚我设法仅在 (event_time) 上创建索引。索引的大小略低于第一个示例。大约 1.1Gb。与上面列出的相同查询的性能:

  • 删除 |稍微快一点,大约 30 秒
  • 选择 |稍微慢一点,大约 0.1 秒。
  • 我只测试了 350,000 的索引禁用启用插入。又变慢了|大约 35 分钟。

    我拒绝了数据库的这种状态,因为我对选择速度不够满意,这对我来说是优先级N1。

hobodave,我只是好奇,你认为在 (some_other_id_not_fk,event_time) 而不是 (event_time,some_other_id_not_fk) 上创建索引真的会改变一些戏剧性的事情吗?我的查询将始终过滤这两个字段。如果没有some_other_id_not_fk 过滤,我将永远不会有查询。但我可能有一个查询通过 IN(x,y,...) 大多数不同的some_other_id_not_fk 过滤。正如我所说,他们并不多。

我的优先事项是:

  1. 选择速度
  2. 插入速度
  3. 磁盘上的索引大小(因为表将增长数倍)
    ...其他一切

我还想知道为什么 1Gb 数据需要 1.2Gb 这么大的索引大小?指数仍然大于数据。我的逻辑表明,这种日期索引可以在更小的索引中完成?我对么?是否有与可能是 BTREE 的索引类型相关的内容?

谢谢。你们都很棒。我正在关闭线程。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-31
    • 1970-01-01
    • 1970-01-01
    • 2018-02-28
    • 2019-06-14
    • 1970-01-01
    相关资源
    最近更新 更多