【发布时间】:2020-12-19 15:07:10
【问题描述】:
我正在尝试将我拥有的几个 MySQL 表非规范化为一个新表,我可以使用它来加速一些具有大量业务逻辑的复杂查询。我遇到的问题是我需要将 230 万条记录添加到新表中,为此我需要从多个表中提取数据并进行一些转换。这是我的查询(名称已更改)
INSERT INTO database_name.log_set_logs
(offload_date, vehicle, jurisdiction, baselog_path, path,
baselog_index_guid, new_location, log_set_name, index_guid)
(
select STR_TO_DATE(logset_logs.offload_date, '%Y.%m.%d') as offload_date,
logset_logs.vehicle, jurisdiction, baselog_path, path,
baselog_trees.baselog_index_guid, new_location, logset_logs.log_set_name,
logset_logs.index_guid
from
(
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(path, '/', 7), '/', -1) as offload_date,
SUBSTRING_INDEX(SUBSTRING_INDEX(path, '/', 8), '/', -1) as vehicle,
SUBSTRING_INDEX(path, '/', 9) as baselog_path, index_guid,
path, log_set_name
FROM database_name.baselog_and_amendment_guid_to_path_mappings
) logset_logs
left join database_name.log_trees baselog_trees
ON baselog_trees.original_location = logset_logs.baselog_path
left join database_name.baselog_offload_location location
ON location.baselog_index_guid = baselog_trees.baselog_index_guid);
查询本身有效,因为我能够使用log_set_name 上的过滤器运行它,但是该过滤器的条件仅适用于不到总记录的 1%,因为 log_set_name 的值之一有 220 万条记录其中大部分是记录。因此,我无法使用其他方法将这个查询分解为我所看到的更小的块。问题是查询在 220 万条记录的其余部分上运行的时间太长,并且在几个小时后最终超时,然后事务被回滚,并且没有任何内容添加到 220 万条记录的新表中;只有 10 万条记录能够被处理,那是因为我可以添加一个过滤器,说明 log_set_name != 'value with the 2.2 million records' 的位置。
有没有办法让这个查询更高效?我是否试图一次进行太多连接,也许我应该在他们自己的单独查询中填充行的列?或者有什么方法可以分页这种类型的查询,以便 MySQL 批量执行它?我已经删除了log_set_logs 表上的所有索引,因为我读到这些索引会减慢插入速度。我还将我的 RDS 实例提升到 db.r4.4xlarge 写入节点。我也在使用 MySQL Workbench,所以我将它的所有超时值都增加到了最大值,给了它们所有的 9。所有这三个步骤都有帮助,并且对于我将 1% 的记录放入新表中是必要的,但仍然不足以在不超时的情况下获得 220 万条记录。感谢任何见解,因为我不擅长从选择中进行这种类型的批量插入。
'CREATE TABLE `log_set_logs` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`purged` tinyint(1) NOT NULL DEFAUL,
`baselog_path` text,
`baselog_index_guid` varchar(36) DEFAULT NULL,
`new_location` text,
`offload_date` date NOT NULL,
`jurisdiction` varchar(20) DEFAULT NULL,
`vehicle` varchar(20) DEFAULT NULL,
`index_guid` varchar(36) NOT NULL,
`path` text NOT NULL,
`log_set_name` varchar(60) NOT NULL,
`protected_by_retention_condition_1` tinyint(1) NOT NULL DEFAULT ''1'',
`protected_by_retention_condition_2` tinyint(1) NOT NULL DEFAULT ''1'',
`protected_by_retention_condition_3` tinyint(1) NOT NULL DEFAULT ''1'',
`protected_by_retention_condition_4` tinyint(1) NOT NULL DEFAULT ''1'',
`general_comments_about_this_log` text,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1736707 DEFAULT CHARSET=latin1'
'CREATE TABLE `baselog_and_amendment_guid_to_path_mappings` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`path` text NOT NULL,
`index_guid` varchar(36) NOT NULL,
`log_set_name` varchar(60) NOT NULL,
PRIMARY KEY (`id`),
KEY `log_set_name_index` (`log_set_name`),
KEY `path_index` (`path`(42))
) ENGINE=InnoDB AUTO_INCREMENT=2387821 DEFAULT CHARSET=latin1'
...
'CREATE TABLE `baselog_offload_location` (
`baselog_index_guid` varchar(36) NOT NULL,
`jurisdiction` varchar(20) NOT NULL,
KEY `baselog_index` (`baselog_index_guid`),
KEY `jurisdiction` (`jurisdiction`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1'
'CREATE TABLE `log_trees` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`baselog_index_guid` varchar(36) DEFAULT NULL,
`original_location` text NOT NULL, -- This is what I have to join everything on and since it's text I cannot index it and the largest value is above 255 characters so I cannot change it to a vachar then index it either.
`new_location` text,
`distcp_returncode` int(11) DEFAULT NULL,
`distcp_job_id` text,
`distcp_stdout` text,
`distcp_stderr` text,
`validation_attempt` int(11) NOT NULL DEFAULT ''0'',
`validation_result` tinyint(1) NOT NULL DEFAULT ''0'',
`archived` tinyint(1) NOT NULL DEFAULT ''0'',
`archived_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`dir_exists` tinyint(1) NOT NULL DEFAULT ''0'',
`random_guid` tinyint(1) NOT NULL DEFAULT ''0'',
`offload_date` date NOT NULL,
`vehicle` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `baselog_index_guid` (`baselog_index_guid`)
) ENGINE=InnoDB AUTO_INCREMENT=1028617 DEFAULT CHARSET=latin1'
【问题讨论】:
-
请编辑您的问题并包含这些表及其索引的 DDL -
database_name.tableA、database_name.tableB和database_name.tableD -
那些看起来更像图片而不是 DDL
-
我们希望看到每个表格的
SHOW CREATE TABLE <name>的text 输出,而不是屏幕截图。这为我们提供了有关表中索引的更多信息。使用文本而不是屏幕截图允许人们复制并粘贴到测试环境中以重现查询。 Stack Overflow guide 还建议不要使用代码图像。 -
好的,DDL 已添加。我在
log_trees.original_location上留下了评论,我认为最终的问题是我必须加入那个文本字段,这意味着我不能索引它。它的最大值超过 255 个字符,所以我不能将它们更改为 varchars 然后索引它们。 -
如果有人对此问题感兴趣,就开始悬赏。
标签: mysql mysql-workbench query-optimization amazon-rds