유니크란 사실 인덱스라기보다는 제약 조건에 가깝다고 볼 수 있다. 말 그대로 테이블이나 인덱스에 같은 값이 2개 이상 저장될 수 없음을 의미하는데, MySQL에서는 인덱스 없이 유니크 제약만 설정할 방법이 없다. 유니크 인덱스에서도 NULL도 저장될 수 있는데, NULL은 특정의 값이 아니므로 2개 이상 저장될 수 있다. MySQL에서 프라이머리 키는 기본적으로 NULL을 허용하지 않는 유니크 속성이 자동으로 부여된다. MyISAM이나 MEMORY 테이블에서 프라이머리 키는 사실 NULL이 허용되지 않는 유니크 인덱스와 같지만 InnoDB 테이블의 프라이머리 키는 클러스터 키의 역할도 하므로 유니크 인덱스와는 근본적으로 다르다.
유니크 인덱스와 유니크하지 않은 일반 보조 인덱스는 사실 인덱스의 구조상 아무런 차이점이 없다. 유니크 인덱스와 일반 보조 인덱스의 읽기와 쓰기를 성능 관점에서 한번 살펴보자.
인덱스 읽기
많은 사람이 유니크 인덱스가 빠르다고 생각한다. 하지만 이것은 사실이 아니다. 어떤 책에서는 유니크 인덱스는 1건만 읽으면 되지만 유니크하지 않은 일반 보조 인덱스에서는 한 번 더 읽어야 하므로 느리다고 이야기한다. 하지만 유니크하지 않은 보조 인덱스에서 한 번 더 해야 하는 작업은 디스크 읽기가 아니라 CPU에서 칼럼값을 비교하는 작업이기 때문에 이는 성능상의 영향이 거의 없다고 볼 수 있다. 유니크하지 않은 보조 인덱스는 중복된 값이 허용되므로 읽어야할 레코드가 많아서 느린 것이지, 인덱스 자체의 특성 때문에 느린 것이 아니라는 것이다. 즉, 레코드 1건을 읽는데 0.1초가 걸렸고 2건을 읽을 때 0.2초가 걸렸다고 했을 때 후자를 느리게 처리됐다고 할 수 없는 것과 같은 이치다.
하나의 값을 검색하는 경우, 유니크 인덱스와 일반 보조 인덱스는 사용되는 실행 계획이 다르다. 하지만 이는 인덱스의 성격이 유니크한지 아닌지에 따른 차이일 뿐 큰 차이는 없다. 1개의 레코드를 읽느냐 2개 이상의 레코드를 읽느냐의 차이만 있다는 것을 의미할 뿐, 읽어야 할 레코드 건수가 같다면 성능상의 차이는 미미하다.
인덱스 쓰기
새로운 레코드가 INSERT되거나 인덱스 칼럼의 값이 변경되는 경우에는 인덱스 쓰기 작업이 필요하다. 그런데 유니크 인덱스의 키값을 쓸 때는 중복된 값이 있는지 없는지 체크하는 과정이 한 단계 더 필요하다. 그래서 일반 보조 인덱스의 쓰기보다 느리다. 그런데 MySQL에서는 유니크 인덱스에서 중복된 값을 체크할 때는 읽기 잠금을 사용하고, 쓰기를 할 때는 쓰기 잠금을 사용하는데 이 과정에서 데드락이 아주 빈번히 발생한다.
InnoDB 스토리지 엔진에서는 인덱스 키의 저장을 버퍼링하기 위해 인서트 버퍼(Insert Buffer)가 사용된다. 그래서 인덱스의 저장이나 변경 작업이 상당히 빨리 처리되지만 안타깝게도 유니크 인덱스는 반드시 중복 체크를 해야 하므로 작업 자체를 버퍼링하지 못한다. 이 때문에 유니크 인덱스는 일반 보조 인덱스보다 더 느려진다.
꼭 필요한 경우라면 유니크 인덱스를 생성하는 것은 당연하다. 하지만 더 성능이 좋아질 것으로 생각하고 불필요하게 유니크 인덱스를 생성하지는 않는 편이 좋다. 그리고 하나의 테이블은 같은 칼럼에 유니크 인덱스와 일반 인덱스를 각각 중복해서 생성해 둔 경우가 가끔 있는데, MySQL의 유니크 인덱스는 일반 다른 인덱스와 같은 역할을 하므로 중복해서 인덱스를 생성할 필요는 없다. 즉, 다음과 같은 테이블에서 이미 nick_name이라는 칼럼에 대해서는 유니크 인덱스인 "ux_nickname"이 있기 때문에 ix_nickname 인덱스는 필요하지 않다. 이미 유니크 인덱스도 일반 보조 인덱스와 같은 역할을 동일하게 수행할 수 있으므로 다음과 같이 중복해서 보조 인덱스를 만들어 줄 필요는 없다.
CREATE TABLE tb_unique (
id INTEGER NOT NULL,
nick_name VARCHAR(100),
PRIMARY KEY (id),
UNIQUE INDEX ux_nickname (nick_name),
INDEX ix_nickname (nick_name)
);
그리고 가끔, 똑같은 칼럼에 대해 프라이머리 키와 유니크 인덱스를 동일하게 생성한 테이블도 있는데, 이 또한 불필요한 중복이므로 주의하자. 이 밖에도 유니크 인덱스는 쿼리의 실행 계획이나 테이블의 파티셔닝에 미치는 영향이 있다.
결론적으로 유일성이 꼭 보장돼야 하는 칼럼에 대해서는 유니크 인덱스를 생성하되, 꼭 필요하지 않다면 유니크 인덱스보다는 유니크하지 않은 보조 인덱스를 생성하는 방법도 한 번씩 고려해보자.
참고