배경
Spring boot에서 eneity를 db에 table로 저장하려면 Id가 필수로 포함되어야합니다. 이 Id를 통해 검색을 하게 되면, 다른 속성들에 비해 성능이 잘 나온다는 것은 알고 있었죠. 그런데 꼭 Id로만 검색을 해야 성능이 나오려나? 다른 방법은 없을까? 라는 생각을 하며 구글링과 GPT를 이용해보니 DB인덱싱이라는 것을 알게 되었습니다.
내용
DB Index?
index 뭘까요? 사전 정의로 보면 목록입니다. 목차를 보지 않고 두꺼운 책에서 특정 내용을 찾으려면 어떻게 해야될까요? 첫 페이지부터 하나하나 보면서 모든 내용을 보고 찾아야 할 것입니다. 너무 비효율적이죠. 하지만 목차를 이용하면 우리는 빠르게 어디에 내용이 있는지 알고 해당 내용을 볼 수 있습니다. 이러한 개념을 DB에도 적용시켜서 DB에서 index를 만들어서 검색을 빠르게 할 수 있도록 해주는 것이 DB indexing 작업입니다. 이때 index에는 정렬상태를 유지하 수 있는 또다른 공간이 필요해집니다.
DB indexing 작업을 해주게 되면 항상 최신의 정렬 상태를 유지하게 됩니다. 인덱스도 하나의 DB 객체로 인식하기 때문에 DB 크기의 약 10%정도의 저장공간이 필요하게 되죠. 이를 잘 생각해서 인덱싱을 해야하긴 합니다. 속성들중 UK를 가진 모든 것들에 대해서 인덱싱을 하게 된다면, 저장공간이 남아나질 않고 성능이 떨어져버릴겁니다.
Index 알고리즘
index가 되어 있지 않은 값을 이용해서 조회를 하면 어떻게 될까요? 보통 Full Table Scan을 사용해서 모든 테이블을 내용들을 하나씩 보면서 찾게 됩니다. 적용 가능한 인덱스가 없는 경우나 인덱스 처리범위가 넓고 크기가 작은 테이블에서 사용 가능한 방법입니다.
다음으로는 Binary Search Tree(이진 탐색 트리)가 있습니다.
full table scan과 비슷한 성능을 내는 경우도 있기 때문에 큰 메리트가 없어 이를 더 발전시킨 B-Tree 알고리즘이 나오게 됩니다. 이것이 기본 데이터베이스 인덱스 구조가 됩니다.(Mysql 기준)
B-Tree를 이용한 방법과 Full Table Scan을 활용한 방법을 보게 되면, 다음과 같은 차이가 보여집니다.
이처럼 검색 횟수가 줄어들게 되어 빠르게 찾을 수 있습니다. 인덱스 검색의 경우 이렇게 되는 건 이해를 했습니다. 그럼 인덱스의 insert, delete, update의 경우에는 어떻게 될까요?
인덱스의 Insert, Update, Delete(*table에서의 select, insert, update, delete가 아닌 index의 경우입니다.)
인덱스 insert의 경우 페이지에 공간이 없게 되면 페이지를 추가로 생성하게 되면서 공간을 확보하게 됩니다.
이렇게 되면 페이지를 추가로 만들어야하는 공간이 필요하게 되고 성능에 영향을 주게 됩니다. 앞서 말씀드린 index를 하려면 추가 공간이 필요하다는 말이 페이지를 추가로 만들어 정보를 담아두어야 하기 때문이라는 이유도 존재합니다.
delete의 경우 인덱스 데이터를 실제로 지우지 않고 사용안함 표시를 하게 됩니다. 마치 soft delete같은거죠.
update의 경우, 실제 인덱스에는 이런 개념이 없지만 delete를 통해 기존 값을 사용 안함처리 한 후에 insert를 통해 변경된 값을 삽입하게 됩니다.
정리를 해보면 다음과 같습니다.
인덱스의 종류
인덱스의 종류에는 clustering index와 non-clustering index(보조 인덱스, secondary index로도 불림)로 나뉘게 됩니다. 일단 cluster란 군집, 무리를 의미합니다. 이렇게 보게 되면 clustering index은 실제 데이터와 같은 무리의 인덱스, non-clustering index는 실제 데이터와 다른 무리의 인덱스를 나타냅니다. clustering index와 비슷한 것이 사전, non-clustering index와 비슷한 개념이 보통 전공 책들의 마지막에 있는 핵심 단어들로 찾기에 도움을 주는 페이지같은 것을 생각하시면 됩니다.
테이블을 생성하게 되면 PK를 생성하고, 필요하면 Unique 제약을 설정하게 되는데 이는 DB에서 자동적으로 PK - clustering index, Unique - non-clustering index를 생성해줍니다. clustering index 설정 방법은 PK 지정 말고도 unique 제약조건과 not null 제약조건을 동시에 걸게 되면 clustering index로 등록이 됩니다.
clustering index를 활용하게 되면 이를 기준으로 데이터를 정렬합니다. 그리고 위에 말씀드린 B-Tree 알고리즘 방식으로 인덱스를 구성하게 되죠.
이처럼 clustering index는 실제 데이터 자체가 정렬이 되고, 실제 테이블당 1개만 존재가 가능합니다. PK와 unique+not null이 동시에 존재하게되면 PK에 우선순위가 주어지게 됩니다.
non-clustering index를 활용하는 방법에 대해 말씀드릴게요. 먼저 좀 특이한 점이 있습니다. unique 제약조건을 거는 것 말고도 다른 방법이 있는데요,
방법2의 경우 index에 중복을 허용하지 않는 것이고, 방법3의 경우 index에 중복을 허용하게 됩니다. 좀 특이하죠? 중복가능 index라니..
위 테이블에서 name을 기준으로 non-clustering index를 적용하게 되면 다음과 같게 됩니다.
리프 페이지를 보게 되면 앞의 숫자는 실제 저장된 데이터 페이지를 나타내고, #으로 표현된 경우는 그 페이지에서 몇번째에 있는지 보여줍니다.
non-clustering index의 특징으로는 실제 데이터는 그대로사용하며 별도의 인덱스 페이지를 생성하게 됩니다. 이때 추가 공간이 필요하게 되죠. 테이블당 이러한 인덱싱을 여러개 만들게 되면, DB에 저장공간이 남아나질 않게 되어 오히려 성능이 떨어지는 효과를 보이게 됩니다.
그럼 clustering index와 non-clustering index를 동시에 사용하면 어떻게 될까요?
non-clustering index를 하나만 사용했던 방식과는 조금 다르게, non-clustering index에는 clustering index를 활용해 저장된 값을 가지게 됩니다.
지금까지 동작 방식들을 알게 보았는데 그럼 대체 인덱싱을 언제 어떻게 사용해야 될까요? 정답은 카디널리티가 높은 것 즉, 중복 수치가 낮은 것에 활용을 해야합니다.
이 테이블을 보게 되면 id, 이메일과 같은것을 index로 적용하면 되겠죠?
위 결과를 보게 되면 인덱스를 한것과 안한 것의 차이가 어마어마하게 난다는 것을 볼 수 있습니다.
마지막으로 주의사항입니다. 앞서 말씀드렸던 내용들도 다수 있는데, 정리의 개념으로 확인하시면 될 것 같습니다.
긴 글 읽어주셔서 감사합니다. 내일은 db lock에 대해 알아봅시다!
참고 : https://www.youtube.com/watch?v=edpYzFgHbqs
'개발 > DB' 카테고리의 다른 글
트랜잭션 격리수준(Isolation Level) (MySQL) (0) | 2024.11.30 |
---|---|
Lock (1) | 2024.11.29 |