InnoDB 导入性能

nua*_*ala 11 mysql innodb performance import

我正在努力批量导入一个由大约 1000 万行(或 7GB)组成的相当大的 InnoDB 表(对我来说这是迄今为止我使用过的最大的表)。

我做了一些研究如何提高 Inno 的导入速度,目前我的设置如下所示:

/etc/mysql/my.cnf/
[...]
innodb_buffer_pool_size = 7446915072 # ~90% of memory
innodb_read_io_threads = 64
innodb_write_io_threads = 64
innodb_io_capacity = 5000
innodb_thread_concurrency=0
innodb_doublewrite = 0
innodb_log_file_size = 1G
log-bin = ""
innodb_autoinc_lock_mode = 2
innodb_flush_method = O_DIRECT
innodb_flush_log_at_trx_commit=2
innodb_buffer_pool_instances=8


import is done via bash script, here is the mysql code:
SET GLOBAL sync_binlog = 1;
SET sql_log_bin = 0;
SET FOREIGN_KEY_CHECKS = 0;
SET UNIQUE_CHECKS = 0;
SET AUTOCOMMIT = 0;
SET SESSION tx_isolation='READ-UNCOMMITTED';
LOAD DATA LOCAL INFILE '$filepath' INTO TABLE monster
COMMIT;
Run Code Online (Sandbox Code Playgroud)

数据以CSV文件形式提供。
目前,我使用较小的“测试转储”测试我的设置,每行 200 万、300 万……行,并用于time import_script.sh比较性能。

缺点是我只能获得总体运行时间,因此我必须等待完整导入完成才能获得结果。

到目前为止我的结果:

  • 10 000 行:<1 秒
  • 100 000 行:10 秒
  • 300 000 行:40 秒
  • 200 万行:18 分钟
  • 300 万行:26 分钟
  • 400 万行:(2 小时后取消)

似乎没有“食谱”解决方案,必须自己找出最佳的设置组合。
除了关于在我的设置中更改什么的建议之外,我还非常感谢更多信息,我可以如何更好地对导入过程进行基准测试/获得更多洞察正在发生的事情以及瓶颈可能在哪里。
我试图阅读我正在更改的设置的文档,但我再次不知道任何副作用,如果我什至可能会因选择不当的值而降低性能。

目前,我想尝试MyISAM在导入和更改表引擎期间使用聊天中的建议。
我想试试这个,但目前我的DROP TABLE查询也需要几个小时才能完成。(这似乎是我的设置不太理想的另一个指标)。

附加信息:
我目前使用的机器有 8GB 的​​ RAM 和带有 5400RPM 的固态混合硬盘驱动器。
虽然我们的目标是从有问题的表中删除过时的数据,但我仍然需要在开发时快速导入到
a) 测试automatic data cleanup feature
b) 万一我们的服务器崩溃,我们想使用我们的第二台服务器作为替代(需要- 最新数据,上次导入耗时超过 24 小时)

mysql> SHOW CREATE TABLE monster\G
*************************** 1. row ***************************
       Table: monster
Create Table: CREATE TABLE `monster` (
  `monster_id` int(11) NOT NULL AUTO_INCREMENT,
  `ext_monster_id` int(11) NOT NULL DEFAULT '0',
  `some_id` int(11) NOT NULL DEFAULT '0',
  `email` varchar(250) NOT NULL,
  `name` varchar(100) NOT NULL,
  `address` varchar(100) NOT NULL,
  `postcode` varchar(20) NOT NULL,
  `city` varchar(100) NOT NULL,
  `country` int(11) NOT NULL DEFAULT '0',
  `address_hash` varchar(250) NOT NULL,
  `lon` float(10,6) NOT NULL,
  `lat` float(10,6) NOT NULL,
  `ip_address` varchar(40) NOT NULL,
  `cookie` int(11) NOT NULL DEFAULT '0',
  `party_id` int(11) NOT NULL,
  `status` int(11) NOT NULL DEFAULT '2',
  `creation_date` datetime NOT NULL,
  `someflag` tinyint(1) NOT NULL DEFAULT '0',
  `someflag2` tinyint(4) NOT NULL,
  `upload_id` int(11) NOT NULL DEFAULT '0',
  `news1` tinyint(4) NOT NULL DEFAULT '0',
  `news2` tinyint(4) NOT NULL,
  `someother_id` int(11) NOT NULL DEFAULT '0',
  `note` varchar(2500) NOT NULL,
  `referer` text NOT NULL,
  `subscription` int(11) DEFAULT '0',
  `hash` varchar(32) DEFAULT NULL,
  `thumbs1` int(11) NOT NULL DEFAULT '0',
  `thumbs2` int(11) NOT NULL DEFAULT '0',
  `thumbs3` int(11) NOT NULL DEFAULT '0',
  `neighbours` tinyint(4) NOT NULL DEFAULT '0',
  `relevance` int(11) NOT NULL,
  PRIMARY KEY (`monster_id`),
  KEY `party_id` (`party_id`),
  KEY `creation_date` (`creation_date`),
  KEY `email` (`email`(4)),
  KEY `hash` (`hash`(8)),
  KEY `address_hash` (`address_hash`(8)),
  KEY `thumbs3` (`thumbs3`),
  KEY `ext_monster_id` (`ext_monster_id`),
  KEY `status` (`status`),
  KEY `note` (`note`(4)),
  KEY `postcode` (`postcode`),
  KEY `some_id` (`some_id`),
  KEY `cookie` (`cookie`),
  KEY `party_id_2` (`party_id`,`status`)
) ENGINE=InnoDB AUTO_INCREMENT=13763891 DEFAULT CHARSET=utf8
Run Code Online (Sandbox Code Playgroud)

Rol*_*DBA 14

首先,当您将数百万行放入 InnoDB 表时,您需要知道对 InnoDB 做了什么。让我们来看看 InnoDB 架构。

InnoDB 架构

在左上角,有一个 InnoDB 缓冲池的插图。请注意,其中有一部分专用于插入缓冲区。那有什么作用?它将对二级索引的更改从缓冲池迁移到系统表空间(又名 ibdata1)内的插入缓冲区。默认情况下,innodb_change_buffer_max_size设置为 25。这意味着最多 25% 的 Buffer Pool 可用于处理二级索引。

在您的情况下,您有 6.935 GB 的 InnoDB 缓冲池。最多 1.734 GB 将用于处理您的二级索引。

现在,看看你的桌子。您有 13 个二级索引。您处理的每一行都必须生成一个二级索引条目,将其与该行的主键耦合,并将它们作为一对从Buffer Pool 中的Insert Buffer 发送到ibdata1 中的Insert Buffer。每行发生 13 次。将此乘以 1000 万,您几乎可以感觉到瓶颈即将来临。

不要忘记,在单个事务中导入 1000 万行会将所有内容堆积到一个回滚段中,并填满 ibdata1 中的 UNDO 空间。

建议

建议#1

我导入这个相当大的表的第一个建议是

  • 删除所有非唯一索引
  • 导入数据
  • 创建所有非唯一索引

建议#2

摆脱重复的索引。在你的情况下,你有

KEY `party_id` (`party_id`),
KEY `party_id_2` (`party_id`,`status`)
Run Code Online (Sandbox Code Playgroud)

两个索引都以 开头party_id,您可以将二级索引处理增加至少 7.6%,去掉 13 个索引中的一个。您最终需要运行

ALTER TABLE monster DROP INDEX party_id;
Run Code Online (Sandbox Code Playgroud)

建议#3

摆脱你不使用的索引。查看您的应用程序代码,看看您的查询是否使用了所有索引。您可能需要查看pt-index-usage以使其建议未使用哪些索引。

建议 #4

您应该将innodb_log_buffer_size增加到 64M,因为默认值为 8M。更大的日志缓冲区可能会提高 InnoDB 写入 I/O 性能。

结语

落实前两条建议,请执行以下操作:

  • 删除 13 个非唯一索引
  • 导入数据
  • 除了创建的所有非唯一索引party_id的索引

也许以下可能会有所帮助

CREATE TABLE monster_new LIKE monster;
ALTER TABLE monster_new
  DROP INDEX `party_id`,
  DROP INDEX `creation_date`,
  DROP INDEX `email`,
  DROP INDEX `hash`,
  DROP INDEX `address_hash`,
  DROP INDEX `thumbs3`,
  DROP INDEX `ext_monster_id`,
  DROP INDEX `status`,
  DROP INDEX `note`,
  DROP INDEX `postcode`,
  DROP INDEX `some_id`,
  DROP INDEX `cookie`,
  DROP INDEX `party_id_2`;
ALTER TABLE monster RENAME monster_old;
ALTER TABLE monster_new RENAME monster;
Run Code Online (Sandbox Code Playgroud)

将数据导入到monster. 然后,运行这个

ALTER TABLE monster
  ADD INDEX `creation_date`,
  ADD INDEX `email` (`email`(4)),
  ADD INDEX `hash` (`hash`(8)),
  ADD INDEX `address_hash` (`address_hash`(8)),
  ADD INDEX `thumbs3` (`thumbs3`),
  ADD INDEX `ext_monster_id` (`ext_monster_id`),
  ADD INDEX `status` (`status`),
  ADD INDEX `note` (`note`(4)),
  ADD INDEX `postcode` (`postcode`),
  ADD INDEX `some_id` (`some_id`),
  ADD INDEX `cookie` (`cookie`),
  ADD INDEX `party_id_2` (`party_id`,`status`);
Run Code Online (Sandbox Code Playgroud)

试一试 !!!

选择

您可以创建一个名为monster_csvMyISAM 表且没有索引的表,然后执行以下操作:

CREATE TABLE monster_csv ENGINE=MyISAM AS SELECT * FROM monster WHERE 1=2;
ALTER TABLE monster RENAME monster_old;
CREATE TABLE monster LIKE monster_old;
ALTER TABLE monster DROP INDEX `party_id`;
Run Code Online (Sandbox Code Playgroud)

将您的数据导入到monster_csv. 然后,使用 mysqldump 创建另一个导入

mysqldump -t -uroot -p mydb monster_csv | sed 's/monster_csv/monster/g' > data.sql
Run Code Online (Sandbox Code Playgroud)

mysqldump 文件data.sql将扩展 INSERT 命令,一次导入 10,000-20,000 行。

现在,只需加载 mysqldump

mysql -uroot -p mydb < data.sql
Run Code Online (Sandbox Code Playgroud)

最后,去掉 MyISAM 表

DROP TABLE monster_csv;
Run Code Online (Sandbox Code Playgroud)


jyn*_*nus 8

我想写一个评论(因为这不是一个明确的答案),但它变得太长了:

我将为您提供几条广泛的建议,如果您愿意,我们可以详细介绍每条建议:

  • 降低耐久性(你已经做了一些)。最新版本甚至允许做更多。您可以尽可能禁用双写缓冲区,因为损坏不是导入的问题。
  • 通过以下方式增加缓冲:增加事务日志大小并增加可用的缓冲池大小。监视事务日志文件使用情况和检查点。不要害怕导入的巨大日志。
  • 避免大量事务 - 您的回滚将充满不需要的数据。这可能是你最大的问题。
  • SQL 将成为瓶颈,避免 SQL 开销(handlersocket、memcached)和/或同时与多个线程并发加载它。并发必须达到最佳状态,不能太多,也不能太少。
  • 按主键顺序加载数据碎片可能是一个问题
  • 如果 IO 是瓶颈并且 CPU 和内存不会使其变慢,请测试 InnoDB 压缩
  • 之后尝试创建辅助键(在某些情况下更快),不要加载索引数据 - DISABLE KEYS 不会影响 InnoDB。如果没有,请监视您的插入缓冲区(可能超过一半的缓冲池)。
  • 更改或禁用校验和算法 - 可能不是您的问题,但它成为高端闪存卡的瓶颈。
  • 最后的手段:监控您的服务器以找到您当前的瓶颈并尝试缓解(InnoDB 对此非常灵活)。

请记住,其中一些不安全或不建议用于非导入(正常操作)。