1. 목표: 데이터베이스 아키텍쳐 단순화

관계형 데이터베이스 설계는 테이블 구조 뿐 아니라 테이블간의 관계도 구성해야 한다. 여기서 참조 무결성(Reference Integrity)은 데이터베이스를 적절히 설계하고 운영하는 데 있어 중요한 부분이다.

1.1 일반적으로 FK를 무시하라고 하는 이유

일반적으로 사람들이 FK를 무시하라고 하는 이유

  • 데이터 업데이트 시 제약 조건과 충돌할 수 있다.
  • 참조 정합성 제약 조건을 지원할 수 없는 매우 융통성있는 데이터베이스 설계를 사용하고 있다.
  • FK에 데이터베이스가 자동 생성하는 인덱스 때문에 성능에 영향을 받는다고 믿는다.
  • FK를 지원하지 않는 데이터베이스를 사용하고 있다.
  • FK선언을 위해 문법을 찾아봐야 한다.

2. 안티패턴:제약조건 무시

FK 제약 조건을 사용하지 않는 경우 참조 무결성을 보장하는 다른 방법을 강구해야 한다.

2.1 무결점 코드

FK를 사용하지 않는 경우 application에서 관련 작업을 진행해 줘야 한다.
즉,

  • 행을 삽인할 때 마다 FK 칼럼에 해당하는 값이 저장된 테이블을 조회해서 확인한다.
  • 행을 삭제하는 작업 마다 자식 테이블이 적절히 업데이터되는지 확인해야 한다.

비즈니스 로직 작성 시 실수를 해서는 안된다.

2.1.1 예제

Bugs 테이블에 새로운 행을 삽입한다고 가정하고 FK를 지키기 위한 작업을 진행하면 다음과 같을 것이다.

  1. 부모 테이블(Account)에 참조 데이터가 있는지 확인한다.
  2. 참조하는 버그를 삽입한다.

버그 테이블에 행을 삭제하는 경우에는 다음과 같다.

  1. Bugs 테이블에 자식 행이 없다는 것을 확인한다.
  2. 버그를 삭제한다.

하지만, 1번 작업과 2번 작업 사이에 다른 사용자에 의해 데이터 변경이 발생하면???
해결 방법이 없다. 즉, 참조 무결성이 깨질 수 있다.
유일한 해결책은 확인전에 Bugs 테이블을 잠그고 작업을 진행하는 것이다. 이건 동시성과 확장성이 떨어지게 된다.

2.2 오류 확인

유효하지 않은 상태의 bugs 데이터를 찾기위해 다음과 같은 sql을 사용할 수도 있다.


SELECT b.bug_id, b.status
FROM Bugs b LEFT OUTER JOIN BugStatus s
ON (b.status = s.status)
WHERE s.status IS NULL;

application단에서 오류 확인을 하려면 위와 같은 쿼리를 모든 참조 관계에서 처리해야 하는데, 이것을 주기적으로 계속적으로 한다는 것은 굉장히 불필요한 일일 것이다.
필요에 따라서는 이상한 값을 가진 레코드를 default값으로 변경할수도 있지만, 이것도 때에 따라서는 어떤 값으로 변경할지 결정할 수 없는 것도 있을 것이고, 이러한 작업 자체가 참으로 귀찮은 일이다.

2.1.3 내 잘못이 아니야

데이터베이스를 변경하는 모든 로직은 여러 함수를 사용하여 비즈니스 로직도 다양하다. 이 모든 코드에서 모든 경우에 대해 문제가 없도록 변경을 적용하는 것을 어떻게 확신할 수 있을 것인지도 생각해 봐야 한다.
데이터베이스는 일관성 있게 유지해야 하는데, application과 스크립트가 데이터베이스의 일관성 있게 작성되었는지 일일히 확인하는 것은 어려운 일이다.

2.3 진퇴양난 업데이트

많은 개발자들은 여러 테이블과 관련된 칼럼을 변경할 때 불편하기 때문에 FK 제약 조건을 꺼린다.
FK제약 조건을 위반하지 않기 위해 자식 행을 먼저 삭제해야 한다.

[예제]
다음과 같이 진행될 것이다.
삭제시는 다음과 같다.


DELETE FROM BugStatus WHERE status = 'BOGUS' ; --에러 
DELETE FROM Bugs WHERE status = 'BOGUS' ;
DELETE FROM BugStatus WHERE status = 'BOGUS' ; --재시도 성공

변경시는 다음과 같다.


UPDATE BugStatus SET status = 'INVALID' WHERE status = 'BOGUS'; --에러
UPDATE Bugs SET status = 'INVALID' WHERE status = 'BOGUS'; --에러

위와 같은 문제로 FK를 사용하려고 하지 않지만, 간단하고 효율적인 방법으로 철히할 수 있다.

3. 안티패턴 인식 방법

키가 없는 안티패턴을 사용하고 있는 경우 다음과 같은 고민을 하게 된다.

  • 어떤 값이 한 테이블에는 있고 다른 테이블에는 없는지 확인하려면 쿼리는 어떻게 작성해야 하는가
  • 테이블에 삽입하면서 다른 테이블에 어떤 값이 있는지를 확인하는 빠른 방법이 없는가
  • FK라고? FK는 데이터베이스를 느리게 만든다고 들었다.

4. 안티패턴 사용이 합당한 경우

다음과 같은 경우는 안티패턴 사용이 합당할 수 있다.

  • DB에서 지원하지 않는 경우
  • 극단적으로 유연한 데이터베이스를 설계하는 경우

5. 해법: 제약 조건 선언하기

[오류검증]
오류를 방지하거나 바로잡거나 또는 발생하는 즉시 관심을 가져 제품의 결함을 제거하는 데 도움이 되는 생산 공적을 뜻함.
품질을 향상시키고 정정 요구를 감소시켜, 추가비용을 상쇄하고도 남는다.

5.1 여러 테이블 변경 지원

FK는 추가적으로 다음과 같은 기능이 존재한다.
[CASCADING UPDATE]
이 기능을 사용하면 부모 행을 update, delete할 때 해당 부모를 참조하는 자식 행을 알아서 처리해준다.

5.2 오버헤드?

FK가 약간의 오버헤드가 있지만, 다음과 같은 효과가 있기 때문에 효율적이다.

  • INSERT, UPDATE, DELETE전에 데이터를 확인하기 위해 SELECT쿼리를 실행할 필요가 없다.
  • 여러 테이블을 변경하기 위해 테이블 잠금을 사용할 필요가 없다.
  • 불가피하게 생기는 고아 데이터를 정정하기 위해 품질 제어 작업을 주기적으로 실행할 필요가 없다.