我需要通过各种来源更新主数据集,这些来源为某些现有记录提供了一些更改,例如新的手机号码。
每个查询的执行时间都在10小时以上。
环境:MySQL 8、8 核 CPU、32 GB 内存。
我有以下主表,它保存atm 3M记录:
CREATE TABLE `contact_data` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`email` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`email_status` tinyint(3) unsigned DEFAULT '0',
`mobile_phone` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL,
`firstname` varchar(128) COLLATE utf8mb4_general_ci DEFAULT NULL,
`lastname` varchar(128) COLLATE utf8mb4_general_ci DEFAULT NULL,
`nickname` varchar(128) COLLATE utf8mb4_general_ci DEFAULT NULL,
PRIMARY KEY (`email`),
UNIQUE KEY `id` (`id`),
KEY `country` (`country`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
到目前为止我已经尝试通过各种方式进行更新。源表大多只有 10K - 100K 条记录。我尝试使用 MyISAM 和“id”作为主键进行相同的操作。
加入:
UPDATE contact_data cd
LEFT JOIN (SELECT email, firstname FROM source2 WHERE firstname <> '' GROUP BY email ORDER BY id DESC) AS t2
ON cd.email = t2.email
SET cd.firstname = t2.firstname
直接:
UPDATE contact_data SET mobile_phone = (SELECT phone FROM source1 WHERE email = contact_data.email ORDER BY id DESC LIMIT 1)
WHERE mobile_phone IS NULL
直接但有记录限制:
UPDATE contact_data SET mobile_phone = (SELECT phone FROM source1 WHERE email = contact_data.email ORDER BY id DESC LIMIT 1)
WHERE mobile_phone IS NULL
AND email IN (SELECT DISTINCT email FROM source1)
配置:
innodb_buffer_pool_size = 16G
innodb_log_file_size = 512M
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT
innodb_log_buffer_size = 10M
key_buffer_size = 512M
对配置文件进行更多调整大多会带来微小的改进。
有什么我可以尝试的吗?
最佳答案
首先,您需要通过删除电子邮件重复项并在电子邮件字段上添加主/唯一索引来准备源表。
其次,因为您的源表比主表更小,您可以使用下一个查询:
UPDATE source s
JOIN contact_data cd ON s.email = cd.email
SET cd.mobile_phone = s.mobile_phone;
第三,您可以通过使用 mysqltuner 调整 MySQL 配置来提高更新性能或任何其他乐器
关于MySQL 在大型数据集上使用 IN 更新时速度缓慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59765740/