MySQL Foreign Key 1215 error
얼마전 담당하는 MySQL 서비스의 복제본 한벌을 더 추가 구성하게 되었습니다.
single-transaction + dump-slave 혹은 single-transaction + master-data 옵션으로 mysqldump 를 받은 뒤
새 서버에 반영해주고 dumpfile에 dump-slave / master-data 옵션으로 인해 딸려온 change master to 구문을 확인하여
replication을 맺어주면 되는 아주 간단한 작업인데요
그런데 실패했습니다…
- replication error log
show slave status\G;
.
.
Last_Errno: 1146
Last_Error: Error 'Table 'xxxxx.xxxx' doesn't exist' on query. Default database: 'xxxxxxxxxxxx'. Query: 'UPDATE xxxx SET lastused = NOW(), lingertime = 7 WHERE siteid = 'xxxxxxxxxxx''
.
.
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 1146
Last_SQL_Error: Error 'Table 'xxxxxxxx.xxxxx' doesn't exist' on query. Default database:'xxxxxxx'. Query: 'UPDATE xxxxx SET lastused = NOW(), lingertime = 7 WHERE siteid = 'xxxxxxxxxx''
Replicate_Ignore_Server_Ids:
Master_Server_Id: 1
이전에도 종종 해본 작업이고 mysqldump 를 그대로 import 하고 백업받을 때 master의 binary log position 도 정확히 잘 찍어놔서
데이터가 유실될 일은 없을 터인데..
dump 복원 과정에서 무려 테이블이 유실되어 당혹스러웠는데요
Import 에러 로그부터 dump file의 내용까지 천천히 확인해보니 이유를 알 수 있었습니다.
- import error log
line 5048 ERROR 1215 (HY000): Cannot add foreign key constraint
정확하진 않지만 대략 위 에러문구였습니다.
mysqldump file을 반영하다가 line 5048번에서 FK 생성 에러가 발생했습니다.
보통 mysqldump을 별다른 옵션없이 수행하면 아래처럼 FK check 옵션을 끄고 테이블 생성 & 데이터 적재 후 모든작업을 마치고
마지막에 다시 FK 옵션을 원복해줍니다.(이전에 off였든 on이었든)
### dumpfile 시작부분
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
### dumpfile 끝부분
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
이 FOREIGN_KEY_CHECKS=0 옵션 덕에 parent table 보다 child table이 먼저 생성되는 경우나
데이터 dml 순서도 FK제한없이 처리될 수 있습니다.
그런데도 왜 FK에러가 났느냐?
위 import error log 에러에서 나온 mysqldump file의 5048 line을 살펴보니 아래와 같은 테이블 생성구문이었습니다.
- 생성 실패한 table
CREATE TABLE `tb_test_child` (
`id` varchar(64) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`column_fk` varchar(64) DEFAULT NULL,
`col2` tinyint(1) NOT NULL,
`col3` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx1` (`name`),
KEY `idx2` (`column_fk`),
CONSTRAINT `tb_test_child_fk1` FOREIGN KEY (`column_fk`) REFERENCES `tb_test_parent` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
=> 에러로그에서 FK를 생성하는데 실패했다고 했기 때문에 tb_test_parent 테이블의 id 컬럼을 reference 하여 FK 생성에 실패한 것이 되는데요
그래서 tb_test_parent 테이블의 생성구문을 확인해봤습니다.
- parent table
CREATE TABLE `tb_test_parent` (
`id` varchar(64) CHARACTER SET utf8mb4 NOT NULL DEFAULT '',
`name` varchar(255) DEFAULT NULL,
.
.
`create_time` datetime DEFAULT NULL,
`deleted_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `tb_test_parent_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
=> tb_test_child의 tb_test_child_fk1 FK는 tb_test_parent의 id를 refernce 합니다.
이때 FK 생성 조건에는 아래와 같은 조건 등이 있습니다.
- 참조하는 컬럼이 FK 컬럼과 동일한 타입, length를 가져야한다
- 참조하는 컬럼은 해당 테이블에서 어떤 종류던 key여야한다
- parent , child의 해당 컬럼의 charset과 collation은 동일해야한다
이번 에러는 어떤 이유에서인지 dumpfile에서 FK 컬럼의 charset과 collation이 parent / child 간 상이했습니다.
# parent
`id` varchar(64) CHARACTER SET utf8mb4 NOT NULL DEFAULT '',
# child
`column_fk` varchar(64) DEFAULT NULL,
CONSTRAINT `tb_test_child_fk1` FOREIGN KEY (`column_fk`) REFERENCES `tb_test_parent` (`id`)
그래서 child 테이블의 column_fk 컬럼을 parent의 id컬럼과 동일하게 CHARACTER SET utf8mb4로 맞춰 생성 후 정상화 되었습니다.