【问题标题】:How can I improve LOAD DATA performance on large InnoDB tables?如何提高大型 InnoDB 表的 LOAD DATA 性能?
【发布时间】:2012-12-21 09:04:14
【问题描述】:

我的这张表有超过 700 万行,我正在 LOAD DATA LOCAL INFILE'ing 一次将 50 万行的更多数据放入其中。前几次很快,但这次添加花费的时间越来越长,可能是由于索引开销:

CREATE TABLE `orthograph_ests` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `digest` char(32) NOT NULL,
  `taxid` int(10) unsigned NOT NULL,
  `date` int(10) unsigned DEFAULT NULL,
  `header` varchar(255) NOT NULL,
  `sequence` mediumblob,
  PRIMARY KEY (`id`),
  UNIQUE KEY `digest` (`digest`),
  KEY `taxid` (`taxid`),
  KEY `header` (`header`)
) ENGINE=InnoDB AUTO_INCREMENT=12134266 DEFAULT CHARSET=latin1

我正在开发一个将在预先存在的数据库上运行的应用程序。我很可能无法控制服务器变量,除非我强制更改它们(我不希望这样做),所以恐怕像 these 这样的建议用途有限。

我已经读过,最小化此表上的键会有所帮助。但是,我需要这些键用于以后的查询。我猜如果我删除并重新创建它们也需要很长时间,但我还没有测试过。我还读到,尤其是 UNIQUE 约束会使插入速度变慢。 digest 列将采用 必须 唯一的 SHA256 摘要,我无法确保没有冲突(我知道,这不太可能,但可能)。

按照here 的建议,分区会有所帮助吗?我可以改进索引,例如,通过限制 digest 列上的键长度?我应该换成MyISAM,它在交易期间支持DISABLE KEYS吗?我还能做些什么来提高LOAD DATA 的性能?

编辑:

大插入后,此表仅用于SELECTs,不再写入。这种大型加载主要是一次完成的操作,但是在完成之前需要上传大约 1,000 个数据集(每 0.5M 行)。

我将使用摘要来查找行,这就是我为该列编制索引的原因。如果发生冲突,则不应上传该单独的行。

sequence blob 放在外部文件系统中可能不是一个可行的选择,因为我不能轻易地将文件系统更改强加给用户。

【问题讨论】:

  • "我知道,不太可能,但可能" 您的服务器更有可能发生随机内存位翻转。您无需针对 SHA-256 冲突进行规划。
  • 请澄清一些事情:这个表是用作参考表,还是你会做改变它的生产交易?您会定期将半兆行的新数据加载到其中,还是加载一次完成的操作?您的摘要列的目的是什么——您将使用摘要来查找生产中的行吗?如果您的摘要确实遇到哈希冲突,您的恢复计划是什么?您是否考虑过将您的序列列(您的 blob)放在外部文件系统中?
  • @OllieJones:感谢您的评论。编辑了问题。
  • @usr: 感谢您的评论 :)
  • 无论您进行何种微优化,除非您可以访问服务器配置或将机械 HDD 子系统替换为更快的子系统 - 没有任何帮助。人们通常会忘记计算机不是神奇的独角兽,使用关系数据库的应用程序通常很慢​​,因为数据持久性设备的 IOPS 层会立即受到影响。

标签: mysql performance innodb load-data-infile


【解决方案1】:

这确实是您正在加载的大量数据,您应该预计它会花费数十个小时,尤其是在通用共享服务器硬件上。几乎没有什么魔力(除非你在谷歌或其他什么公司工作)可以让这份工作成为一件非常痛苦的事情。所以要有勇气。

这是一个参考表。这意味着您应该立即切换到 MyISAM 并为这张桌子留在那里。您不需要 InnoDB 的事务完整性功能,但您确实需要 MyISAM 在加载期间禁用索引并在之后重新启用它。重新启用索引需要很长时间,因此请做好准备。

您应该考虑使用比 SHA-256 更短的哈希值。 SHA-1(160 位)很好。信不信由你,MD-5(128 位)也可以使用。 MD-5 已被破解,因此不适合安全内容认证。但它仍然是一个有用的哈希。从您的角度来看,较短的哈希值是更好的哈希值。

如果您可以禁用索引 MyISAM 样式,那么您的摘要键是否唯一可能并不重要。但您可能会考虑允许它是非唯一的以节省时间。

如果不了解您的数据和服务器硬件的更多信息,就很难提出有关分区的建议。但考虑到这是一个参考数据库,最好还是硬着头皮花几个星期加载它。

如果您有足够的服务器磁盘空间,您可以考虑将每个半兆块加载到自己的表中,然后将其插入到大表中。事实证明,这可能是一种很好的方式来处理您可能需要在某天重新加载整个内容的可能性。

在共享服务器硬件上,使用比半兆行更小的块可能是有意义的。

您可以考虑制作一个单独的 id/digest 表。然后,您可以在没有摘要的情况下加载数据并快速完成。然后,您可以自己编写一个存储过程或客户端,以每批几千行的形式创建摘要,直到它们完成。这仅在被消化的内容在您的数据集中时才有效。

【讨论】:

  • 非常感谢您的意见和宝贵的建议!
【解决方案2】:

数据加载缓慢主要有两个原因:

  1. 插入数据本身的写入性能。
  2. 读取现有数据以加载现有数据以在添加新数据时修改现有页面的性能。

可以通过主要降低持久性和减少日志记录来解决写入性能问题。这就是您会发现的许多建议,例如以下设置:innodb_flush_log_at_trx_commit=0innodb_doublewrite=0innodb_support_xa=0。减少写入的数据数量也很有帮助,例如通过设置log-bin=0。但是,期望您的客户在现有生产系统中更改这些与耐用性相关的设置也是不可接受的。更改它们更适合在专用系统上一次性批量加载,而不是定期向现有系统添加数据。

批量加载INSERT(有很多行)或LOAD DATA INFILE 旨在通过在单个事务中写入更多数据来降低写入吞吐量要求,从而减少事务日志的同步次数.降低写入吞吐量或提高写入性能只能在一定程度上有所帮助。

通常还可以在加载前按PRIMARY KEY 对数据进行排序,以减少将数据插入索引结构时不必要的页面拆分量。但是,当有多个辅助键时,这使用有限,因为按PRIMARY KEY 排序必然意味着数据未按至少一个辅助键排序。

读取性能问题可能更有趣,并且通常是将新数据加载到现有表中的实际性能问题,尤其是在存在辅助键的情况下。最好的办法是所有现有数据都适合内存(innodb_buffer_pool_size 足够大),这样在加载过程中就不需要将数据分页进出缓存。鉴于您说的只有几百万行,这可能是可行的。

【讨论】:

  • 感谢您的意见。我不明白您关于读取性能问题的观点:您的意思是实际的 I/O 读取性能吗?因为我认为我的性能问题与索引开销有关。关于缓冲:这可能确实有帮助,但我使用的是LOAD DATA,据我所知,它没有使用缓冲池。
  • 在 InnoDB 中,总是使用缓冲池。它是缓存数据(作为页面,而不是行)的主要机制。基本上,如果缓冲池不足以容纳所有现有数据,则当写入需要修改同一页面上的数据(键值彼此靠近)时,必须首先将该页面加载到缓冲区中游泳池。
  • 感谢您的解释!但是,由于我无法访问服务器变量,恐怕您的建议在这种情况下用处有限。
猜你喜欢
  • 1970-01-01
  • 2020-02-06
  • 2018-07-11
  • 1970-01-01
  • 2012-08-24
  • 1970-01-01
  • 2013-06-30
  • 2021-08-01
  • 2018-12-10
相关资源
最近更新 更多