mysql InnoDB: FOREIGN KEY 约束性能
mysql InnoDB: FOREIGN KEY constraint performance
我有以下 InnoDB tables:
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;
Table 车辆有大约 45.000 条记录,table 零件有大约 3.500.000 条记录,table 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;
需要很长时间才能完成。我知道 table 是重建的,因为它消耗大量磁盘 space。我可以做些什么来提高性能?
如果我使用 fk 约束创建 table,然后添加记录,vehicle_part 中的插入过程也需要很长时间(大约 3 天)。
我使用的是 4GB RAM 的笔记本电脑。
编辑 2016 年 1 月 12 日
Drew 给出的答案对显着提高性能有很大帮助。我使用 SELECT ... INTO outfile 更改了每个脚本,然后从导出的 csv 文件中加载数据 INFILE。有时在 LOAD DATA INFILE 之前删除索引并在加载过程之后重新创建它们可以节省更多时间。无需仅删除二级索引的 fk 约束。
如果您从 FK 的角度知道您的数据是原始的,则按照评论中的建议建立没有二级索引的结构,但在模式中使用 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
根据操作评论,如何在初始模式创建后删除 referencing table 中的自动生成索引:
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 |
更多链接
- Temporarily disable foreign keys
- A Rolando Answer
我有以下 InnoDB tables:
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;
Table 车辆有大约 45.000 条记录,table 零件有大约 3.500.000 条记录,table 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;
需要很长时间才能完成。我知道 table 是重建的,因为它消耗大量磁盘 space。我可以做些什么来提高性能? 如果我使用 fk 约束创建 table,然后添加记录,vehicle_part 中的插入过程也需要很长时间(大约 3 天)。 我使用的是 4GB RAM 的笔记本电脑。
编辑 2016 年 1 月 12 日
Drew 给出的答案对显着提高性能有很大帮助。我使用 SELECT ... INTO outfile 更改了每个脚本,然后从导出的 csv 文件中加载数据 INFILE。有时在 LOAD DATA INFILE 之前删除索引并在加载过程之后重新创建它们可以节省更多时间。无需仅删除二级索引的 fk 约束。
如果您从 FK 的角度知道您的数据是原始的,则按照评论中的建议建立没有二级索引的结构,但在模式中使用 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
根据操作评论,如何在初始模式创建后删除 referencing table 中的自动生成索引:
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 |
更多链接
- Temporarily disable foreign keys
- A Rolando Answer