백그라운드 쿼리

백그라운드 쿼리 (Background queries)

백그라운드 쿼리는 run_query_in_background=1을 설정해 클라이언트 세션과 독립적으로 실행되는 쿼리를 제출할 수 있게 해 줍니다. 쿼리 실행을 클라이언트 네트워크 연결에서 분리해 클라이언트 연결 끊김이나 일시적 네트워크 실패에도 완전히 견딥니다.

출처: 문서

본문

개요 (Overview)

백그라운드 쿼리는 run_query_in_background=1을 설정해 클라이언트 세션과 독립적으로 실행되는 쿼리를 제출할 수 있게 해 줍니다. 제출되면 ClickHouse 서버는 클라이언트에 즉시 응답하고, 쿼리는 서버 쪽에서 성공 또는 실패까지 계속 실행됩니다.

쿼리 실행을 클라이언트 네트워크 연결에서 분리함으로써 백그라운드 작업은 클라이언트 측 연결 끊김이나 일시적 네트워크 실패에 완전히 견딥니다.

백그라운드 쿼리는 주로 INSERT ... SELECT, CREATE TABLE ... AS SELECT, CREATE MATERIALIZED VIEW ... POPULATE, OPTIMIZE TABLE ... FINAL 같은 장기 실행 연산을 위한 것입니다. 이것들은 클라이언트 연결이 끊겨도 멈추지 않아야 합니다.

모든 쿼리가 연결에서 분리될 수 있는 것은 아닙니다. 대신 거부되는 요청은 "지원되지 않는 쿼리 형태"를 참고하세요.

백그라운드 쿼리의 결과는 버려집니다. 나중에 검색하거나 첨부할 수 없습니다. 쿼리의 query_id를 사용해 실행 중에는 system.processes에서, 완료 후에는 system.query_log에서 모니터링하세요.

백그라운드 쿼리는 서버 재시작을 견디지 못합니다. 서버 종료 동작은 shutdown_wait_unfinished_queriesshutdown_wait_unfinished로 제어됩니다.

지원되지 않는 쿼리 형태

백그라운드 쿼리는 제출한 연결보다 오래 살아남으므로, 서버는 쿼리를 수용하는 순간 실행에 필요한 모든 것을 이미 갖고 있어야 합니다. 이 조건을 만족하지 못하는 요청은 제출 연결에서 동기적으로 거부되고 쿼리는 시작되지 않습니다.

연결을 통해 스트리밍되는 데이터

서버가 쿼리 디스패치 후에도 제출 연결에서 데이터를 읽어야 한다면 INSERT가 거부됩니다.

이것은 INSERT ... FORMAT ...input을 통해 읽는 쿼리 모두에 영향을 줄 수 있습니다. 그런 요청은 A query whose data streams over the connection cannot be run in the background와 함께 거부됩니다:

-- Rejected over the native protocol: the client sends the data separately
INSERT INTO target_table FORMAT TSV
INSERT INTO target_table SELECT * FROM input('n UInt64') FORMAT TSV

-- Accepted: the server produces the data itself
INSERT INTO target_table SELECT number FROM numbers(1000000)

clickhouse-clientINSERT ... FORMAT ...의 데이터를 별도의 패킷으로 보내므로, 이 형태는 네이티브 프로토콜에서 백그라운드로 실행될 수 없습니다.

HTTP에서는 완전한 쿼리와 그 데이터가 max_query_size에 제한되는 초기 파싱 버퍼에 들어맞을 때 두 형태 모두 수용될 수 있습니다.

여기에는 input을 통해 인라인 페이로드를 읽는 HTTP 쿼리도 포함됩니다. 더 큰 본문은 버퍼링된 쿼리 텍스트를 넘어 계속 스트리밍되며 거부됩니다.

그 크기 경계에 의존하지 마세요. 백그라운드로 로드해야 하는 데이터에는 INSERT ... SELECTurl, s3 같은 테이블 함수를 사용하세요.

기타 거부되는 요청

요청 오류
SET run_query_in_background = 1 run_query_in_background cannot be changed with SET, because it must be requested per query
명시적 트랜잭션 안의 쿼리 Background queries inside transactions are not supported
implicit_transaction = 1을 가진 쿼리 Background queries with 'implicit_transaction' are not supported
예를 들어 clickhouse-client --query_kind secondary_query로 요청된 2차 쿼리 run_query_in_background cannot be used for a secondary query
Complete 외의 쿼리 처리 단계(예: clickhouse-client --stage with_mergeable_state) run_query_in_background cannot be used with the WithMergeableState query processing stage
저장 정의를 담은 CREATE/ATTACH의 SETTINGS 절(클라이언트가 그 절을 해석되지 않은 채 서버로 보내므로) run_query_in_background cannot be changed in the SETTINGS clause of this particular query

이 설정은 분산 쿼리의 2차 쿼리로는 절대 전파되지 않습니다: 백그라운드 분산 INSERT는 백그라운드 초기 쿼리 안에서 샤드별 쿼리를 포그라운드로 실행합니다.

백그라운드 쿼리 제출

네이티브 TCP 프로토콜

clickhouse-client에서는 run_query_in_background를 명령줄 설정으로 전달하세요:

clickhouse-client --echo-query-id --run_query_in_background=1 \
  -q "INSERT INTO target_table SELECT number FROM numbers(1000000)"

인라인 SETTINGS 절을 사용할 수도 있습니다:

clickhouse-client --echo-query-id \
  -q "INSERT INTO target_table SELECT number FROM numbers(1000000) SETTINGS run_query_in_background=1"

네이티브 프로토콜은 쿼리 설정을 SQL 텍스트와 별도로 운반합니다. clickhouse-client는 대부분의 인라인 쿼리 설정을 파싱해 이 설정 섹션으로 보냅니다.

네이티브 프로토콜 드라이버는 대신 per-query 설정 맵에 run_query_in_background를 전달해 SQL 텍스트를 그대로 둘 수 있습니다.

네이티브 프로토콜은 서버 생성 query_id를 반환하지 않습니다. 네이티브 클라이언트는 고유 ID를 생성해 쿼리와 함께 보내야 합니다.

clickhouse-client --echo-query-id는 이를 수행하고 쿼리 제출 전에 ID를 출력합니다:

Query id: 6b57dffd-8aac-4be5-b331-fa8b2e70227e

HTTP 프로토콜

HTTP 요청에서는 run_query_in_background를 URL 파라미터로 전달하세요:

curl -sS -D - -o /dev/null \
  'http://localhost:8123/?run_query_in_background=1' \
  --data-binary 'INSERT INTO target_table SELECT number FROM numbers(1000000)'

응답은 생성된 쿼리 ID를 X-ClickHouse-Query-Id 헤더에 담습니다:

X-ClickHouse-Query-Id: 689d4147-7531-46ee-b74e-8dced676b397

그 헤더는 응답을 읽는 클라이언트에게만 도달합니다. 응답 도착에 의존하지 않는 쿼리 핸들이 필요하면 대신 자신의 query_id를 URL 파라미터로 보내세요.

그러면 클라이언트는 요청 전에 ID를 알고, 응답을 결코 보지 못해도 수용 노드에서 쿼리를 모니터링하거나 KILL할 수 있습니다:

curl -sS 'http://localhost:8123/?run_query_in_background=1&query_id=nightly_load_2026_09_03' \
  --data-binary 'INSERT INTO target_table SELECT number FROM numbers(1000000)'

네이티브 프로토콜과 달리 HTTP는 인라인 SQL SETTINGS 절로는 백그라운드 실행을 활성화할 수 없습니다:

curl 'http://localhost:8123/' \
  --data-binary 'INSERT INTO target_table SELECT number FROM numbers(1000000) SETTINGS run_query_in_background=1'

이 요청은 BAD_ARGUMENTS 예외를 반환합니다. HTTP 핸들러는 요청 본문이 파싱되기 전에 분리된 쿼리 컨텍스트를 만들지 결정해야 합니다.

설정을 URL로 전달하거나 사용자·프로필 수준에서 구성하세요.

실행 모니터링

query_id를 사용해 쿼리가 현재 실행 중인지 확인하세요:

SELECT
    query_id,
    elapsed,
    query
FROM system.processes
WHERE query_id = '6b57dffd-8aac-4be5-b331-fa8b2e70227e';

쿼리가 끝난 후에는 system.query_log에서 최종 상태를 확인하세요:

SELECT
    type,
    query_duration_ms,
    exception_code,
    exception
FROM system.query_log
WHERE query_id = '6b57dffd-8aac-4be5-b331-fa8b2e70227e'
  AND type IN ('QueryFinish', 'ExceptionBeforeStart', 'ExceptionWhileProcessing')
ORDER BY event_time_microseconds DESC
LIMIT 1;

백그라운드 실행이 나중에 실패해도 제출 요청은 성공할 수 있습니다. 그 경우 예외는 원래 연결로 반환되는 대신 system.query_log에 기록됩니다.

system.processes, system.query_log, KILL QUERY는 노드 로컬입니다: 각각 자신에게 응답한 서버의 쿼리만 봅니다. 백그라운드 쿼리는 그걸 수용한 서버에 속하는데, 로드 밸런서를 통해 다음 요청이 도달하는 서버와 같지 않을 수 있습니다. 전체 클러스터를 읽으세요:

SELECT hostName(), query_id, elapsed, query
FROM clusterAllReplicas(my_cluster, system.processes)
WHERE query_id = '6b57dffd-8aac-4be5-b331-fa8b2e70227e';

system.query_log에도 같은 것이 적용되며, 취소에도 클러스터 전체 형태가 필요합니다:

KILL QUERY ON CLUSTER my_cluster WHERE query_id = '6b57dffd-8aac-4be5-b331-fa8b2e70227e';

쿼리 로그 플러시 지연

엔트리는 system.query_log에 나타나기 전에 버퍼링됩니다. 자체 관리 ClickHouse에서 예제 서버 구성은 query_log.flush_interval_milliseconds7500으로 설정합니다.

ClickHouse Cloud 엔트리는 나타나는 데 최대 30초가 걸릴 수 있습니다. 짧게 실행되는 백그라운드 쿼리를 모니터링할 때 이 지연을 고려하세요.

자체 관리 서버에서 충분한 권한이 있는 사용자는 쿼리 로그를 강제로 플러시할 수 있습니다. 다른 시스템 로그를 건드리지 않도록 로그를 명시적으로 지정하세요:

SYSTEM FLUSH LOGS query_log;

플러시는 문장을 받는 서버에서 일어납니다.

백그라운드 쿼리는 그걸 수용한 서버에서 추적되는데, 이것은 현재 세션이 연결된 서버와 같지 않을 수 있습니다. 따라서 클러스터에서는 쿼리를 조회하기 전에 모든 곳을 플러시하세요:

SYSTEM FLUSH LOGS ON CLUSTER my_cluster query_log;

클러스터 전체 플러시는 각 서버가 자신의 버퍼링된 엔트리를 쓰도록 할 뿐입니다. 다른 서버의 system.query_log를 로컬에서 볼 수 있게 만들지는 않으므로, 여전히 "실행 모니터링"에서 설명한 대로 clusterAllReplicas를 통해 로그를 읽으세요.

더 알아보기 (Learn more)