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
比较性能。
缺点是我只能获得总体运行时间,因此我必须等待完整导入完成才能获得结果。
到目前为止我的结果:
似乎没有“食谱”解决方案,必须自己找出最佳的设置组合。
除了关于在我的设置中更改什么的建议之外,我还非常感谢更多信息,我可以如何更好地对导入过程进行基准测试/获得更多洞察正在发生的事情以及瓶颈可能在哪里。
我试图阅读我正在更改的设置的文档,但我再次不知道任何副作用,如果我什至可能会因选择不当的值而降低性能。
目前,我想尝试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 缓冲池的插图。请注意,其中有一部分专用于插入缓冲区。那有什么作用?它将对二级索引的更改从缓冲池迁移到系统表空间(又名 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 空间。
我导入这个相当大的表的第一个建议是
摆脱重复的索引。在你的情况下,你有
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)
摆脱你不使用的索引。查看您的应用程序代码,看看您的查询是否使用了所有索引。您可能需要查看pt-index-usage以使其建议未使用哪些索引。
您应该将innodb_log_buffer_size增加到 64M,因为默认值为 8M。更大的日志缓冲区可能会提高 InnoDB 写入 I/O 性能。
落实前两条建议,请执行以下操作:
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_csv
MyISAM 表且没有索引的表,然后执行以下操作:
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)
我想写一个评论(因为这不是一个明确的答案),但它变得太长了:
我将为您提供几条广泛的建议,如果您愿意,我们可以详细介绍每条建议:
请记住,其中一些不安全或不建议用于非导入(正常操作)。