JOIN 관련 설정
JOIN 관련 설정
ClickHouse의 JOIN 동작 방식을 제어하는 설정들이에요. 어떤 JOIN 알고리즘을 사용할지, ANY strictness의 동작, 기본 strictness, 크기 제한 초과 시 동작, NULL 처리 방식 등을 다룬답니다. 특히 join_algorithm은 콤마로 구분해 여러 알고리즘을 지정할 수 있어요. 이 설정들은 system.settings 테이블에서 확인할 수 있고 소스 코드에서 자동 생성된 값들이에요.
출처: 문서
본문
이 설정들은 system.settings에서 확인할 수 있고, 소스 코드에서 자동 생성된 값들이에요.
join_algorithm
사용할 JOIN 알고리즘을 지정해요. 여러 알고리즘을 지정할 수 있고, 특정 쿼리에 대해 kind/strictness와 테이블 엔진에 따라 사용 가능한 하나가 선택돼요.
해시 기반 알고리즘이 디스크로 spill하는지 여부는 이 선택의 일부가 아니에요. max_bytes_before_external_join / max_bytes_ratio_before_external_join은 모든 알고리즘의 spill 임계값이고(둘 중 하나가 0이 아니면 enable_adaptive_memory_spill_scheduler가 메모리 압박 아래에서 조인을 더 일찍 spill할 수 있음), max_rows_in_join / max_bytes_in_join은 모든 알고리즘의 하드 캡이에요. 단, legacy_join_size_limits_trigger_spilling이 두 캡을 디스크의 spill 트리거로 되돌리지 않는 한 그렇죠. 선택한 값은 조인이 어떻게 spill하는지를 결정해요. grace_hash는 첫 번째 블록부터 오른쪽 테이블을 파티셔닝하고, hash와 parallel_hash는 메모리에 모은 뒤 임계값을 넘으면 전환해요.
대부분의 알고리즘은 자신이 그 쿼리에 선택된 경우에만 쿼리에 영향을 줘요. 그러나 일부는 단지 나열되는 것만으로도 계획을 바꿔요. 최종적으로 선택되지 않는 낮은 우선순위 fallback이어도 말이죠. 알고리즘이 선택되기 전에 결정이 이루어지기 때문이에요. 두 가지 효과가 있어요:
- 조인 키 타입 추론이 더 엄격해져요(머지 조인은 다른 타입의 키를 조인할 수 없음. 예:
String과Nullable(String)). 이는USING컬럼의 결과 타입을 바꿀 수 있고, 조인이Join-엔진 테이블에서TYPE_MISMATCH로 실패하게 할 수 있어요.full_sorting_merge와parallel_full_sorting_merge에 의해 트리거돼요. - 조인의 보존 측에서
ORDER BY ... LIMIT가 프라이머리 키 순서의 읽기 대신 명시적 정렬을 받아요. 조인이 정렬된 읽기를 깨뜨린다고 가정하기 때문이에요(머지 조인은 자신의 사전-조인 정렬을 삽입하고, 부분 머지 조인은 왼쪽 블록을 재정렬하며, 지연 블록을 생성할 수 있는 조인은 정렬된 읽기를 전파하지 않음). 결과는 같지만 계획은 덜 효율적이에요.full_sorting_merge,parallel_full_sorting_merge,partial_merge,prefer_partial_merge,grace_hash,auto, 그리고 0이 아닌max_bytes_before_external_join/max_bytes_ratio_before_external_join에 의해 트리거돼요.
둘 다 쿼리가 결국 hash나 다른 알고리즘으로 실행되더라도 적용돼요. 이것이 바람직하지 않으면, 영향을 받는 쿼리에서는 위 알고리즘을 join_algorithm에 나열하지 마세요.
가능한 값:
-
grace_hashGrace hash 조인이 사용돼요. Grace hash는 메모리 사용을 제한하면서 성능 좋은 복잡한 조인을 제공하는 알고리즘 옵션이에요.
grace_hash는 첫 번째 블록부터 외부(external)에요. 오른쪽 테이블이 곧바로 파티셔닝되는 반면,hash와parallel_hash는 먼저 메모리에 모은 뒤 spill 임계값을 넘을 때만 파티셔닝해요. 오른쪽이 메모리에 맞지 않을 것을 이미 알고 있고 인-메모리 단계를 건너뛰고 싶을 때 선택하세요. spill 임계값 자체는 모든 해시 알고리즘이 사용하는 것과 동일한max_bytes_before_external_join/max_bytes_ratio_before_external_join이며,legacy_join_size_limits_trigger_spilling이 켜져 있지 않으면 둘 중 하나는 0이 아니어야 해요. 임계값이 없으면grace_hash는 목록의 다음 알고리즘으로 건너뛰고, 유일한 알고리즘이면 거부돼요.grace 조인의 첫 단계는 오른쪽 테이블을 읽고 키 컬럼의 해시 값에 따라 N개 버킷으로 나눠요(초기 N은
grace_hash_join_initial_buckets). 각 버킷이 독립적으로 처리될 수 있도록 보장하는 방식이에요. 첫 번째 버킷의 행은 인-메모리 해시 테이블에 추가되고 나머지는 디스크에 저장돼요. 해시 테이블이 spill 임계값을 넘어 커지면 버킷 수가 증가하고 각 행에 할당된 버킷도 함께 증가해요. 현재 버킷에 속하지 않는 행은 flush되고 재할당돼요.INNER/LEFT/RIGHT/FULL ALL/ANY JOIN을 지원해요. -
hashHash join 알고리즘이 사용돼요. 모든 kind와 strictness 조합, 그리고
JOIN ON섹션에서OR로 결합된 여러 조인 키를 지원하는 가장 일반적인 구현이에요.hash알고리즘을 사용하면JOIN의 오른쪽 부분이 RAM에 올라가요. -
parallel_hashhash조인의 변형으로, 데이터를 버킷으로 나누고 동시에 여러 해시 테이블을 빌드해 이 과정을 가속화해요.parallel_hash알고리즘을 사용하면JOIN의 오른쪽 부분이 RAM에 올라가요. -
partial_merge정렬-머지 알고리즘의 변형으로, 오른쪽 테이블만 완전히 정렬돼요.
RIGHT JOIN과FULL JOIN은ALLstrictness에서만 지원돼요(SEMI,ANTI,ANY,ASOF는 지원되지 않음).partial_merge알고리즘을 사용하면 ClickHouse는 데이터를 정렬해 디스크에 덤프해요. ClickHouse의partial_merge알고리즘은 고전적인 구현과 약간 다르답니다. 먼저 ClickHouse는 오른쪽 테이블을 조인 키 블록으로 정렬하고 정렬된 블록에 대한 min-max 인덱스를 만들어요. 그런 다음 왼쪽 테이블의 일부를join key로 정렬하고 오른쪽 테이블 위에서 조인해요. min-max 인덱스는 불필요한 오른쪽 테이블 블록을 건너뛰는 데도 사용돼요. -
directdirect(중첩 루프라고도 함) 알고리즘은 왼쪽 테이블의 행을 키로 사용해 오른쪽 테이블에서 조회를 수행해요. Dictionary, EmbeddedRocksDB, MergeTree 테이블 같은 특수 스토리지에서 지원돼요. MergeTree 테이블의 경우 이 알고리즘은 조인 키 필터를 스토리지 레이어로 직접 푸시해요. 키가 테이블의 프라이머리 키 인덱스를 조회에 사용할 수 있을 때 더 효율적일 수 있고, 그렇지 않으면 왼쪽 테이블 블록마다 오른쪽 테이블의 전체 스캔을 수행해요.INNER와LEFT조인과 다른 조건이 없는 단일 컬럼 동등 조인 키만 지원해요. -
autoauto로 설정하면hash조인이 먼저 시도되고, 메모리 제한이 위반되면 즉석에서 다른 알고리즘으로 전환돼요. -
full_sorting_merge조인 전 조인되는 테이블의 전체 정렬을 하는 정렬-머지 알고리즘이에요.
-
ie_joinON섹션에 조인된 테이블의 표현식 간 부등식 비교(<,<=,>,>=) 두 개가 있는JOIN을 위한 정렬 기반 IEJoin 알고리즘이에요.ALL INNER/LEFT/RIGHT/FULL JOIN과SEMI/ANTILEFT/RIGHT JOIN을 지원해요.목록에서의 위치가 우선순위를 설정해요. 기본값처럼 다른 알고리즘 뒤에 나열되면 IEJoin은 그것들이 적용되지 않을 때만 사용돼요(
ON섹션에 동등 조건이 없을 때). 첫 번째에 나열되면ON섹션에 부등식 조건 두 개가 있을 때마다 사용돼요. 나머지 조건(동등 조건 포함)은ALL INNER JOIN에 대해 조인 결과에 대한 필터로 적용되고, 다른 kind는 일치에 영향을 주는 잔여 조건으로 오퍼레이터 안에서 평가돼요.ON섹션에 두 개 이상의 적격 부등식 조건이 있으면 알고리즘이 사용하는 두 개는 컬럼 min/max 통계의 추정 선택도로 선택돼요(Column statistics의basic타입 참조). 추정치를 사용할 수 없으면(통계가 없거나use_statistics가 비활성화), 구문 순서상 처음 두 개가 사용돼요. 목록에ie_join이 없으면 부등식 조건만 있는INNER JOIN은 필터가 있는CROSS JOIN으로 실행되고, 다른 kind는 지원되지 않아요.두 입력 모두 조인 전에 메모리에 누적돼요.
max_rows_in_join과max_bytes_in_join은 양쪽의 누적 입력을 함께 제한하고(오른쪽만이 아니라), 오버플로 시 동작은join_overflow_mode로 설정돼요. 오퍼레이터가 누적 입력 위에 만드는 정렬 인덱스는 제한에 계산되지 않아요. 조인 오퍼레이터 자체는 단일 스레드로 실행되고, 입력의 사전-조인 정렬만 병렬화돼요. -
parallel_full_sorting_mergefull_sorting_merge와 같지만, 해시 호환 동등 조인은 조인 키의 해시로 각 독립적 샤드별 머지 조인으로 샤딩돼(max_threads까지) 병렬로 실행돼요. 단일 머지 조인 대신 말이죠. 이렇게 하면 머지 조인의 낮고 스트리밍식 메모리 사용을 유지하면서 모든 스레드를 사용하며, 결과는 정렬되지 않아요.조인 키에 의한 해시 샤딩은 해시가 머지-조인 비교와 일치하는 키 타입의 일반 동등 조인에만, 그리고 양쪽이 이미 정렬되지 않은 경우에만 적용돼요. 다음 경우에는 건너뛰어요:
ASOF조인과 부동소수점 /JSON/Object/Dynamic키 타입: 그 해시는 머지-조인 비교와 일관되지 않아서 같은 키가 다른 샤드에 들어갈 수 있어요.- 이미 정렬된 측(순서대로 읽는 MergeTree 읽기 또는 사전 정렬된 입력): 샤드별 머지로의 순서 보존 흩뿌리기는 파이프라인을 데드락시킬 수 있어요. 대신 순서대로 읽기와 그
read_in_order_use_virtual_row최적화가 유지돼요. - initiator가 분산 계획(
make_distributed_plan)을 빌드하는 동안: 흩뿌린 정렬은 원격 실행에 직렬화할 수 없기 때문이에요. 로컬 단일 프래그먼트 계획과 워커별 프래그먼트는 그 설정이 비활성화된 상태로 재최적화되므로 여전히 샤딩될 수 있어요.
건너뛰는 것은 이 재작성만 비활성화하지, 일반적으로 병렬 처리를 비활성화하는 것은 아니에요. 조인은 단일
full_sorting_merge로 실행되고, 순서대로 읽는 MergeTree 측은query_plan_join_shard_by_pk_ranges가 활성화되면 프라이머리 키 범위로 소스에서 여전히 샤딩될 수 있어요(그 비교는 조인이 사용하는 것과 같으므로 같은 키가 함께 유지됨). -
prefer_partial_mergeClickHouse는 가능하면 항상
partial_merge조인을 사용하려 하고, 그렇지 않으면hash를 사용해요. Deprecated,partial_merge,hash와 같아요. -
default(deprecated)레거시 값이니 더 이상 사용하지 마세요.
direct,hash와 같아요. 즉 direct 조인과 hash 조인을 순서대로 시도해요.
join_any_take_last_row
오른쪽 테이블이 키에 대해 둘 이상의 일치 행을 가질 때 ANY strictness의 조인 동작을 변경해요.
이 설정은 Join 엔진 테이블과 해시 기반 조인 알고리즘에 적용돼요. 조인이 병렬로 빌드되면 행의 순서가 비결정적일 수 있어요. 즉 join_any_take_last_row = 1은 ANY JOIN 쿼리에 대해 비결정적 행을 반환할 수 있어요.
가능한 값:
- 0 — 오른쪽 테이블에 둘 이상의 일치 행이 있으면 처음 발견된 것만 조인돼요.
- 1 — 오른쪽 테이블에 둘 이상의 일치 행이 있으면 마지막으로 발견된 것만 조인돼요.
함께 보기:
- JOIN 절
- Join 테이블 엔진
join_default_strictness
join_default_strictness
JOIN 절의 기본 strictness를 설정해요.
가능한 값:
ALL— 오른쪽 테이블에 여러 일치 행이 있으면 ClickHouse가 일치하는 행에서 카테시안 곱을 만들어요. 이것이 표준 SQL의 정상JOIN동작이에요.ANY— 오른쪽 테이블에 여러 일치 행이 있으면 처음 발견된 것만 조인돼요. 오른쪽 테이블에 일치 행이 하나만 있으면ANY와ALL의 결과는 같아요.ASOF— 불확실한 일치가 있는 시퀀스 조인용.Empty string— 쿼리에ALL또는ANY가 지정되지 않으면 ClickHouse가 예외를 던져요.
join_on_disk_max_files_to_merge
MergeJoin 작업이 디스크에서 실행될 때 병렬 정렬에 허용되는 파일 수를 제한해요. 값이 클수록 더 많은 RAM이 사용되고 더 적은 디스크 I/O가 필요해요.
가능한 값: 2부터 시작하는 양의 정수.
join_output_by_rowlist_perkey_rows_threshold
해시 조인에서 행 목록으로 출력할지 여부를 결정하는 오른쪽 테이블의 키당 평균 행 수의 하한이에요.
join_overflow_mode
조인이 다음 한도 중 하나에 도달했을 때 ClickHouse가 수행하는 동작을 정의해요:
max_bytes_in_joinmax_rows_in_join
모든 해시 기반 join_algorithm 값은 디스크로 spill하는 것을 포함해 이 설정을 존중해요. 한도에 도달하면 spill을 트리거하는 대신 쿼리를 중지해요. 예외는 legacy_join_size_limits_trigger_spilling이에요. 이것이 켜져 있으면 이미 디스크에서 실행 중인 조인 부분이 이 설정에 따라 행동하는 대신 더 spill해요.
ie_join도 양쪽에서 누적하는 입력에 대해 이 설정을 존중해요. partial_merge는 여전히 전략 전환으로 한도를 처리해요. join_algorithm 참조.
가능한 값:
THROW— ClickHouse가 예외를 던지고 쿼리를 중지해요.BREAK— ClickHouse가 쿼리를 중지하고 예외를 던지지 않아요.
기본값: THROW.
함께 보기:
- JOIN 절
- Join 테이블 엔진
join_use_nulls
JOIN 동작의 타입을 설정해요. 테이블을 병합할 때 빈 셀이 나타날 수 있어요. ClickHouse는 이 설정에 따라 그것들을 다르게 채워요.
가능한 값:
- 0 — 빈 셀이 해당 필드 타입의 기본값으로 채워져요.
- 1 —
JOIN이 표준 SQL과 같은 방식으로 동작해요. 해당 필드의 타입이 Nullable로 변환되고 빈 셀이 NULL로 채워져요.