@@ -100,7 +100,7 @@ void Spiller::fillSpillRuns(std::vector<SpillableStats>& statsList);
100100 - 해시 빌드와 해시 프로브 연산자는 연관 파티션이 스필되었다면 입력 로우를 디스크에 스필한다.
101101 - 해시 빌드 연산자는 파티션이 스필되었다면 그 파티션의 모든 입력 로우는 스필되어야 한다.
102102 - 조인할 파티션의 일부 로우로만 해시 테이블을 빌드할 수 없기 때문.
103- - 해시 프로브 연산자는 그 자체로는 스필 불가능하다. 다만 해시 빌드에서 연관된 파티션이 스필된 경우 입력 행을 스필해야 합니다 .
103+ - 해시 프로브 연산자는 그 자체로는 스필 불가능하다. 다만 해시 빌드에서 연관된 파티션이 스필된 경우 입력 행을 스필해야 한다 .
104104
105105``` c++
106106voild 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+ 
152+
153+ **spill**
154+ - 해시 집계 연산자는 중간 집계 상태를 그룹 당 하나의 테이블 엔트리로 해시 테이블에 저장한다.
155+ - 스필이 발생하면 연산자의 스필러 객체는 로우 컨테이너 내 모든 로우를 스캔하여 스필 목표를 만족하는 파티션 집합을 선택한다.
156+ - 스필된 행의 테이블 엔트리는 해시 테이블에서 제거된다.
157+ - 스필 이후 연산자는 다음 스필이 발생할 때까지 반복해서 입력을 처리한다.
158+ - 스필러는 동일 파티션을 다시 스필하는것을 선호한다.
159+
160+ 
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+ 
200+ - OOM을 막기 위해 해시 빌드 연산자는 빌드 측 입력을 스필해야 한다.
201+ - 해시 빌드 연산자는 모든 연산자가 동일 파티션 집합을 스필함을 보장하기 위해 서로 조정한다.
202+ - 만약 연산자가 독립적으로 스필하면, 모든 파티션이 스필되는 상황이 발생할 수 있다.
203+ - 해시 테이블 빌드를 위해 하나 이상의 파티션에서 모든 로우가 필요하다.
204+ - 해시 집계나 order by와 다르게 해시 조인 스필은 해시 빌드 연산자가 명시적으로 통제한다.
205+
206+ **probe**
207+
208+ 
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