본문 바로가기
Database

외래키를 사용하지 않는 이유 feat. 인덱스

by softserve 2022. 5. 25.
반응형

외래 키(foreign key) 란

 

관계형 데이터베이스에서 다른 릴레이션(테이블)을 참조할 때 사용하는 키입니다.

아래 학생 릴레이션을 봅시다.

id 학생명 강의명 강의코드
1 개똥이 병충해 방지기법 1231
2 길동이 분신술 4242
3 말똥이 축산업 개론 2323

우리는 길동이에게 분신술을 가르치는 사람이 누구인지 알고 싶지만 학생 릴레이션에는 관련된 정보가 없습니다.

강의 코드가 4242라는 것만 알 수 있을 뿐입니다.

그럼 강의 릴레이션을 한번 뒤져봐야겠죠?

강의코드 강의명 교수
1231 병충해 방지기법 김개동
4242 분신술 나루도
2323 축산업 개론 이말동

강의 코드 4242인 행을 보면 교수님 이름이 나루도라는 것을 알 수 있습니다.

이렇게 우리는 학생 릴레이션으로부터 강의 릴레이션을 참조하기 위해 강의 코드를 사용했고,

이때 강의 코드가 바로 외래 키입니다.

 

  • 외래 키에는 다음과 같은 제약사항이 따릅니다.

참조되는 릴레이션, 즉 외래 키의 고향인 강의 릴레이션에서 외래 키에 해당하는 속성은 기본키이거나 유일해야 합니다.

강의 코드 4242에 대한 정보를 찾으려고 했더니 분신술도 4242이고 축산업 개론도 4242라면 외래 키는 제 역할을 전혀 수행할 수 없게 되어버립니다.

참조되는 릴레이션의 행을 유일하게 식별할 수 있는 칼럼만이 외래 키가 될 수 있습니다.

 

  • 외래 키는 무결성을 유지하기 위해 사용합니다.

현재 4자리 숫자로 이루어진 강의 코드에 알파벳을 추가하여 확장하려고 합니다. 문제는 강의 릴레이션을 참조하고 있는 여러 릴레이션들에 포함된 강의 코드를 모두 수정해줘야 한다는 것입니다.

담당자가 깜빡하고 학생 릴레이션의 강의 코드를 수정하지 못했다면 학생으로부터 강의 릴레이션을 더 이상 참조할 수 없게 되고, 이를 전제로 구현된 여러 프로그램들에 오류가 발생하는 대참사가 벌어지게 됩니다.

만약 강의 코드를 외래 키로 만들면서 'ON UPDATE CASCADE'라는 옵션을 추가해주었다면 강의 코드를 참조하는 모든 릴레이션의 강의 코드가 함께 수정되기 때문에 이런 고민을 하지 않아도 됩니다.

무결성이란 현재 데이터베이스에 존재하는 값이 틀리지 않았다는 것을 의미합니다.

어떤 키를 수정(ON UPDATE, 이하 MySQL기준) 또는 삭제(ON DELETE)하려고 할 때 다른 곳에서 해당 키를 참조하고 있다면

- 키 자체를 수정, 삭제 못하도록 막거나(RESTRICT)

- 참조하는 릴레이션에서도 모두 함께 수정, 삭제되도록 할 수 있습니다(CASCADE).

- 또는 참조하는 릴레이션에서 해당 키의 값을 NULL로 바꿔줄 수도 있습니다(SET NULL)

이렇게 외래 키를 사용하면 서로 얽히고 얽힌 릴레이션들의 데이터들이 모두 무결성을 갖추도록 할 수 있습니다.

 

외래 키를 사용하지 않는 이유

 

하지만 실무에서는 몇 가지 이유에서 외래키를 사용하지 않기도 합니다.

신입 개발자 입장에서 왜 외래키를 사용하지 않는 건지 의아한 건 사실입니다. 제가 알아본 몇 가지 이유는 다음과 같습니다.

일단은 귀찮습니다.

RESTRICT나 CASCADE 옵션을 넣거나 넣지 않거나 어느 쪽이든 개발자 입장에서 신경 써야 할 부분이 늘어나기 때문에 불편한 것이 사실입니다. 데이터의 양이 더 많아지고 관계가 복잡해질수록 더욱 심해지겠죠.

예를 들어, 참조되는 릴레이션은 참조하는 릴레이션보다 먼저 만들어져 있어야 합니다. 때문에 생성, 수정할 때마다 순서를 신경 써줘야 하는 번거로움이 발생하게 됩니다.

CASCADE로 설정해두었을 경우에 한 릴레이션의 데이터를 실수로 바꿨을 경우 참조하는 릴레이션들의 데이터가 모두 변경되기 때문에 특히 데이터를 보존하는 것이 중요한 시스템에서는 외래 키를 사용하지 않는 것이 조금 더 안정적이라고 볼 수 있습니다.

ON DELETE 나 ON UPDATE 옵션이 제대로 기능하려면 매번 무결성을 검사해줘야 하니 속도, 성능 저하가 발생할 수 있다는 얘기도 있습니다. 하지만 무결성이 유지되지 않으면 데이터베이스를 사용하는 의미가 없게 되고 따라서 외래 키를 사용하지 않더라도 어떻게든 무결성을 확인해줄 필요가 있으니 이 부분은 별로 고려대상이 되지 않을 것 같습니다.

한 마디로 요약해서 외래 키를 사용하지 않는 것은

무결성, 정합성을 희생시켜 개발 편의와 안정성, 확장성을 도모하기 위함이라고 할 수 있습니다.

그럼 외래키를 사용하지 않는 데이터베이스를 어떻게 만드느냐?

 

대신 인덱스를 사용합니다.

 

인덱스란 데이터베이스의 접근 속도를 높이기 위해, 자주 사용되는 속성에 대해서 별도의 인덱스 테이블을 만들어 해당 위치를 빠르게 찾기 위해 사용하는 것입니다.

id 학생명 강의명 강의코드
1 개똥이 병충해 방지기법 1231
2 길동이 분신술 4242
3 말똥이 축산업 개론 2323

위 릴레이션에서 id 120번인 회원을 찾으려면 테이블 전체를 탐색해서 id가 120인 행을 찾아야 합니다.

하지만 인덱스가 있다면 인덱스 테이블에서 id가 120인 행의 위치를 찾은 다음

해당 위치로 바로 접근할 수 있어 시간이 절약됩니다.

* 경우에 따라서 인덱스를 사용하는 것이 더 불리할 수 있어 인덱스를 만들 때는 신중할 필요가 있습니다.

보통 기본키와 외래 키에 대해서는 자동으로 인덱스 테이블이 생성되기 때문에 별도로 인덱스를 만들어줄 필요가 없습니다. 하지만 개념상 외래 키에 해당하는 속성을 외래 키로 설정하지 않고 사용하는 경우는 검색 속도를 높이기 위해 인덱스를 만들어주어야 합니다.

외래 키는 다른 릴레이션들에 그대로 복붙 해줍니다. 만약 외래 키를 수정하는 경우에는 외래 키를 참조하는 릴레이션들을 모두 기억해두었다가 모두 수정해주어야 합니다. 이 작업을 스크립트로 만들어서 일괄 처리하면 조금 더 편해질 수도 있겠지만 어쨌든 외래키를 사용하는 것에 비해 추가적인 수고가 들어간다는 건 사실입니다.

인덱스를 만들 때는 필요에 따라 유니크 인덱스로 만들 수도 있고

필요에 따라 여러 속성을 묶어서 만들 수도 있습니다. 이런 다중 칼럼 인덱스를 만들 때 유의할 점은 순서입니다.

CREATE INDEX INDEXNAME ON TABLENAME (COL1, COL2, COL3);

위 예시에서처럼 여러 개의 칼럼을 인덱스로 만든 경우 

COL1으로 검색하거나

COL1, COL2로 검색하거나

COL1, COL2, COL3로 검색할 때는 인덱스가 적용되지만(인덱스 테이블을 타지만)

COL2 또는 COL3만으로 검색하거나

COL2, COL3의 조합으로 검색할 때는 인덱스가 적용되지 않습니다.

따라서 COL2나 COL3로 자주 검색할 것이 예상된다면 순서를 바꾸거나 별도로 COL2, COL3에 대한 인덱스를 만들어줄 필요가 있습니다.

반응형

댓글