MySQL在相关大表上慢速INSERT,100%CPU使用

时间:2015-09-18 04:27:15

标签: mysql indexing insert innodb insert-update

我正在构建一个以Amazon RDS MySQL实例作为后端的网站(LAMP堆栈)(类型为db.m3.medium)。

我对数据库完整性感到满意,它在SELECT / JOIN / ETC查询方面非常有效(所有表都是规范化,索引和外键控制,所有表都有id主键和相关的辅助键/唯一键)。 / p>

我有一张'df_products'表,里面有大约五十万种产品。产品需要每晚更新。该过程涉及PHP脚本读取大型产品数据文件并将数据插入到多个表(产品表,product_colours表,品牌表等)中,根据行是否已存在调用INSERT或UPDATE。这是一次巨额交易。

我看到的是UPDATE命令足够快(50 /秒,不完全闪电但应该这样做),但是INSERT命令超级慢(1 /秒)并且似乎消耗了100%的CPU 。在双核实例上,我们看到50%的CPU使用率(即一个完整核心)。 我假设这是因为索引(1x PRIMARY + 5x INDEX + 1x UNIQUE + 1x FULLTEXT)在每次INSERT后重建。但是,我将整个流程放入一个事务中应该停止重建索引,直到提交事务为止。

我尝试过通过PHP设置以下参数,但性能改善可以忽略不计:

$this->db->query('SET unique_checks=0');
$this->db->query('SET foreign_key_checks=0;');

此过程需要数周才能完成,因此我们必须提高效果。 Google似乎建议使用LOAD DATA。但是:

  • 我必须生成五个文件才能填充五个表
  • 由于表已经存在,因此该过程必须使用UPDATE命令而不是INSERT
  • 我仍然需要遍历产品并扫描数据库以查找已经存在且不存在的值

数据库完全是InnoDB,我不打算转移到MyISAM(我想要交易,外键等)。这意味着我无法禁用索引。即使我这样做也可能是一个巨大的性能消耗,因为我们需要在插入之前检查一行是否已经存在,并且没有索引这将是超级慢。

我已提供以下产品表格定义以供参考。您能否就我们应该使用什么过程来提供建议,以便在多个大型相关表上实现更快的INSERT / UPDATE?或者我们可以对现有流程进行哪些优化?

谢谢,

CREATE TABLE `df_products` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `id_brand` int(11) NOT NULL,
  `title` varchar(255) NOT NULL,
  `id_gender` int(11) NOT NULL,
  `id_colourSet` int(11) DEFAULT NULL,
  `id_category` int(11) DEFAULT NULL,
  `desc` varchar(500) DEFAULT NULL,
  `seoAlias` varchar(255) CHARACTER SET ascii NOT NULL,
  `runTimestamp` timestamp NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `seoAlias_UNIQUE` (`seoAlias`),
  KEY `idx_brand` (`id_brand`),
  KEY `idx_category` (`id_category`),
  KEY `idx_seoAlias` (`seoAlias`),
  KEY `idx_colourSetId` (`id_colourSet`),
  KEY `idx_timestamp` (`runTimestamp`),
  KEY `idx_gender` (`id_gender`),
  FULLTEXT KEY `fulltext_title` (`title`),
  CONSTRAINT `fk_id_colourSet` FOREIGN KEY (`id_colourSet`) REFERENCES `df_productcolours` (`id_colourSet`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  CONSTRAINT `fk_id_gender` FOREIGN KEY (`id_gender`) REFERENCES `df_lu_genders` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=285743 DEFAULT CHARSET=utf8

1 个答案:

答案 0 :(得分:0)

有多少"性别"在那儿?如果通常的2,不要将其标准化,不要将其编入索引,请不要使用4字节的INT来存储它,使用CHAR(1) CHARACTER SET ascii(仅1个字节)或ENUM(1个字节)。

每个不必要的索引都是负载的性能消耗,无论它是如何完成的。

对于INSERT vs UPDATE,请查看使用INSERT ... ON DUPLICATE KEY UPDATE

将夜间数据加载到单独的表中(这可能是没有索引的MyISAM)。然后运行一个查询来更新现有行,一个查询以插入新行。 (每个都需要JOIN。)请参阅http://mysql.rjweb.org/doc.php/staging_table,尤其是用于"规范化"的2个SQL。它们可以根据您的情况进行调整。

任何类型的多行查询一次运行速度明显快于1行。 (100行INSERT的运行速度是100个1行插入的10倍。)

innodb_flush_log_at_trx_commit = 2会让个人写入语句运行得更快。 (按照我的建议对它们进行批量处理并加快速度。)