그리고 한 명의 유저가 동일한 게시글을 신고할 수 없도록 (포스트 id, 신고자 id) 로 multipe unique key 제약조건을 걸어놓은 상태입니다.
유니크 키를 걸게 되면, 해당 제약조건의 빠른 검증을 위해 index가 함께 생성됩니다. 현재 'idx01_unique_report_post' 이라는 이름으로 인덱스가 걸려있습니다.
처음에는 일단 유저가 신고한 게시글인지 빠르게 탐색하기 위해서 `user_id` 에 인덱스를 걸었었습니다.
그런데 실제로 실행계획을 돌려보니..
제가 이번에 추가한 `idx02_report_post` 를 후보로만 올려놓고, 실제로는 기존에 걸려있던 `idx01_unique_report_post` 키를 활용하더군요.
[아! 커버링 인덱스..]
어! 왜 이렇지!!! 라고 샤워를 하며 30분동안 생각에 잠기게 됩니다.
어! 왜 이렇지!!! 라고 샤워를 하며 곰곰히 생각을 했는데, 생각해보니 기존 unique index table에 `(user_id, post_id)`가 있다는 것이 떠올라서, 샤워를 끝내고 실제로 호다닥 확인해보니 `Extra: Using index` 가 적혀있더군요 ㅎㅎ
왜 Extra 컬럼을 볼 생각을 안했을까
Extra의 Using index는 커버링 인덱스를 통해 결과를 가져왔다는 것을 의미합니다.
인덱스에 모든 값이 올라가있어서, 실제로는 disk I/O가 일어나지 않는 커버링 인덱스로 실행된 것 입니다.
[실제 내부 인덱스 테이블 구조]
실제 내부의 인덱스 테이블은 다음과 같이 생겼을 것 입니다.
MySQL 같은 경우에는 인덱스 테이블이 PK 값을 가지고 있어서, 다음과 같이 표현이 되는데
제가 위에서 실행한 쿼리에서 필요한 (post_id, user_id)는 인덱스 테이블 안에 모두 포함되어 있음을 알 수 있습니다.
그렇다면 굳이 느린 Disk I/O 를 하면서 테이블을 직접 읽을 필요가 없겠죠?
그런데 사실 다시 생각해보면, 만약 커버링 인덱스가 아니었더라도 `(user_id, post_id)` 복합키에서 user_id가 선행 컬럼이라서, 단독 index를 거는 것과 차이가 없었을 겁니다. ㅎㅎㅎ
왜냐. 인덱스 테이블 순서를 보시면, 선행 컬럼인 post_id 이후에 user_id가 정렬되는 방식입니다.
그렇다면 post_id에 따로 인덱스를 걸더라도, 동일한 데이터 순서로 탐색을 하게 됩니다.