我有一个大约4.2亿条记录的大型数据集,我能够使用 LOAD DATA INFILE
声明。我需要这个临时表来暂存数据,因为在将数据加载到最终目的地之前,我对它进行了一些清理。
临时表定义为:
CREATE TABLE `temporary_data` (
`t_id` smallint(10) unsigned NOT NULL,
`s_name` varchar(512) NOT NULL,
`record_type` varchar(512) NOT NULL,
`record_value` varchar(512) NOT NULL
) ENGINE=MyISAM;
需要加载此数据的目标表称为 my_data
定义为:
CREATE TABLE `my_data` (
`s_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`t_id` smallint(10) unsigned NOT NULL,
`s_name` varchar(63) NOT NULL,
PRIMARY KEY (`s_id`),
UNIQUE KEY `IDX_MY_DATA_S_NAME_T_ID` (`t_id`,`s_name`) USING BTREE,
KEY `IDX_MY_DATA_S_NAME` (`s_name`) USING BTREE,
CONSTRAINT `FK_MY_DATA_MY_PARENT` FOREIGN KEY (`t_id`) REFERENCES `my_parent` (`t_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
问题是,要将数据从临时表加载到 my_data
很慢,因为 my_data
包含两个索引和一个主键。到目前为止,该查询已经运行了6个多小时:
INSERT IGNORE INTO my_data (t_id, s_name)
SELECT t_id, s_name
FROM temporary_data;
我需要确定一种方法来加快这个查询,以便它及时完成(30分钟以下将是理想的)。
我考虑过的一些方法:
禁用索引:我也许可以通过禁用/删除 IDX_MY_DATA_S_NAME
索引,但我依赖于唯一的索引( IDX_MY_DATA_S_NAME_T_ID
)保持数据的干净。这是一个每天都会自动运行的过程,不可避免地会有一些重复。另外,当我再次启用索引时,在这么大的数据集上重建索引似乎也同样耗时。
使用数据输出文件:将清理后的数据直接导出并重新导入到 my_data
. 我在某个地方看到了这个建议,但仔细考虑之后,index/pk仍然是重新插入时的争论点。
交换表:替换 my_data
与 temporary_data
听起来很吸引人,但是这个表有很多外键关系 s_id
字段,所以我需要一些保证,这种方法将值得麻烦的禁用外键和重新启用它们。子表包含的记录将明显少于 my_data
因此,在这方面,重新启用外键可能可以忽略不计。
直接加载数据填充:将数据直接加载到 my_data
在语句的set部分使用条件使所有字段 NULL
当它不符合我最初应用的清理标准时 temporary_data
在将其装入 my_data
. 这是黑客,但它是依靠的假设,加载数据填充将比插入更快。。。即使在编制索引时也要选择,由于表上的唯一约束,在运行之后也只需要删除一行空值。
这些听起来都不是什么好主意。如果有人有什么建议,我洗耳恭听。
1条答案
按热度按时间yqlxgs2m1#
摆脱
s_id
,可能是没用的。然后提升UNIQUE(t_id, s_name) to be the
主键“”。这减少了为插入的每一行执行的测试的数量。考虑禁用
FOREIGN KEYs
; 毕竟,他们需要执行可能是多余的检查。这样,插入就不会在目标表中跳跃,从而(希望)避免了大量的i/o。
你在扩充table吗?或者替换它?如果替换,有一个更好的方法。
更多。。。
你注意到了吗
INSERT IGNORE
浪费AUTO_INCREMENT
未插入的每行的值?让我们试试另一种方法。。。这个
ORDER BY
避免在比赛中跳来跳去INSERT
.这个
LEFT JOIN
将活动限制为“新”行。不
AUTO_INCREMENT
价值观将被烧毁。每次插入多少行?如果它是数百万,那么最好把它分成小块。请看我关于分块的讨论。它可能比构建一个巨大的撤销路径来最终抛出要快。
进一步讨论——给出
这些是有效的:
自从
ORDER BY
而二级索引是相同的,对索引的添加将是有效的。同时,新的
AUTO_INCREMENT
值将在表的“末尾”按顺序生成。最好的办法是
(t_id, s_name)
是独一无二的。然后我们可以考虑摆脱s_id
将两个索引改为:如果其他表引用
s_id
. 一个可能的解决方法是保留s\u id并我对大局和其他问题了解不够,无法判断应该朝哪个方向走。所以我最初的建议(在“进一步讨论”之前)是“保守的”。