내가 배우기로는 외래키를 사용하면 데이터 무결성과 데이터 정합성을 보장하여 데이터베이스 관리가 용이해 진다고 배웠다. 하지만 실무에서는 외래키로 인한 오버헤드가 발생하여 쓰이지 않는 곳이 매우 많다고한다. 그럼 어떤 오버헤드가 발생하는지 알아보도록 하자
🚨외래키가 발생시키는 오버헤드
테이블 B
의 INSERT
쿼리를 실행시킬경우 테이블 A
의 참조되는 키가 있는지 확인하여 SELECT
쿼리를 날린다.테이블 B
가 UPDATE
될때 업데이트되는 값이 테이블 A
에도 있는지 확인하는 조회
쿼리가 실행된다.테이블 B
의 있는 데이터가 삭제
될때 테이블 B
의 참조되는 테이블을 조회
하는 쿼리를 실행한 후 테이블 A
가 참조되는 것을 찾았다면 테이블 A
의 데이터를 함께 삭제
한다.JPA
의 N+1
문제와 유사한문제이다.
이처럼 CUD(Create, update, delete)
와 외래키 검증 과정에서 발생되는 쿼리들로 인해 연관되어 있는 테이블들때문에 추가적인 작업이 필요하며 추가적인 작업을 실행할때마다 모두 락
이 걸리게 된다. 이는 데드락(Dead Rock)
을 발생시키는 요인으로써 치명적인 장애
를 발생시킬 수 있다.
테스트 코드 데이터
를 만들때도 불편함을 유발한다.
@BeforeEach
public void init() {
RequestScheduleWithUserDto requestScheduleWithUserDto1 = new RequestScheduleWithUserDto();
requestScheduleWithUserDto1.setUsername("user1");
requestScheduleWithUserDto1.setPassword("1234");
requestScheduleWithUserDto1.setTodoList("hi");
requestScheduleWithUserDto1.setEmail("user1@naver.com");
scheduleId = UUID.randomUUID();
userId = UUID.randomUUID();
// USER 테이블과 SCHEDULE 테이블 저장
userRepository.add(scheduleId, userId, LocalDateTime.now(), requestScheduleWithUserDto1);
scheduleRepository.add(scheduleId, userId, LocalDateTime.now(), requestScheduleWithUserDto1);
}
연관관계
가 존재하는 테이블들을 모두 저장
해야 한다는 단점이 존재한다.테스트 코드 데이터만을 생성하는데도 프로그래밍의 발목을 잡게되어버린다. 실무에서는 테이블 설계와 요구사항으로 인해 데이터베이스 구조는 바뀔 수 있다. 외래키가 잡혀있으면 시간대비 비용처리가 크다는 관점이 존재한다. 하지만 물리적으로 FK
가 존재하지 않는다는 뜻이지 논리적으로는 FK
가 존재한다. FK
제약조건없이 JOIN
을 통해 해결하거나 UNIQUE
와 인덱스
를 이용하여 해결한다고 한다. 그렇기에 당연하게도 무결성
으로 만족하는 프로그래밍이 따라줘야한다.
운영적인 측면에서도 불편함은 존재한다. 참조 테이블에 대한 데이터 재적재 시 종속 테이블의 FK
를 해제한 후 작업해야한다. 작업이 다 이루어진후 FK
를 활성화할 때 검증에 시간이 소요된다. 빈번하게 데이터 재적재가 일어난다면 비즈니스 로직에 집중되어야할 개발 환경에 치명적일 수 있어 테이블에 대한 FK
생성의 대해 고민이 필요하다.
📖 정리
무결성
을 보장하는 도구
이다.DML
성능문제를 야기할 수 있다. (특히 INSERT/UPDATE)정합성
과 무결성
이 중요하기때문에 외래키 제약조건이 필요하다.JOIN
, UNIQUE
, INDEX
등으로 처리할 수 있다.추가
/변경
/확장
에 시간대비 큰 비용
이 발생할 수 있다.