【问题标题】:mysql InnoDB: FOREIGN KEY constraint performancemysql InnoDB:外键约束性能
【发布时间】:2016-03-19 04:00:37
【问题描述】:

我有以下 InnoDB 表:

 CREATE TABLE `vehicle` (
  `ID` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  `Name` varchar(50) DEFAULT NULL,
  `Model` varchar(100) DEFAULT NULL,
  `Engine_Type` varchar(70) DEFAULT NULL,
  `Construction_From` date DEFAULT NULL,
  `Construction_To` date DEFAULT NULL,
  `Engine_Power_KW` mediumint(8) unsigned DEFAULT NULL,
  `Engine_Power_HP` mediumint(8) unsigned DEFAULT NULL,
  `CC` mediumint(8) unsigned DEFAULT NULL,
  `TTC_TYP_ID` int(11) unsigned DEFAULT NULL,
  `Vehicle_Type` tinyint(1) DEFAULT NULL,
  `ID_Body_Type` tinyint(3) unsigned DEFAULT NULL,
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=49407 DEFAULT CHARSET=utf8;

CREATE TABLE `part` (
  `ID` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `ID_Brand` smallint(5) unsigned DEFAULT NULL,
  `Code_Full` varchar(50) DEFAULT NULL,
  `Code_Condensed` varchar(50) DEFAULT NULL,
  `Ean` varchar(50) DEFAULT NULL COMMENT 'The part barcode.',
  `TTC_ART_ID` int(11) unsigned DEFAULT NULL COMMENT 'TecDoc ID.',
  `ID_Product_Status` tinyint(3) unsigned DEFAULT NULL,
  PRIMARY KEY (`ID`),
  UNIQUE KEY `TTC_ART_ID_UNIQUE` (`TTC_ART_ID`),
  UNIQUE KEY `ID_Brand_Code_Full_UNIQUE` (`ID_Brand`,`Code_Full`)
) ENGINE=InnoDB AUTO_INCREMENT=3732260 DEFAULT CHARSET=utf8;

CREATE TABLE `vehicle_part` (
  `ID_Vehicle` mediumint(8) unsigned NOT NULL,
  `ID_Part` int(11) unsigned NOT NULL,
  PRIMARY KEY (`ID_Vehicle`,`ID_Part`),
  KEY `fk_vehicle_part_vehicle_id_vehicle_idx` (`ID_Vehicle`),
  KEY `fk_vehicle_part_part_id_part_idx` (`ID_Part`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

表vehicle 有大约45.000 条记录,表部分有大约3.500.000 条记录,表vehicle_part 有大约100.000.000 条记录。 为 vehicle_part 创建二级索引并没有花费太长时间,大约 30 分钟。 我不能做的是创建外键约束:例如

ALTER TABLE `vehicle_part` 
ADD CONSTRAINT `fk_vehicle_part_vehicle_id_vehicle`
  FOREIGN KEY (`ID_Vehicle`)
  REFERENCES `vehicle` (`ID`)
  ON DELETE NO ACTION
  ON UPDATE NO ACTION;

需要很长时间才能完成。我知道该表已重建,因为它消耗了大量磁盘空间。我可以做些什么来提高性能? 如果我使用 fk 约束创建表,然后添加记录,则 vehicle_part 中的插入过程也需要很长时间(大约 3 天)。 我使用的是 4GB RAM 的笔记本电脑。

编辑 2016 年 12 月 1 日

Drew 给出的答案对显着提高性能有很大帮助。我使用 SELECT ... INTO outfile 更改了每个脚本,然后从导出的 csv 文件中加载数据。有时在 LOAD DATA INFILE 之前删除索引并在加载过程之后重新创建它们可以节省更多时间。不需要只删除二级索引的 fk 约束。

【问题讨论】:

  • 一个小提示......在索引表中插入大量数据会降低性能......如果您可以删除索引然后插入然后再次构建索引,性能将大大提高
  • 是的,这就是我所做的,并且性能有所提高,但主要问题不是索引而是外键约束。
  • 为什么不考虑对表进行分区,在分区上循环,按分区插入数据
  • 我对 FK 的看法?调试你的代码;那么你就不需要 FK 检查了。
  • @osamayaccoub - 添加PARTITIONing 需要很长时间,而且不会有任何帮助。

标签: mysql performance foreign-keys innodb


【解决方案1】:

如果您知道从 FK 角度来看您的数据是原始的,那么请按照 cmets 中的建议建立没有二级索引的结构,但在架构中使用 FK 但暂时禁用 FK 检查。

加载您的数据。如果是外部数据,当然要使用 LOAD DATA INFILE。

加载数据后,打开 FK 检查。并用Alter Table建立二级索引。

再次假设您的数据是干净的。对于风险厌恶者,还有其他方法可以在事后证明这一点。

create table student
(   id int auto_increment primary key,
    sName varchar(100) not null
    -- secondary indexes to be added later
);

create table booksAssigned
(   id int auto_increment primary key,
    studentId int not null,
    isbn varchar(20) not null,
    constraint foreign key `fk_b_s` (studentId) references student(id)
    -- secondary indexes to be added later
);


insert booksAssigned(studentId,isbn) values (1,'asdf'); -- Error 1452 as expected

set FOREIGN_KEY_CHECKS=0; -- turn FK checks of temporarily

insert booksAssigned(studentId,isbn) values (1,'asdf'); -- Error 1452 as expected

set FOREIGN_KEY_CHECKS=1; -- succeeds despite faulty data

insert booksAssigned(studentId,isbn) values (2,'38383-asdf'); -- Error 1452 as expected

根据 op cmets,如何在初始模式创建后删除 引用 表中的自动生成索引:

mysql> show create table booksAssigned;

| booksAssigned | CREATE TABLE `booksassigned` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `studentId` int(11) NOT NULL,
  `isbn` varchar(20) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `fk_b_s` (`studentId`),
  CONSTRAINT `booksassigned_ibfk_1` FOREIGN KEY (`studentId`) REFERENCES `student` (`id`)
) ENGINE=InnoDB |

mysql> set FOREIGN_KEY_CHECKS=0;
Query OK, 0 rows affected (0.00 sec)

mysql> drop index `fk_b_s` on booksAssigned;
Query OK, 0 rows affected (0.49 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> show create table booksAssigned;

| booksAssigned | CREATE TABLE `booksassigned` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `studentId` int(11) NOT NULL,
  `isbn` varchar(20) NOT NULL,
  PRIMARY KEY (`id`),
  CONSTRAINT `booksassigned_ibfk_1` FOREIGN KEY (`studentId`) REFERENCES `student` (`id`)
) ENGINE=InnoDB |

更多链接

【讨论】:

  • 但是如果我离开 FK 约束 InnoDB 会自动在引用列上创建二级索引,不是吗?数据也是外部的(csv 文件),但我不能使用加载数据 infile,因为我需要加入来自不同 csv 文件的数据。
  • 根据手册,我可能会同意你。但是,如果您执行set FOREIGN_KEY_CHECKS=0; 并插入几行,然后执行show create table booksAssigned,您可能会发现它不会自动修补您建议的索引。
  • 是的,我总是使用 FOREIGN_KEY_CHECKS=0 执行插入操作。但是插入所花费的时间太多了。之后即使是mysqldump也需要花费大量时间才能将数据导出到服务器。
  • 我永远不会那样做,因为导入是相反的。 stackoverflow.com/a/32702768
  • 至于你上面2行评论中的最后一个问题,mysqldump是一个强大而灵活的工具。我们正在尝试做的是尽可能快地执行导入。缺少使用原始输入文件或来自 3rd 方的工具,...最快的执行方式是使用获得的 CSV 数据,然后通过select into outfile 和 LOAD DATA INFILE 使用
猜你喜欢
  • 2010-12-07
  • 2014-06-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-24
相关资源
最近更新 更多