Indexing은 왜 쓰는 걸까? feat. RDBMS
인덱싱은 데이터베이스내 데이터들을 더 빠르게 읽어오기 위한 기능입니다. 데이터베이스에서 Where 조건문을 통해 데이터 조회를 하게 되면, DBMS에서는 모든 레코드를 대상으로 주어진 조건을 비교하면서 조건을 만족하는 레코드들을 찾습니다. 만약 대용량 데이터를 가진 DB라면 당연히 검색 시간이 늘어날 수 밖에 없죠. 이렇게 늘어나는 검색시간을 단축하기 위해 사용하는 기술이 인덱싱입니다. Indexing은 말 그대로 책의 목차와 같은 역할을 합니다. Index할 컬럼의 값들을 일정한 순서(숫자 오름차순 혹은 알파벳 순서)로 저장해 놓고, 검색할 때 이 목록을 참고하여 검색할 필요가 없는 데이터들은 건너뛰고 바로 조건에 맞는 데이터를 찾습니다. 특정 컬럼 또는 컬럼들의 조합에 대한 포인터 목록을 유지 및 관리해서 데이터베이스 엔진이 레코드들을 더 빠르게 찾을 수 있도록 도와줍니다. 보통 B-Tree 형태로 만들어놓습니다. 날아온 쿼리의 조건을 Index에서 찾게 되면 위의 트리구조에서 결과를 찾게 된다. 트리에 이미 순서대로 정렬이 되어있으므로 결과를 훨씬 빠르게 찾을 수 있고 각각의 노드들에 데이터 레코드의 위치를 가리키는 포인터가 저장돼있으므로 아주 빠르게 레코드에 접근할 수 있습니다. 👉 인덱싱을 하는 이유 - 쿼리 성능 개선으로 데이터 검색 속도를 높이기 위해 - 인덱스를 사용하면 전체 테이블을 순차적으로 풀스캔하는 것보다 훨씬 빠르고 효율적으로 데이터를 찾을 수 있습니다. 👉 데이터 검색 속도가 높아지는 이유 인덱싱을 사용하게 되면 데이터 검색 속도가 높아지는데요. 일반적인 경우 B-Tree 자료구조를 인덱싱에 많이 활용합니다. CREATE INDEX idx_user_name ON users (username); 위의 SQL 명령어는 user 테이블의 username 컬럼에 B-Tree 인덱스를 생성합니다. 인덱스가 생성이 되면 username 컬럼을기준으로하는 검색과 정렬작업을 더 빠르게 수행할 수 있게 해줍니다. B-Tree 인덱스는 균형 이진 트리 구조인데요. 각 노드는 여러 키를 가질 수 있으며, 키들은 정렬된 상태로 유지됩니다. 위에서 username 컬럼을 기준으로 검색을 하게 되면, B-Tree는 루트 노드에서 시작하여 트리를 내려가며 찾고자하는 키 값을 가진 노드를 찾습니다. 이 과정은 O(logn) 복잡도를 갖습니다. 인덱스 키 값들이 정렬이 되어있기 때문에 범위 검색이나 순차 검색에 효율적일 수 밖에 없죠. 일일히 전체를 다 뒤지는 풀스캔과는 속도면에서 차이가 날 수 밖에 없습니다. 예시로 좀 더 자세히 알아보겠습니다. users 테이블에 username 컬럼이 있고, 인덱스가 생성된 상태라고 가정합니다. SELECT * FROM users WHERE username = 'alice'; 가장 먼저, alice라는 username을 가진 레코드를 찾기 위해 쿼리를 실행하게 됩니다. 데이터베이스 엔진은 username에 대한 인덱스를 찾고, 이 인덱스의 B-Tree 구조에서 alice를 검색합니다. 그 다음에는 트리 안에서 탐색이 이루어지죠. B-Tree 구조를 통해 하위 노드로 이동하면서 alice값이 있는 노드를 효율적으로 탐색합니다. 인덱스에 alice가 위치하는 노드를 찾으면, 그 노드는 실제 테이블에서 해당 레코드의 위치를 가리키는 포인터값을 가지고 있습니다. 데이터베이스는 이 포인터를 활용해서 실제 레코드를 빠르게 찾아낼 수 있는 것이죠. 이런 방식으로 동작하기 때문에 풀스캔보다 인덱싱을 활용한 검색이 빠를 수 밖에 없는 것입니다. B-Tree 구조 덕분에 데이터베이스가 전체 테이블을 순차적으로 검색할 필요가 없습니다. B-Tree 구조 안에서 이미 인덱스 키값들이 정렬이 되어있어 범위 검색이나 순차 검색이 훨씬 효율적이죠. 그리고 노드에 실제 레코드의 포인터 값이 포함되어있기 때문에 데이터베이스에서 해당 레코드에 바로 접근이 가능합니다. 👉 인덱싱 사용시 주의사항 인덱싱을 사용하게 되면 쓰기 연산에 영향을 미칠 수 있습니다. 인덱스가 생성되어 있는 경우, 쓰기 연산(INSERT, Update, Delete)을 수행할 때 인덱스를 업데이트 해줘야 합니다. 이는 추가 오버헤드가 발생할 수 있기 때문에 자주 업데이트가 될 가능성이 있는 경우 인덱성 설정에 주의해야 합니다. 👉 인덱싱 조합의 효과적인 기준 인덱싱을 올바르게 설정하는 것은 정말 중요합니다. 데이터베이스 연산 성능에 아주 크리티컬한 영향을 미치기 때문이죠. 그럼 어떤 컬럼 혹은 컬럼 조합에 인덱싱을 설정해야 할까요? 1. 자주 사용되는 검색조건 1. 쿼리 중에서 가장 자주 사용되는 조건의 컬럼이 인덱스 적용하기 적합합니다. 사용자의 이름이나 이메일로 자주 검색을 하는 경우 해당 컬럼에 인덱스를 적용하는게 좋습니다. 2. 조인 조건에 사용되는 컬럼 1. 여러 테이블을 조인할 때 사용되는 컬럼들에 인덱스를 적용하면 조인 성능을 향상시킬 수 있습니다. 3. 정렬 및 그룹화에 사용되는 컬럼 1. Order by나 Group By 쿼리에서 자주 사용되는 컬럼에 인덱스를 적용하면 데이터 정렬 및 그룹화 성능을 향상시킬 수 있습니다 4. Unique한 컬럼 1. 중복값이 없거나 적은 컬럼일수록 인덱스 효율이 더 좋습니다. 5. 균일한 데이터 분포값을 가진 컬럼 1. 값의 분포가 균일한 컬럼에 인덱스를 적용하는게 좋습니다. 예를 들어, active, inactive 값을 가지는 컬럼에 인덱스를 적용하는 것은 효율적이지 않습니다. 6. 컬럼의 데이터 크기 1. 긴 문자열이나 BLOB에 인덱스를 적용하는 것보다 정수나 날짜같이 작은 크기의 데이터에 인덱스를 적용하는 것이 더 효율적입니다. 7. 쿼리 패턴 분석 1. 실제 쿼리 로그를 분석하여 가장 자주 실행되고 비용이 높은 쿼리를 찾아내서 그 쿼리를 구성하는 컬럼에 인덱스를 적용하는 것이 효율적입니다. 👉 인덱싱 종류와 용도 - Standard Index - 가장 기본적인 형태의 인덱스입니다. 특정 컬럼의 데이터를 빠르게 가져올 수 있게합니다. - Unique Index - Primary Key는 DB 엔진에서 테이블을 생성할 때 자동으로 인덱스를 생성해줍니다. 레코드별 고유한 키값을 가지고 있어 빠르게 레코드를 식별해서 가져올 수 있습니다. Primary Key가 Unique Index에 해당됩니다. 그 외 나머지 인덱스를 통틀어 Secondary Index라고 부릅니다. - Composite Index - 두 개 이상의 컬럼을 조합해서 생성한 인덱스입니다. 여러 열을 동시에 검색조건으로 활용할 때 유용합니다. - Foreign Key Index - 외래키 조건이 걸려있는 컬럼에 사용합니다. 설정하면 조인 성능이 향상되고, 참조 무결성을 유지하는데 도움이 됩니다. - Full-text Index - 텍스트 기반의 콘텐츠를 효율적으로 검색하기 위한 인덱스입니다. 대용량 텍스트 필드 검색을 최적화합니다.