Skip to content

Commit 7cc7646

Browse files
committed
spill hash join
1 parent 738db1a commit 7cc7646

File tree

1 file changed

+84
-1
lines changed

1 file changed

+84
-1
lines changed

_posts/2024-12-14-spilling.md

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ void Spiller::fillSpillRuns(std::vector<SpillableStats>& statsList);
100100
- 해시 빌드와 해시 프로브 연산자는 연관 파티션이 스필되었다면 입력 로우를 디스크에 스필한다.
101101
- 해시 빌드 연산자는 파티션이 스필되었다면 그 파티션의 모든 입력 로우는 스필되어야 한다.
102102
- 조인할 파티션의 일부 로우로만 해시 테이블을 빌드할 수 없기 때문.
103-
- 해시 프로브 연산자는 그 자체로는 스필 불가능하다. 다만 해시 빌드에서 연관된 파티션이 스필된 경우 입력 행을 스필해야 합니다.
103+
- 해시 프로브 연산자는 그 자체로는 스필 불가능하다. 다만 해시 빌드에서 연관된 파티션이 스필된 경우 입력 행을 스필해야 한다.
104104

105105
```c++
106106
voild Spiller::spill(uint32_t partition, const RowVectorPtr& spillVector);
@@ -136,3 +136,86 @@ SpillPartition::createReader();
136136
```
137137
138138
## SpillFileList and SpillFile
139+
- `SpillFileList` 객체는 단일 파티션의 스필 파일을 관리한다.
140+
- 각 스필 파일은 하나의 `SpillFile` 객체로 관리된다.
141+
- `SpillFile` 객체는 저수준 입출력 연산을 제공한다.
142+
- spill
143+
- `SpillFileList`는 입력으로 로우 벡터를 받아 `VectorStreamGroup`생성해 로우 벡터를 직렬화하고, `SpillFile`과 연관된 현재 열린 스필 파일에 쓴다.
144+
- `SpillFileList`는 목표 파일 사이즈를 넘어가면 새 스필 파일을 생성한다.
145+
- resotre
146+
- `SpillFile` 객체는 직렬화된 바이트 스트림을 읽어서 `VecotrStreamGroup`을 이용해 로우 벡터로 역직렬화한다.
147+
148+
# Spilling Algorithms
149+
150+
## Hash Aggregation
151+
![](https://facebookincubator.github.io/velox/_images/spill-aggregation-spill.png)
152+
153+
**spill**
154+
- 해시 집계 연산자는 중간 집계 상태를 그룹 당 하나의 테이블 엔트리로 해시 테이블에 저장한다.
155+
- 스필이 발생하면 연산자의 스필러 객체는 로우 컨테이너 내 모든 로우를 스캔하여 스필 목표를 만족하는 파티션 집합을 선택한다.
156+
- 스필된 행의 테이블 엔트리는 해시 테이블에서 제거된다.
157+
- 스필 이후 연산자는 다음 스필이 발생할 때까지 반복해서 입력을 처리한다.
158+
- 스필러는 동일 파티션을 다시 스필하는것을 선호한다.
159+
160+
![](https://facebookincubator.github.io/velox/_images/spill-aggregation-restore.png)
161+
162+
**restore**
163+
- 모든 입력을 처리한 뒤 해시 집계 연산자는 인메모리와 디스크 상태를 병합하여 결과를 출력한다.
164+
- 각 스필 파티션에서 연산자는 로우 컨테이너의 모든 행을 정렬한다.
165+
- 각 스필 파일도 정렬되어 있다.
166+
- 이후 연산자는 정렬 병합 reader를 생성하여 같은 그룹 키를 가진 중간 상태를 병합하고 하나의 최종 집계 상태를 출력한다.
167+
- 그룹의 중간 상태는 연산자 수행 중 여러 번 스필될 수 있다.
168+
- 정렬은 **그룹 키를 기준으로 수행된다.**
169+
170+
## OrderBy
171+
172+
**spill**
173+
- order by 연산자는 모든 입력을 로우 컨테이너에 저장하고 모든 입력을 받은 후 정렬한다.
174+
- 스필이 발생하면 스필러는 스필 목표를 만족하는 충분한 수의 로우를 수집한다.
175+
- 해시 집계 스필과 다르게, 로우를 파티션하지 않는다.
176+
- order by 연산자는 전체 입력의 순서를 생성해야 하기 때문.
177+
- 스필 이후 연산자는 다음 스필이 발생할 때까지 반복해서 입력을 처리한다.
178+
179+
**resotre**
180+
- 모든 입력을 처리한 뒤 order by 연산자는 먼저 로우 컨테이너 내 모든 행을 정렬한다.
181+
- 각 스필 파일도 정렬되어 있다.
182+
- 이후 연산자는 정렬 병합 reader를 생성하여 정렬된 데이터를 읽고 출력한다.
183+
- 정렬은 **쿼리 플랜 노드가 명시한 비교 옵션을 사용해야 한다.**
184+
185+
## Hash Join
186+
- 해시 조인은 해시 빌드와 해시 프로브 연산자로 나뉜다.
187+
- 각각은 별개의 드라이버 파이프라인으로 나뉜다.
188+
- 두 파이프라인은 공유 해시 조인 브릿지 자료구조를 통해 연결된다.
189+
- 해시 빌드 연산자는 빌드 측 입력을 받아 해시 테이블을 생성한다.
190+
- 빌드가 끝나면 해시 빌드 연산자중 하나는 공유 해시 조인 브릿지를 통해 모든 해시 프로브 연산자에 테이블을 전송한다.
191+
- 해시 프로브 연산자는 프로브 측 입력을 받아 해시 테이블을 사용해 조인을 수행한다.
192+
- 한 번에 하나의 배치를 처리한다.
193+
- 해시 프로브 연산자는 한번에 최대 하나의 배치를 메모리에서 처리한다. 때문에 메모리를 과도하게 사용하지 않는다.
194+
- 해시 빌드 연산자는 해시 테이블을 생성하기 위해 메모리를 많이 사용하며 전체 해시 조인이 수행되는 동안 메모리에 유지한다.
195+
- order by 처리와 유사하게, 각 해시 빌드 연산자는 빌드 측 입력을 로우 컨테이너에 저장하고 모든 입력을 처리하면 연산자 중 하나가 단일 집계 해시 테이블을 생성한다.
196+
197+
**build**
198+
199+
![](https://facebookincubator.github.io/velox/_images/spill-hash-join-build.png)
200+
- OOM을 막기 위해 해시 빌드 연산자는 빌드 측 입력을 스필해야 한다.
201+
- 해시 빌드 연산자는 모든 연산자가 동일 파티션 집합을 스필함을 보장하기 위해 서로 조정한다.
202+
- 만약 연산자가 독립적으로 스필하면, 모든 파티션이 스필되는 상황이 발생할 수 있다.
203+
- 해시 테이블 빌드를 위해 하나 이상의 파티션에서 모든 로우가 필요하다.
204+
- 해시 집계나 order by와 다르게 해시 조인 스필은 해시 빌드 연산자가 명시적으로 통제한다.
205+
206+
**probe**
207+
208+
![](https://facebookincubator.github.io/velox/_images/spill-hash-join-probe.png)
209+
- 해시 프로브 연산자 자체는 스필 불가능하다. 다만 빌드 측에서 스필이 발생하면 스필해야 한다.
210+
- 빌드 연산자가 파티션 N을 스필하면, 프로브 연산자 또한 N의 모든 로우를 스필해야 한다.
211+
- 그리고 프로브 입력의 나머지만 해시 테이블과 조인해야 한다.
212+
- 나중에 빌드 연산자가 파티션 N으로부터 해시 테이블을 생성하면, 프로브 연산자는 스필된 연관 입력을 다시 읽어야 한다.
213+
- **해시 조인은 파티션 컬럼으로 조인 키 컬럼을 사용하기에 스필 데이터를 정렬할 필요가 없다.**
214+
215+
- 빌드 측이 너무 크면 이전에 스필된 파티션을 복구할 때 OOM이 발생한다.
216+
- 이때 재귀 스필을 수행한다. 재귀적으로 스필된 파티션을 다시 여러 부파티션으로 쪼갠다.
217+
- 재귀 스필을 지원하기 위해 파티션 비트를 우 시프트한다. 비트는 파티션 번호를 계산하는데 사용된다.
218+
219+
### Hash Build
220+
### Hash Probe
221+
### HashJoinBridge

0 commit comments

Comments
 (0)