서울대학교 데이터사이언스대학원 정형수 교수님의 "데이터사이언스 응용을 위한 빅데이터 및 지식 관리 시스템" 강의를 필기한 내용입니다.
Query Optimizer Overview
- SQL 은 relational calculus 에 기반하고 있는데, 이것은 declarative 하고 “어떤 순서로” 이것을 처리할 지에 대해서는 명시하지 않는다.
- 따라서 이런 relational calculus 에 “어떤 순서로” 처리할지를 결정해야 하고, 이것을 통해 생성된 relational algebra 에 기반한 결과물이 query plan 이다.
- 어떤 plan 을 사용하냐에 따라 음청나게 성능이 달라질 수 있고, 따라서 좋은 plan 을 정하는 것이 중허다 이거야.
- 이렇게 query plan 을 짜주는 component 를 Query Optimizer 라고 한다.
- 참고로 첫 query optimizer 는 셀린저 optimizer (IBM system R) 라고 한다.
- 크게 (1) heuristic, rule-based (2) cost-based search 두 가지 방법을 사용한다.
Architecture
- 위 그림이 SQL 이 입력되었을 때부터 실행의 모든 부분이 명세된 Physical Plan 이 나오기까지의 과정을 나타낸 그림이다.
- 일단 Logical Plan 은 logical 한, relational algebra expression 이다.
- 그리고 이것과 동등하지만 더 빠르고, 각 operator 마다 어떻게 data 에 접근해야 하는지에 대한 access strategy 까지 전부 명세되어 있는 relational algebra expression 이 Physical Plan 인 것.
- 그래서 간단하게 살펴보면
- SQL Query 는 SQL Rewriter 에 의해 parsing 전에 조금 변형되는 것이 가능하다. 하지만 흔한 일은 아닌 것.
- Rewrite 된 SQL 은 Parser 에 의해 AST 로 바뀌고, Binder 에 의해 table name 등의 “이름” 에서 system catalog 에 명시된 ID 로 변환되어 Logical Plan 으로 바뀐다.
- 그 다음에 Tree Rewriter 가 이 Logical Plan 을 보고 plan tree 를 좀 변형하게 된다. 이것은 보통 수행된다.
- 마지막으로 rewrite 된 Logical Plan 는 Optimizer 에 의해 system catalog 및 cost model 을 고려해 Physical Plan 으로 바뀌게 된다.
- 참고로 system catalog 를 보는 것은 schema check 의 용도도 있지만, statistics 를 위해서도 있다.
- SQL 부터 relational algebra, logical plan, physical plan 까지의 변환 과정 예시이다. 한번 보고 넘어가자.
N-P Hard
- Join table 이 15개 정도 이전까지는 dynamic programming 으로 cost model 을 다 돌려 가장 최적의 plan 을 찾지만
- 이후부터는 optimize 자체가 너무 오래걸리기 때문에 적정선에서 optimize 를 멈춘다.
- 즉, estimation 을 하는셈
- 이것은 query optimization 이 N-P hard problem 이기 때문이다. 즉, problem size 가 커질수록 정답을 찾는 cost 가 기하급수적으로 커져서 정답을 찾을 수 없다는 것임.
Relational Algebra
- Relational calculus 은 set-based notation 으로 조건에 맞는 set 을 정의하는 것을 의미한다.
- 즉, 이것은 declarative 한 표현 방법이다.
- Relation algebra: 그 set 을 구하기 위한 계산 순서로, operational 한 표현방법이다.
- SQL 을 단순히 relational algebra 로 바꾸어서 AST 로 만들면 그게 logical plan 가 되고
- 그것을 최적화해서 실제 수행할 연산 및 취할 전략같은 것들이 모두 명시된 상태가 physical plan 이 되는 것.
- SQL 은 relational calculus 에 기반을 두기 때문에, 이것만으로는 어떻게 수행할지 알 수 없어 algebra (plan) 으로 바꾼다.
- 근데 이걸 막 바꿔도 되나? -> (Relational algebra 의 창시자인) Codd’s theorem 에 따르면 calculus 와 algebra 가 1:1 로 대응되고 이때 성능상의 차이는 없다고 한다.
Property
- Relational algebra 는 다음과 같은 특징을 가진다:
- Closed property: Relational algebra 의 연산은 항상 input 과 output 이 모두 relational schema 이다.
- 따라서, relational algebra 의 연산 결과를 또 다른 relational algebra 에 피연산자로 넣는 것이 가능하다 이말이야.
- Typed property: 각 값들은 “자료형 (type)” 을 가지는데, 이때의 자료형은 Attribute 를 말한다.
- 연산에 자료형이 있다는 것은 예를 들어 column 로 projection 하고 싶으면 input, output 에 모두 column 가 있어야 한다는 소리이다.
- Closed property: Relational algebra 의 연산은 항상 input 과 output 이 모두 relational schema 이다.
- 그리고 relational algebra 엄밀하게는 set-based 이고, SQL 는 bag (순서 없는 중복 허용 자료구조) 이라고 한다.
Operators
Unary
- Projection ():
SELECT …
- 원하는 column 을 고르는 것 (전체 schema 에서 원하는 column 을 vertical 로 filtering).
- Output schema 는 input schema 와 다를 수 있다 (schema change).
- 실제 DBMS 에서는 그렇지 않지만, Relational algebra 에서의 projection 은 중복 제거 때문에 row 의 개수가 줄어들 수 있다.
- Selection ():
WHERE predicate
- 조건에 따라 원하는 tuple 을 고르는 것 (즉, horizontal filtering).
- Select 는 schema change 가 일어나지 않는다.
- 알다시피 predicate 는 우선 실행하는게 유리하다 (Predicate pushdown): 먼저 filtering 하는 것이 당연히 데이터 사이즈를 줄여놓고 시작하기 때문.
- 여기에 깔린 생각은 output 이 동일하다면 빨리 실행되는 것이 와따라는 것이다.
- 어차피 언젠가는 filtering 한다면 이것을 먼저 수행해버려 빨리 실행될 수 있게 하는 것.
- Rename (): Column name 바꾸는 것 (즉, aliasing).
- 여러 table 에 같은 이름의 column 이 있을 수 있기 때문에 이런 rename operation 이 존재한다.
- 물론 내부적 (system catalog) 에서는 고유한 ID 가 있긴 하다.
- Self-join, relationship table 에서 모호성이 발생할 수 있기 때문에 이런 operation 이 있는 것.
- 으로 명시된다: 즉, 예를들어 등.
Binary
- Union (): 뭐 너가 아는 합집합.
- 인데, 좀 다른점은 relational algebra 는 typed 라는 것이다. 즉, 두 피연산자는 동일한 schema 를 가져야 한다.
- 그리고 SQL 에서는 중복제거를 하지 않는
UNION ALL
이 있다.
- Set-difference (): 이것도 너가 아는 차집합.
- Cross-product (): 너가 아는 cartesian product.
Compound
- Compound operator 는 복잡한 expression 에 대한 “macro” 라고 생각하면 된다.
- Intersect (): 너가 아는 교집합.
- 이기 때문.
- Join (): 너가 아는 join.
- 이건 Theta join 이라고도 한다.
- Join 은 cartesian product 이후 theta 로 selection 하는 것이기 때문 ().
- 따라서 보통 () 이 join 기호지만, () 라고 적기도 한다.
- Natural join: 같은 이름의 column 으로 join 을 한 다음 두 column 이 중복되어 output 에 들어가기 때문에 이것을 projection 하는 것을 의미한다.
- 즉, .
- Join 을 Condition Propagation 라고 부르기도 한다. 왜냐면 Dimension table 에 predicate 를 걸고 Fact table 에 projection 해서 query 하는 경우가 많고, 이때 저 dimension 에 걸린 predicate 이 fact 로 “전파” 되는 것이기 때문.
- 이건 Theta join 이라고도 한다.
Relational Algebra Equivalences
- 두 relational algebra expression 은 “동일한 set” 을 결과로 가질 때 그 둘이 Equivalent 하다고 한다.
- 이 점을 이용하여 cost model 을 돌리지 않고 어느정도 query plan 을 변경할 수 있고, 이것을 Query rewriting 이라고 한다.
- 사례? 를 몇가지 알아보자면
- 위에서 말한 predicate pushdown 이나 (위 그림)
- 처럼 selection 을 break 해서 더 간단한 것으로 바꾸거나
- Join 이 교환법칙 (commutative) 을 만족한다는 것을 이용해 IO 가 덜 발생하도록 순서를 바꾸는 등
- 또한 projection pushdown 도 있다 (위 그림): 사용하지 않는 attribute 들을 일찍 배제해버려 early materialization 에 비해 적은 양의 intermediate data 를 차지하도록 하는 것.
Heuristic, Rule-based Logical Query Optimization
- Logical Query Optimization 은:
- 일단 logical query 를 동등한 다른 logical query 로 바꾸는 것이고
- 추후에 이어질 cost search 에서 optimal plan 을 찾기 쉽게 해주는 것이 목표이며
- 다른 plan 과 비교는 하지 않고, pattern (heuristic, rule) 이 맞다면 그냥 바꿔버리는 형식이다.
- 가장 기본적인 아이디어는 predicate pushdown 이다: 즉, early pruning.
- 그래서 predicate 을 type 이 맞는 한 최대한 내려버리게된다.
- 이때 얼마나 털어내느냐를 Reduction Factor (RF) 라고 한다.
- 여기 나오는 기법들은 위에서도 언급한 내용들이다. 정식 명칭들과 함께 살펴보자.
- 이런 작업은 tree rewriter 에서 수행한다.
- 즉, equivalent logical plan 으로 다 바꾸는 것.
Split Conjunctive Predicate
- 말 그대로 연결되어 있는 (Conjunctive) Predicate 들을 분리 (Split) 하는 방법이다.
- 즉, 이 상태에서
- 이렇게 바꾸는 것.
- 이것의 목적은 당연히 이렇게 나눠서 predicate pushdown 을 하려는 것이다.
Predicate Pushdown
- 위에서도 말한것처럼, predicate 을 일찍 적용해 early pruning 을 하는 것이다.
- 즉, 그림으로 보자면 아래와 같은 plan tree 가
- 이렇게 바뀌는 것.
Replace Cartesian Product
- 당연히 Cartesian Product 후 predicate 을 적용하는 것은 inner join 과 동일하다. 따라서 이렇게 cartesian product 를 교체해주는 것이 Replace Cartesian Product 이다.
- 가령 아래와 같은 plan tree 는
- 이렇게 바뀌게 된다.
Projection Pushdown
- 필요 없는 attribute 는 일찍 털어내서 intermediate data size 를 줄이는 것이 목적이다.
- 다만, intermediate operation 에서도 필요한 field 까지 projection 해주긴 해야 한다는 점에 주의하자.
- 가령 아래 plan tree 는
ARTIST
에서는ID
와NAME
만,APPEARS
에서는ARTIST_ID
와ALBUM_ID
만,ALBUM
에서는ID
만 필요하니까 얘네들만 남기고 전부 projection 을 걸어주면 아래처럼 된다:
Nested Subqueries
- SQL 에서는 하나의 값만을 뱉는 query 를
WHERE
clause 에서 마치 함수처럼 사용할 수 있다. - 근데 이놈을 처리하지 않으면 문제가 된다. Volcano 에서 이
WHERE
을 통과할 때마다 nested query 를 실행하기 때문. - 그럼 이때 이놈을 바꿔보자.
- 이 nested subquery 를 바꾸는 작업은 SQL Rewriter 가 수행한다고 한다.
Rewriting
- 위 그림과 같은 방식으로 결과는 같지만 nesting 을 빼도록 새로 작성 (rewrite) 하기도 한다.
- 뭐 규칙이 있겠지만 자세히 다루지는 않는다; 이런게 있구나 정도로만 생각하자.
Decomposing
- Nested query 가 복잡해질수록 rewrite 로는 힘들때가 많다. 따라서 그냥 nested query 를 temporary table (query 실행이 끝나면 삭제되는) 으로 바꾼 다음 처리하고, 이것을 Decomposing 이라고 한다.
- 즉, 아래와 같은 놈을
- 요래바꾼다.
Expression Rewriting
- 뭐
WHERE
절에 있는 expression 을 더 optimal, minimal 한 expression 으로 바꾸는 작업도 한다. - 이것에 관해서는 자세히 알아보지 않고 지나간다.