적을수록 좋습니다. - 루드비히 미스 반 데어 로에
쿼리 최적화 도구의 목표에 대해 이보다 더 정확한 표현은 없습니다. 더 적은 작업을 수행하세요: 적은 메모리, 적은 CPU, 적은 디스크, 적은 IO, 적은 명령어, 적은 파티션, 적은 오버플로. 생성하는 쿼리 계획에 필요한 모든 것을 줄이세요. 이것이 바로 SQL과 NoSQL 최적화 도구.
Couchbase 6.5에서는 쿼리 서비스에서 N1QL을 위한 비용 기반 최적화 도구(CBO-프리뷰)를 발표했습니다. 여기에서는 CBO의 이점에 익숙하지 않은 NoSQL 사용자들의 질문에 답해 보려고 합니다.
- CBO가 꼭 필요한 이유는 무엇인가요?
- CBO가 없는 경우 성과에 미치는 영향은 무엇인가요?
주제는 다음과 같습니다. vast. 여기에 있는 답변은 간단하고 포괄적이지 않습니다.
2019년, 자녀의 발표회나 야구 경기에 제시간에 도착해야 하는 중요한 순간에 교통 상황을 고려하지 않는 정적 길안내 지도를 사용하시겠습니까? Google 지도의 경로 최적화 도구는 시간에 맞춰 최적화합니다. 최적화 도구는 최소한의 리소스로 쿼리를 실행할 수 있는 방안을 찾으려고 노력합니다: CPU, 메모리. 이를 알면서 왜 정적 규칙(또는 쿼리 모양) 기반 최적화를 비즈니스 크리티컬 데이터베이스 워크로드에 적용하고 계신가요?
데이터베이스 최적화 도구가 결정을 내립니다. 이러한 결정은 쿼리 성능, 시스템 처리량 및 SLA 충족 능력에 큰 영향을 미칩니다. 더 나은 최적화 도구가 있는 데이터베이스를 사용하면 개발, 관리 및 SLA를 더 쉽게 충족할 수 있습니다.
SQL은 가장 성공적인 4세대 언어입니다. 언어로서, 기본 스키마가 그렇지 않은 경우에도 매우 유연합니다. 모든 조합을 미리 계획하지 않고도 모든 관계(테이블 또는 중간 관계)를 선택, 조인, 투영할 수 있습니다. 이는 앱 개발과 데이터 분석에 도움이 됩니다. 이 기사에서는 주요 NoSQL 데이터베이스가 어떻게 는 SQL의 다양한 요소를 구현했습니다. 따라서 NoSQL 데이터베이스도 최적화에 신경을 써야 합니다.
쿼리 언어의 뛰어난 유연성에는 쿼리를 효율적으로 최적화하고 실행해야 하는 막중한 책임이 따릅니다. SQL의 초기 구현은 규칙 기반 최적화 도구를 사용했습니다. 이로 인해 규칙의 복잡성, 사용자 정의 최적화 도구 힌트, 복잡한 쿼리에 대한 쿼리 계획 효율성 문제가 발생했습니다. 하지만 비용 기반 최적화 도구 (CBO) 는 다양한 데이터, 데이터 왜곡 및 워크로드에 맞게 쿼리를 올바르게 최적화하여 모든 것을 변화시켰습니다. CBO가 없었다면 RDBMS가 저비용으로 이렇게 다양한 사용 사례를 처리하는 데 성공하지 못했을 것이라고 해도 과언이 아닙니다. 최적화 도구가 있는 NoSQL 시스템도 마찬가지입니다.
데이터베이스 최적화 도구가 결정을 내립니다. 잘못된 결정은 성능에 큰 부정적인 영향을 미칩니다. 실제 워크로드의 경우, 통계를 기반으로 한 의사 결정이 규칙 기반 의사 결정보다 훨씬 낫습니다. 통계가 이를 증명합니다!
최적화 도구는 크게 다음과 같은 작업을 수행합니다:
- 다시 쓰기: 쿼리를 최적의 동등한 형식으로 다시 작성하면 최적화가 더 쉬워집니다. 여기에는 상수 필터 평가, 조인 변환, 하위 쿼리 평탄화, 하위 쿼리 접기 등이 포함됩니다. 재작성 유형은 후속 단계에서 최적화 도구의 특정 기능과 뉘앙스에 따라 달라집니다.
- 액세스 경로: 각 키 스페이스(테이블에 해당)에 대해 사용 가능한 인덱스 또는 전체 스캔(Couchbase의 경우 기본 인덱스 스캔) 중에서 선택합니다. 여기서 각 키스페이스에 대해 하나 이상의 인덱스를 선택하고, 각 스캔 요청에 대한 술어(범위)를 결정하고, 포함 여부를 결정합니다.
- 주문하기: 그 목적은 중간 결과 집합의 크기를 제한하는 것입니다. 조인은 한 번에 두 개의 키 스페이스(테이블)에서 수행됩니다. 조인 유형에 따라 쿼리의 의미와 결과를 변경하지 않고 순서를 변경할 수 있습니다. 예를 들어, ((t1 INNER JOIN t2) INNER JOIN t3)은 ((t3 INNER JOIN t2) INNER JOIN t1)과 동일합니다. 여기서는 조인이 수행되는 순서를 선택합니다. N1QL 옵티마이저는 아직 조인의 순서를 다시 지정하지 않습니다.
- 가입 유형: 각 쿼리 엔진은 특정 유형의 조인을 지원합니다. Couchbase 쿼리 서비스 및 분석 서비스는 모두 중첩 루프(NLJ) 및 해시 조인(HJ)을 지원합니다. 쿼리 서비스의 경우 중첩 루프가 기본값이며, 분석 서비스의 경우 해시 조인이 기본값입니다. 조인 유형을 선택한 후에는 조인 내에서 순서에 대한 추가 결정을 내려야 합니다. NLJ의 경우, 어떤 테이블이 외부 테이블이고 어떤 테이블이 내부 테이블인지 결정해야 합니다. 일반적으로 결과 집합이 더 작은 테이블(키 스페이스)을 외부 테이블로 선택합니다. HJ의 경우, 어느 테이블이 (해시 테이블) 빌드 쪽이고 다른 테이블이 계획의 프로브 쪽이 될지 결정해야 합니다.
- 최적화를 위한 추가 고려 사항이 있습니다(예: LIMIT 절을 지정한 경우 첫 번째 행 최적화).
- 실행 트리 만들기: 마지막으로, 이전 단계의 결정을 나타내는 연산자와 매개변수 값을 사용하여 쿼리 실행 트리(계획)를 만듭니다.
예시:
SELECT id, address FROM customer WHERE postalcode = 57020;
동일한 쿼리가 단일 행, 수백만 행 또는 수십억 행에 대해 작동할 수 있습니다. 이것은 쿼리만큼이나 간단할 수 있지만, 그 이면에는 복잡성이 숨어 있습니다. 옵티마이저는 데이터에 접근하기 위해 여러 가지 옵션을 사용할 수 있습니다.
- 전체 테이블 스캔은 항상 옵션입니다. 고객 테이블에 데이터베이스 페이지에 맞는 행이 한두 개만 있는 경우 전체 테이블 스캔을 수행합니다. 일 수 있습니다 데이터에 도달할 수 있는 가장 효율적인 경로입니다.
- 테이블에 인덱스가 있다고 상상해 보세요.
- CREATE INDEX i1 ON customer(postalcode)
먼저 인덱스를 스캔하여 술어와 일치하는 행의 행ID를 찾은 다음 추가 열(ID, 주소)을 투영할 행을 가져오는 인덱스 경로가 가장 좋을 것이라고 생각할 수 있습니다. 그렇지 않습니다. 테이블에 백만 개의 행이 있고 모든 행의 우편번호가 57020으로 정확히 같다면 어떨까요? 그러면 인덱스 액세스 경로가 실제로 테이블 스캔보다 비용이 더 많이 듭니다.
이제 쿼리를 약간 수정해 보겠습니다.
SELECT id, address FROM customer WHERE postalcode = 57020 and yob < 1980;
다음과 같은 인덱스가 있다고 가정해 보겠습니다:
|
1 2 3 4 5 6 7 8 9 10 |
CREATE INDEX i1 ON customer(postalcode); CREATE INDEX i2 ON customer(yob); CREATE INDEX i3 ON customer(postalcode, yob); CREATE INDEX i4 ON customer(yob, postalcode); CREATE INDEX i5 ON customer(postalcode, id, address); CREATE INDEX i6 ON customer(yob, id, address); CREATE INDEX i7 ON customer(postalcode, yob, id, address); CREATE INDEX i8 ON customer(yob, postalcode, id, address); |
최적화 프로그램에 대한 유효한 액세스 경로를 선택합니다:
- 각 인덱스 i1~i8은 유효한 액세스 경로입니다.
- 테이블 스캔은 항상 옵션입니다.
- 여러 인덱스 결합
이렇게 간단한 쿼리에서도 쿼리에 가장 적합한 인덱스를 선택하기가 쉽지 않습니다. 따라서 규칙 기반 최적화 도구는 일련의 규칙을 유지하고 이러한 규칙을 일관되게 따라 최상의 방안을 제시합니다. 일련의 규칙은 다음과 같습니다. N1QL 규칙 기반 최적화 도구 는 잘 문서화되어 있습니다.
이러한 규칙은 처음부터 정해진 것이 아닙니다. 인덱스 경로, 가장 많은 키를 가진 인덱스 등을 선호하기 시작합니다. 그러다 보면 충돌이 생기게 됩니다.
예:
쿼리: SELECT id, address FROM customer WHERE postalcode = 57020 and yob < 1980;
색인:
CREATE INDEX i7 ON customer(postalcode, yob, id, address); CREATE INDEX i8 ON customer(yob, postalcode, id, address);
규칙 기반 최적화 도구는 이러한 인덱스 중 어떤 것이 가장 효율적인지 파악할 수 없습니다. 모든 것은 데이터 왜곡으로 귀결됩니다: 한 데이터베이스에서 선택한 인덱스가 다른 데이터베이스에서는 최적이 아닐 수 있습니다.
예시:
쿼리:
|
1 2 3 4 5 6 |
SELECT c.state, d.status, SUM(o.sale_amt) FROM order o INNER JOIN customer c ON (o.cid = c.id) INNER JOIN demo d ON (c.did = d.did) WHERE d.edu = “college” AND d.mstatus = “married” GROUP BY c.state, d.status |
주어진 FROM 절에서 다음 주문은 모두 유효한 주문입니다. 최적화 도구는 이 중 어느 것을 선택해야 할까요?
- ((내부 조인 고객 주문) 내부 조인 데모)
- ((고객 내부 조인 주문) 내부 조인 데모)
- ((내부 조인 데모 주문) 내부 조인 고객)
- ((고객 내부 조인 데모) 내부 조인 주문)
- ((내부 조인 주문 데모) 내부 조인 고객)
- ((내부 조인 고객 데모) 내부 조인 주문)
FROM 절의 키 스페이스(또는 테이블) 수가 증가함에 따라 선택 항목이 늘어나고 선택이 더 복잡해집니다. 순서가 잘못되면 중간 결과가 방대해져 나중에 대부분을 버릴 수 있습니다. 예를 들어, 위의 쿼리에서 '대학' 학력의 기혼 고객만 대상으로 하기 때문에 고객에 대한 조인 순서를 먼저 지정하면 중간 결과 집합이 엄청나게 커집니다. 잘못된 조인 순서는 쿼리 지연 시간과 시스템 처리량 모두에 부정적인 영향을 미칩니다.
예시:
쿼리:
|
1 2 3 4 |
SELECT c.state, c.zip, SUM(sale_amt) FROM order o INNER JOIN customer c ON (o.cid = c.id) WHERE o.year = “2018” GROUP BY state, zip; |
여기서 두 가지 결정을 내려야 합니다. 테이블의 조인 유형과 순서입니다. 각각에 대한 통계를 알지 못하면 지능적으로 결정할 수 없습니다. 따라서 규칙 기반 최적화 도구는 단순히 하나의 방법을 기본값으로 설정하고 사용자가 기본값에서 변경할 수 있도록 의존합니다. 이는 비효율적이며 대규모 쿼리에서는 실행 불가능합니다. 이로 인해 성능에 미치는 영향은 몇 초에서 몇 분 또는 몇 분에서 몇 시간까지 엄청납니다.
다시 한 번 통계적 추정치가 도움이 됩니다. 엔터프라이즈 애플리케이션에서는 키 공간(테이블)과 복잡한 술어가 많은 쿼리가 일반적입니다.
결론
실제 워크로드의 경우 통계를 기반으로 한 의사 결정이 규칙 기반 의사 결정보다 훨씬 낫습니다. 당연하죠. 그 이유는 다음과 같습니다. N1QL은 CBO를 구현했습니다.. 다운로드 Couchbase 6.5 를 클릭하고 직접 확인하세요.
NoSQL 데이터베이스를 결정하기 전에 공급업체에 문의하세요: 비용 기반 최적화 도구가 있나요?
참조
- NoSQL 데이터베이스에서 SQL의 불합리한 효율성: 비교 연구. https://www.couchbase.com/blog/the-unreasonable-effectiveness-of-sql-in-nosql-databases/
- SQL의 불합리한 효율성 https://www.couchbase.com/blog/unreasonable-effectiveness-of-sql/
- Couchbase 6.5를 다운로드합니다: https://couchbase.com/downloads?family=server&product=couchbase-server-developer
- 관계형 시스템의 쿼리 최적화에 대한 개요. https://cs.stanford.edu/people/chrismre/cs345/rl/chaudhuri98.pdf
