SQL 인덱스 및 파티션으로 병목 현상 해결
게시 됨: 2022-03-11SQL Indexes Explained의 첫 번째 수업에서 데이터가 이미 특정 열의 값으로 정렬되어 있을 때 SELECT 쿼리가 더 빠르다는 것을 배웠습니다.
두 번째 수업에서는 B-tree 인덱스의 기본 구조와 쿼리를 실행하는 동안 액세스하는 데이터의 양을 줄이는 데 사용하는 방법을 배웠습니다. 또한 여러 테이블을 조인하는 쿼리를 구현하는 방법과 인덱스가 이러한 쿼리의 속도를 높일 수 있는 방법도 알아냈습니다.
또한 SQL에서 인덱스를 사용하는 것이 도움이 되는 두 가지 시나리오를 강조했습니다. 인덱스가 WHERE 조건, JOIN 조건 및 SELECT 목록에서 쿼리의 모든 열을 포함하는 인덱스를 덮을 때 해당 테이블을 완전히 읽는 것을 피합니다. 또는 인덱스는 테이블 크기의 작은 부분에 액세스되는 데이터 블록의 수를 줄일 때 도움이 될 수 있습니다.
그렇지 않으면 인덱스에서 읽고 해당 테이블 행으로 무작위로 앞뒤로 이동하는 것보다 전체 테이블을 스캔하는 것이 더 효율적입니다.
SQL 범위 쿼리
인덱스를 활용할 수 있는 쿼리에는 일반적으로 하나 이상의 열이 취할 수 있는 가능한 값의 범위를 크게 줄이는 조건이 포함됩니다. 범위 쿼리는 "A 열의 값은 X와 Y 사이에 있어야 함"과 같은 조건에 따라 데이터를 제한합니다.
이에 대한 좋은 예는 두 번째 단원의 연습 4에 있는 쿼리입니다.
SELECT c.ClientName FROM Reservations r JOIN Clients c ON r.ClientID = c.ClientID WHERE r.DateFrom BETWEEN ( TO_DATE('2020-08-13', 'YYYY-MM-DD') AND TO_DATE('2020-08-14', 'YYYY-MM-DD') ) AND r.HotelID = 3; 여기에 두 가지 범위가 있습니다. 첫 번째는 2020년 8월 13일에서 2020년 8월 14일 사이의 날짜 범위입니다. 두 번째는 가능한 가장 작은 숫자 범위입니다. 조건은 r.HotelID BETWEEN 3 AND 3 같습니다.
연습 1: 기간(날짜 및 시간 범위 쿼리)
Reservations 테이블에 CheckInTime 이라는 열을 추가해 보겠습니다. 이 스프레드시트에서 샘플 데이터를 볼 수 있습니다. CheckInTime 과 ClientId 를 모두 포함하는 단일 인덱스가 있습니다.
2020년 8월 15일에 체크인한 클라이언트의 이름을 반환하는 쿼리를 작성하십시오.
경험이 부족한 SQL 개발자는 일반적으로 다음 쿼리를 작성합니다.
SELECT c.ClientName FROM Reservations r JOIN Clients c ON r.ClientID = c.ClientID WHERE TO_DATE(r.CheckInTime, 'YYYY-MM-DD') = '2020-08-15';그들은 쿼리 실행이 다음과 같을 것이라고 가정합니다.
Get first row from IX_CheckInTime_ClientID where TO_DATE(CheckInTime, 'YYYY-MM-DD') = '2020-08-15' While found and TO_DATE(CheckInTime, 'YYYY-MM-DD') = '2020-08-15' Fetch Clients.* where ClientID = IX_CheckInTime_ClientID.ClientID Write down Clients.ClientName Get next row from IX_CheckInTime_ClientID 문제는 이 글을 쓰는 시점에서 단일 RDBMS가 그러한 실행 계획을 생성할 수 없다는 것입니다. 그들은 TO_DATE (오라클 구문)를 CheckInTime 열의 값을 인덱싱되지 않은 값으로 변환하는 함수로 봅니다. 따라서 생성하는 실행 계획은 다음과 같습니다.
For each row from IX_CheckInTime_ClientID If TO_DATE(CheckInTime, 'YYYY-MM-DD') = '2020-08-15' then Fetch Clients.* where ClientID = IX_CheckInTime_ClientID.ClientID Write down Clients.ClientName 인덱스 행이 테이블 행보다 좁기 때문에 이것을 실행하면 Reservations 테이블에서 모든 행을 읽는 것보다 빠릅니다. 더 작은 행은 디스크에서 더 적은 수의 블록에 액세스해야 함을 의미합니다.
그러나 첫 번째 실행 계획이 훨씬 더 효율적이라는 것을 알고 있습니다. RDBMS가 그 접근 방식을 사용하도록 설득하려면 쿼리를 다시 작성해야 합니다.
SELECT c.ClientName FROM Reservations r JOIN Clients c ON r.ClientID = c.ClientID WHERE r.CheckInTime >= TO_DATE('2020-08-15 00:00:00', 'YYYY-MM-DD HH:MI:SS') AND r.CheckInTime < TO_DATE('2020-08-16 00:00:00', 'YYYY-MM-DD HH:MI:SS'); 이것은 모든 좋은 RDBMS가 이해하는 적절한 범위 쿼리입니다. RDBMS는 CheckInTime에서 파생된 것이 아닌 CheckInTime 의 값이 잘 정의된 범위에 속하는 Reservations 테이블의 데이터를 원한다는 것을 파악합니다. 생성하는 실행 계획은 다음과 같습니다.
Get first row from IX_CheckInTime_ClientID where CheckInTime >= '2020-08-15 00:00:00' While found and CheckInTime < '2020-08-16 00:00:00' Fetch Clients.* where ClientID = IX_CheckInTime_ClientID.ClientID Write down Clients.ClientName Get next row from IX_CheckInTime_ClientID이것이 우리가 진정으로 원하는 것입니다. 인덱스 자체뿐만 아니라 정렬되어 있다는 사실도 활용하는 것입니다.
연습 2: 시작 부분에 와일드카드가 있는 LIKE
이번에는 우리 형사가 용의자에 대한 모호한 정보를 가지고 호텔에 옵니다. 성은 "-son"으로 끝나는 것뿐입니다. 형사는 다음과 같은 모든 손님의 이름과 성을 원합니다.
SELECT FirstName, LastName FROM Clients WHERE LastName LIKE '%son'; Clients 테이블과 LastName 의 인덱스에 대해 이 스프레드시트를 사용합니다. 쿼리가 반환할 결과를 기록합니다. 적용할 수 있는 다양한 접근 방식에 대해 생각해 보십시오.
테이블 스캔 접근 방식
가장 간단한 전략은 테이블에서 모든 데이터를 읽고 성이 "-son"으로 끝날 때 게스트의 이름을 기록하는 것입니다.
For each row from Clients If LastName like '%son' then write down FirstName, LastName여기서는 전체 테이블을 순차적으로 읽어야 합니다.
인덱스 사용
LastName 열의 인덱스를 활용해 보겠습니다. IX_LastName 시트로 이동하여 주어진 기준을 충족하는 모든 클라이언트를 찾고 이름을 기록하는 데 사용합니다.
테이블에서 Anderson, Robinson 및 Thompson을 모두 찾으려면 전체 인덱스를 읽어야 합니다. 테이블 스캔을 사용하는 것보다 낫습니까? 전체 색인을 읽는 것 외에도 모든 일치 항목에 대해 rowAddress 값을 사용하여 테이블에서 해당 행을 찾은 다음 거기에서 FirstName 을 기록해야 했습니다.
For each row from IX_LastName If LastName like '%son' then Fetch Clients.* where RowAddress = IX_LastName.RowAddress Write down FirstName, LastName우리에게는 테이블을 순차적으로 읽는 것이 더 간단하고 빨랐습니다. RDBMS의 경우 기준을 충족하는 행의 백분율에 따라 달라집니다. 큰 테이블에 소수의 Anderson, Robinson 및 Thompson이 있는 경우 RDBMS는 일치 항목이 발견될 때 테이블에서 몇 블록을 읽어야 하는 경우에도 훨씬 더 좁은 인덱스 항목에서 더 적은 데이터 블록을 읽습니다. 그렇지 않으면 테이블 스캔에 시간이 덜 걸립니다.
인덱스에서 데이터를 정렬하는 것은 그러한 쿼리에 도움이 되지 않습니다. 인덱스 행의 크기가 작을수록 도움이 될 수 있지만 가끔만 해당됩니다.
연습 3: 끝에 와일드카드가 있는 LIKE
다음에 탐정이 오면 성이 "Rob-"로 시작하는 모든 고객을 찾아야 합니다.
SELECT FirstName, LastName FROM Clients WHERE LastName LIKE 'Rob%';동일한 스프레드시트에서 쿼리와 일치하는 데이터를 추출해 보십시오.
테이블 스캔 접근 방식을 사용했다면 인덱스 IX_LastName 을 최대한 활용할 수 있는 기회를 놓친 것입니다. 색인에서 "Rob-"(Roberts)로 시작하는 첫 번째 항목을 찾고 후속 행(Robertses 및 Robinsons 모두)을 읽고 LastName 이 더 이상 기준과 일치하지 않을 때 중지하는 것이 훨씬 빠릅니다.
Get first row from IX_LastName where LastName <= 'Rob' While found and LastName < 'Roc' Fetch Clients.* where rowAddress = IX_LastName.rowAddress Write down FirstName, LastName Get next from IX_LastName이 경우 첫 번째 항목에 대한 B-트리 조회 후 기준을 충족하는 항목만 읽습니다. 기준과 일치하지 않는 이름을 읽는 즉시 읽기를 중단합니다.
B-트리 확장 문제 해결
일반적으로 새 데이터베이스를 배포할 때 채워진 조회 테이블과 빈 트랜잭션 테이블이 몇 개 있습니다. 시스템은 처음부터 원활하게 실행됩니다. 특히 테이블을 정규화하여 데이터베이스 설계의 모범 사례를 존중한다면 더욱 그렇습니다. 기본, 외래 및 고유 키 생성 및 해당 인덱스가 있는 외래 키를 지원합니다.
몇 개월 또는 몇 년 후 데이터 볼륨이 시스템 및 데이터베이스 복잡성을 크게 증가시키면 성능 저하가 감지되기 시작합니다. 시스템이 느려지는 이유와 이에 대해 어떻게 해야 하는지에 대한 의견이 있습니다.
대중적인 의견은 종종 데이터베이스의 크기가 주요 원인이라는 것입니다. 솔루션은 매일 필요하지 않은 과거 데이터를 제거하고 보고 및 분석을 위해 별도의 데이터베이스에 저장하는 것 같습니다.
먼저 주요 가정을 살펴보겠습니다.
SQL 범위 쿼리: 실행 시간이 테이블 크기에 따라 달라지나요?
단일 테이블의 일반적인 범위 쿼리를 고려하십시오.
SELECT Column1, …, ColumnN FROM Table WHERE Column BETWEEN X AND Y; Column 에 인덱스가 있다고 가정하면 최적의 실행 계획은 다음과 같습니다.
Get first row from IX_Column where Column between X and Y While found and Column <= Y Fetch Table.* where rowAddress = IX_Column.rowAddress Write down Column1, …, ColumnN Get next row from IX_ColumnRDBMS가 이 데이터를 반환하기 위해 읽어야 할 블록을 계산해 보겠습니다.
Get first row 부분은 두 번째 단원에서 소개한 B-tree 조회에 의해 구현됩니다. 읽어야 하는 블록의 수는 B-트리의 깊이와 같습니다. 그런 다음 인덱스의 리프 수준에서 후속 항목을 읽습니다.
OLTP 쿼리를 사용하면 일반적으로 모든 결과가 하나의 인덱스 블록 내에서 발견됩니다(때로는 2개, 드물게 더 많이). 그 외에도 각 인덱스 항목에 대해 테이블의 블록에 액세스하여 주소를 기반으로 해당 행을 찾을 수 있습니다. 일부 테이블 행은 이미 로드한 동일한 테이블 블록 내에 있을 수 있지만 추정을 단순화하기 위해 매번 새 블록을 로드한다고 가정하겠습니다.
따라서 공식은 다음과 같습니다.
B = D + 1 + R
B는 읽은 총 블록 수, D는 B-트리 깊이, R은 쿼리에서 반환된 행 수입니다.

테이블의 행 수에 의존하는 유일한 매개변수는 B-트리 깊이인 D입니다.
계산을 단순화하고 요점을 만들기 위해 1,000개의 인덱스 항목이 하나의 블록에 맞는다고 가정합니다. 테이블에 1,000개 미만의 행이 있는 한 D = 1입니다. 비즈니스 트랜잭션을 보유하는 테이블의 경우 시스템 배포 후 첫 영업일의 경우일 수 있습니다. 곧 B-트리 깊이가 증가합니다. 테이블에 100만 개 미만의 행이 있는 한 인덱스는 두 가지 수준으로 구성됩니다.
느린 데이터베이스 응답 시간이 문제가 되고 이에 대한 데이터 볼륨의 책임이 있는 경우 트랜잭션 테이블에는 종종 수백만 개의 행이 있다는 점에 유의하십시오. 100만 행만 2단계 B-트리 인덱스에 적합하므로 깊이는 최소한 3이어야 합니다. 테이블에 10억 개 이상의 행이 없으면 깊이가 4로 증가하지 않습니다. 이제 더 정확한 추정치가 있습니다.
B = 4 + R
R이 작은 경우 B-트리 깊이를 다시 2로 낮추면 쿼리 속도가 크게 빨라집니다. 기본 또는 고유 키 값으로 검색할 때 시스템은 5개가 아닌 4개의 블록을 읽습니다. 이는 20% 개선된 것입니다. 쿼리가 더 많은 행을 반환하는 경우 개선 사항이 눈에 띄지 않을 수 있습니다. 문제는 많은 응용 프로그램의 경우 데이터베이스에 100만 개 미만의 트랜잭션을 유지하여 필수 비즈니스 운영을 지원하지 못할 수 있다는 것입니다.
따라서 결론은 테이블 크기는 중요하지 않다는 것입니다. 즉, 과거 데이터를 이동하는 것은 시간과 리소스를 낭비하는 것입니다.
그러나 그렇게 빠르지는 않습니다. B-트리 인덱스의 구조와 데이터 변경이 이에 미치는 영향에 대해 자세히 알아보겠습니다.
B-트리 인덱스 구현 세부 정보
두 번째 학습에서 B-트리 인덱스에 대한 내용에서 균형 트리의 모든 수준이 키 열 값에 따라 (물리적으로) 정렬되어 있음을 확인했습니다. 그러나 항목을 삽입, 업데이트 또는 삭제하려는 경우 순서를 유지하기 위해 많은 양의 데이터를 이동해야 하는 경우가 많습니다.
가득 찬 블록의 중간에 삽입한다고 가정해 보겠습니다. 블록을 분할하고 데이터를 재정렬해야 하며 때로는 현재를 가리키는 다른 B-트리 수준의 데이터를 업데이트해야 합니다.
이러한 경우를 보다 효율적으로 만들기 위해 각 인덱스 항목에는 이전 행과 다음 행에 대한 포인터가 포함되어 이중 연결됩니다. 일반적으로 삽입의 경우 이는 새 항목을 이전 항목에 최대한 가깝게 작성하고 포인터를 수정함을 의미합니다.
블록도 분할해야 하는 경우 이전 B-트리 수준에서 새 항목을 작성해야 합니다. 이것은 단지 몇 개의 포인터를 더 수정하는 문제이며 트리의 많은 부분을 다시 작성할 필요가 없습니다. 분할 후 데이터의 두 블록은 거의 절반이 채워집니다. 디스크에 여유 공간이 있는 위치에 따라 "인접한" 블록이 물리적으로 상당히 멀리 떨어져 있을 수 있습니다.
일정 시간이 지나면 인덱스 조각화가 증가하고 쿼리 실행 속도가 눈에 띄게 느려집니다. 우리가 설명한 방식으로 쿼리를 실행하는 RDBMS를 사용하면 항목의 순서와 근접성에 대한 가정이 점점 정확하지 않게 되어 훨씬 더 많은 읽기가 수행됩니다. 최악의 경우 모든 데이터 블록이 반쯤 비어 있는 상태에서 시스템은 두 배의 블록을 읽어야 합니다.
B-트리 인덱스 유지 관리
이에 대한 치료법은 인덱스 조각 모음(또는 "재인덱싱")입니다. 모든 RDBMS는 전체 인덱스를 다시 생성하는 기능을 제공합니다. 재인덱싱 후 인덱스는 다시 한 번 물리적으로 정렬됩니다.
재인덱싱은 많은 양의 데이터를 읽고 쓰지만 매우 빠른 작업입니다. 최신 RDBMS는 일반적으로 두 가지 재인덱싱 모드를 제공하며, 더 빠른 모드에서는 처리 중에 테이블을 잠가야 합니다. 어느 쪽이든 사용량이 적은 시간에 다시 색인을 생성하는 것이 좋습니다. 그렇지 않으면 처리로 인해 데이터베이스 성능이 느려질 수 있습니다.
과거 데이터 삭제
수십억 또는 수억 개의 행이 있는 테이블이 있는 경우 사용량이 적은 시간에 재인덱싱 작업을 완료하는 것이 불가능할 수 있습니다.
이러한 상황을 피하려면 OLTP 데이터베이스에서 기록 데이터를 이동하는 것이 솔루션이 될 수 있습니다. 그러나 특정 임계값보다 오래된 행을 단순히 삭제하면 인덱스가 훨씬 더 조각화되어 더 자주 인덱스를 다시 지정해야 합니다.
구조를 위한 SQL 파티셔닝?
생산 데이터베이스에 "활성" 트랜잭션만 유지하고 기록 데이터를 제거하여 발생하는 단편화를 방지하는 방법이 있습니다. 모든 주요 RDBMS가 구현하는 아이디어는 테이블을 더 작은 청크로 분할하고( 파티션 이라고 함) 테이블을 추가, 제거, 심지어 테이블 간에 전환하는 기능을 제공하는 것입니다. 구조).
이 스프레드시트에서 분할된 Reservations 테이블을 살펴보겠습니다. 테이블은 날짜 기간 및 기타 스프레드시트에 매핑된 파티션 이름으로 월별로 분할됩니다. 분할된 테이블에 대한 쿼리가 실행되는 방법을 확인하기 위해 몇 가지 연습을 수행합니다.
연습 4: SQL의 파티션 쿼리
위에 링크된 스프레드시트에서 인덱스를 사용하지 않고 다음 쿼리에서 요청한 데이터를 추출해 보십시오.
SELECT HotelID, ReservationID, ClientID, DateFrom, DateTo FROM Reservations WHERE DateFrom BETWEEN TO_DATE('2021-03-01','YYYY-MM-DD') AND TO_DATE('2021-03-03');먼저 파티션 매핑 시트를 보고 2021년 3월부터 예약이 포함된 파티션을 찾아야 한다는 것을 알았을 것입니다. 그런 다음 해당 파티션을 열고 데이터를 순차적으로 읽고 만족하지 않는 행을 필터링했습니다. 상태.
간단하지만 많은 행을 읽은 후 적은 수의 행을 유지하는 것을 좋아하지 않았을 것입니다. 3월 파티션을 읽는 것이 전체 예약 테이블을 읽는 것보다 낫지만 여전히 이상적이지는 않습니다. 인덱스는 어떻습니까?
글로벌 인덱스
RDBMS를 사용하면 파티션된 테이블의 모든 파티션을 포함하는 글로벌 인덱스 를 생성할 수 있습니다. 그러나 글로벌 인덱스와 일반 인덱스의 작동 방식에는 차이가 없습니다. 글로벌 인덱스는 파티션을 인식하지 않습니다. 따라서 글로벌 인덱스를 사용하는 CRUD 쿼리는 해당 테이블에 대한 파티션 맵을 포함하지 않습니다.
전체 파티션을 삭제할 때만 파티션 맵을 업데이트하면 됩니다. 그런 다음 제거된 파티션을 가리키는 모든 행을 인덱스에서 제거해야 합니다. 즉, 전체 글로벌 인덱스를 다시 작성해야 합니다.
사용되지 않는 항목이 제거될 때까지 인덱스를 사용할 수 없으므로 중단 기간이 필요합니다. 파티션을 정기적으로 삭제하여 활성 파티션 수를 제한할 수 있다면 재인덱싱 작업이 중단 기간에 맞을 수 있습니다. 따라서 파티션을 사용하면 글로벌 인덱스 유지 관리를 포함하여 유지 관리 작업에 필요한 시간을 단축하여 원래 문제를 해결하는 데 도움이 됩니다.
그러나 여전히 정전을 감당할 수 없는 경우에는 어떻게 합니까?
전역으로 분할된 인덱스
이 전략은 해당 문제를 해결합니다. 테이블을 분할하는 것과 같은 방식으로 인덱스를 분할하기만 하면 됩니다. 파티션 스프레드시트가 연결되는 스프레드시트에서 각 파티션은 Reservations 테이블의 해당 부분과 IX_DateFrom이라는 인덱스 시트를 포함하며 둘 다 DateFrom 으로 분할됩니다.
연습 4의 쿼리를 실행하기 위해 RDBMS는 먼저 인덱스 파티션 맵을 보고 범위의 날짜가 포함된 파티션을 식별합니다. (우리의 경우 인덱스 파티션이 하나만 있습니다.) 그 후 B-트리 조회를 사용하고 리프 수준으로 순환한 다음 마지막으로 해당 행 주소를 사용하여 테이블에 액세스합니다.
테이블에서 파티션을 삭제할 때 인덱스에서 해당 파티션을 삭제하는 것으로 충분합니다. 다운타임이 필요하지 않습니다.
로컬 인덱스
전역 분할 인덱스의 주요 단점은 테이블과 해당 인덱스 파티션을 모두 삭제해야 한다는 것입니다. 인덱스 파티션 맵 자체를 읽고 유지하는 것과 관련된 약간의 추가 비용만 있습니다.
로컬 인덱스는 비슷하지만 약간 다른 접근 방식을 사용합니다. 단일 글로벌 인덱스를 분할하는 대신 각 테이블 파티션 내부 에 로컬 인덱스를 생성합니다. 그렇게 함으로써 로컬 인덱스는 전역적으로 분할된 인덱스의 주요 이점(즉, 가동 중지 시간이 없음)을 공유하면서 단점을 피할 수 있습니다.
완벽한 솔루션인 것 같습니다. 그러나 축하하기 전에 몇 가지 쿼리의 가능한 실행 계획을 조사해 보겠습니다.
연습 5: 로컬로 분할된 인덱스
쿼리를 다시 실행해 보십시오. 이번에는 DateFrom 에서 로컬로 분할된 인덱스를 사용합니다.
아마도 다음 실행 계획을 사용했을 것입니다.
For all partitions where [StartDateFrom, StartDateTo) intersects ['2021-03-01', '2021-03-03'] Get first row from IX_DateFrom where DateFrom between '2021-03-01' and '2021-03-03' While found and DateFrom < '2021-03-04' Fetch Reservations.* where RowAddress = IX_DateFrom.RowAddress Write down HotelID, ReservationID, ClientID, DateFrom, DateTo Get next row from IX_DateFrom모든 날짜가 단일 파티션에 속해 있어서 운이 좋았기 때문에 하나의 로컬 인덱스만 탐색해야 했습니다. 기간이 6개월이면 6개의 로컬 인덱스를 읽어야 합니다.
연습 6: 대조적으로
귀하의 작업은 예약 파티션 맵을 다시 사용하는 것입니다. 이번에는 클라이언트 124가 호텔 1을 방문한 기간 목록을 작성하는 것입니다.
SELECT DateFrom, DateTo FROM Reservations WHERE ClientID = 124 AND HotelID = 1; 여기서 우리는 로컬 인덱스의 주요 단점을 볼 수 있습니다. Reservations 테이블의 모든 파티션에서 로컬 인덱스 시트 IX_HotelID_CientID를 읽어야 했습니다.
For all partitions Get first row from IX_HotelID_ClientID where ClientID = 124 and HotelID = 1 While found and ClientID = 124 and HotelID = 1 Fetch Reservations.* where RowAddress = IX_HotelID_ClientID.RowAddress Write down DateFrom, DateTo Get next row from IX_HotelID_ClientID이 실행은 테이블이 분할되지 않은 경우보다 분명히 더 많은 블록을 읽고 더 많은 시간이 걸립니다.
따라서 사용량이 적은 기간 동안 인덱스 상태를 유지하는 방법을 찾았지만 이 전략으로 인해 일부 쿼리가 느려졌습니다.
우리의 비즈니스 모델이 우리가 적은 수의 파티션을 유지하도록 허용하거나 최소한 가장 빈번한 쿼리에 RDBMS가 하나 또는 두 개의 파티션만 읽을 수 있도록 하는 기준이 포함되어 있다면 이 솔루션이 우리에게 필요한 것일 수 있습니다. 그렇지 않으면 파티셔닝을 피하고 데이터 모델, 인덱스 및 쿼리를 개선하고 데이터베이스 서버를 개선하는 것이 좋습니다.
SQL 인덱스: 다음에 배울 내용
이것이 우리 여행의 끝입니다. SQL Indexes Explained에서는 모든 최신 RDBMS에 공통적인 인덱스 구현에 중점을 두었습니다. 또한 일반적으로 데이터베이스 관리자와 관련된 주제 대신 애플리케이션 개발자가 관심을 갖는 주제에 집중했습니다. 후자는 인덱스 조각화에 대한 채우기 비율의 영향을 연구하는 데 유용하지만 두 역할 모두에 대해 더 많은 정보를 읽는 것이 유용할 것입니다.
- 데이터 및 인덱스 캐싱
- 해시, GiST, 비트맵 및 columnstore 인덱스와 같은 비 B-트리 인덱스 구조
- 클러스터형 인덱스(Oracle에서는 인덱스 구성 테이블 이라고 함)
- 기능 인덱스
- 부분 인덱스
우리가 논의한 분할 접근 방식은 범위 분할 입니다. 가장 일반적으로 사용되는 분할 유형이지만 해시 분할 및 목록 분할과 같은 다른 유형도 있습니다. 또한 일부 RDBMS는 여러 수준의 파티셔닝 옵션을 제공합니다.
마지막으로, SQL 개발자는 RDBMS 쿼리 실행과 관련된 다른 중요한 주제를 탐색하는 것이 좋습니다. 첫째, 쿼리 구문 분석, 비용 기반 실행 계획 컴파일, 캐싱 및 재사용입니다.
경험이 있는 4개의 RDMBS에 대해 다음 단계로 다음 리소스를 권장합니다.
신탁
- 옵티마이저 개요
- 인덱스 및 인덱스 구성 테이블
- 인덱스 관리
- 파티션 개요
- 파티셔닝 가이드
- 톰에게 물어봐
PostgreSQL
- 쿼리 처리
- PostgreSQL의 인덱스
- PostgreSQL의 인덱스(공식 문서)
- 버퍼 관리
- 테이블 파티셔닝
- 파티셔닝 가이드
마이크로소프트 SQL 서버
- 쿼리 처리 아키텍처
- 인덱스
- 분할된 테이블 및 인덱스
MySQL/마리아DB
- 쿼리 실행 계획 이해
- 최적화 및 인덱스
- 파티셔닝 - 기본 사항
- 파티셔닝 - 문서
- MariaDB 문서: 쿼리 최적화 및 인덱스
