동기 모드에서 작은 batch를 많이 전송하는 것은 권장되지 않습니다. 이 경우 많은 파트가 생성되어 쿼리 성능이 저하되고 “too many part” 오류가 발생할 수 있습니다.
async_insert 설정으로 제어됩니다.
비동기 삽입은 HTTP와 네이티브 TCP 인터페이스 모두에서 지원됩니다.
활성화되면 (async_insert = 1) 삽입은 버퍼링되며, 다음 플러시 조건 중 하나를 충족할 때만 디스크에 기록됩니다:
- 버퍼가 지정된 데이터 크기에 도달하는 경우(
async_insert_max_data_size, 기본값 100 MiB) - 시간 임계값이 경과하는 경우(
async_insert_busy_timeout_ms, 기본값 200 ms 또는 Cloud에서는 1000 ms) - 삽입 쿼리 수가 최대치까지 누적되는 경우(
async_insert_max_query_number, 기본값 450)
반환 모드 선택
wait_for_async_insert 설정으로 더 세밀하게 조정할 수 있습니다.
이 값을 1(기본값)로 설정하면 ClickHouse는 데이터가 디스크에 성공적으로 플러시된 후에만 삽입을 승인합니다. 이렇게 하면 강력한 내구성이 보장되고 오류 처리도 단순해집니다. 즉, 플러시 중 문제가 발생하면 해당 오류가 클라이언트에 반환됩니다. 이 모드는 대부분의 프로덕션 환경, 특히 삽입 실패를 신뢰성 있게 추적해야 하는 경우에 권장됩니다.
벤치마크에 따르면 적응형 삽입과 안정적인 파트 생성 방식 덕분에 200개 클라이언트든 500개 클라이언트든 높은 동시성에서도 우수하게 확장됩니다.
wait_for_async_insert = 0으로 설정하면 “fire-and-forget” 모드가 활성화됩니다. 이 경우 서버는 데이터가 저장소에 기록될 때까지 기다리지 않고, 데이터가 버퍼에 적재되는 즉시 삽입을 승인합니다.
이 방식은 매우 낮은 지연 시간의 삽입과 최대 처리량을 제공하므로, 빠르게 유입되지만 중요도가 낮은 데이터에 적합합니다. 하지만 그만큼의 대가도 따릅니다. 데이터가 영구 저장된다는 보장이 없고, 오류는 플러시 중에만 드러나며, 실패한 삽입을 위한 데드 레터 큐도 없습니다. 따라서 실패를 추적하려면 사후에 서버 로그와 시스템 테이블을 점검해야 합니다. 워크로드가 데이터 손실을 감수할 수 있는 경우에만 이 모드를 사용하십시오.
벤치마크에서도 버퍼 플러시가 드문 경우(예: 30초마다) 파트 수가 크게 줄고 CPU 사용량도 낮아지는 것으로 나타났지만, 조용한 실패의 위험은 여전히 남아 있습니다.
비동기 삽입을 사용하는 경우 async_insert=1,wait_for_async_insert=1을 사용할 것을 강력히 권장합니다. wait_for_async_insert=0을 사용하는 것은 매우 위험합니다. 오류가 발생해도 INSERT 클라이언트가 이를 인지하지 못할 수 있으며, ClickHouse 서버가 서비스의 신뢰성을 보장하기 위해 쓰기 속도를 늦추고 백프레셔를 형성해야 하는 상황에서도 클라이언트가 계속 빠르게 쓰기를 수행해 잠재적인 과부하를 초래할 수 있기 때문입니다.
적응형 비동기 삽입
async_insert_use_adaptive_busy_timeout)을 사용합니다. 고정된 플러시 인터벌 대신, 유입되는 데이터 속도에 따라 타임아웃이 최소값(async_insert_busy_timeout_min_ms, 기본값 50 ms)과 최대값(async_insert_busy_timeout_max_ms, 기본값 200 ms, Cloud에서는 1000 ms) 사이에서 동적으로 조정됩니다.
데이터가 자주 들어오면 더 빨리 플러시되어 엔드 투 엔드 지연 시간이 줄어들도록 타임아웃이 최소값에 가깝게 유지됩니다. 데이터가 희소하면 더 큰 배치를 모을 수 있도록 최대값 쪽으로 증가합니다. 이는 특히 기본 모드(wait_for_async_insert=1)에서 유용합니다. 고정된 높은 타임아웃을 사용하면 플러시할 데이터가 이미 준비되어 있어도 클라이언트가 전체 인터벌 동안 대기해야 하기 때문입니다.
오류 처리
wait_for_async_insert=1)에서는 오류가 클라이언트에 반환됩니다. fire-and-forget 모드에서는 오류가 서버 로그와 system.asynchronous_inserts 테이블에 기록됩니다.
각 플러시는 버퍼 내에서 서로 다른 각 파티션 키 값마다 최소 1개의 파트를 생성합니다. 파티션 키가 없는 테이블에서도 버퍼링된 데이터가 max_insert_block_size (기본값 약 100만 행)를 초과하면 한 번의 플러시로 여러 파트가 생성될 수 있습니다.
비동기 삽입을 사용하더라도 파티셔닝 키의 cardinality가 높으면 “too many parts” 오류가 발생할 수 있습니다.
중복 제거 및 안정성
비동기 삽입 활성화
-
사용자 수준에서 비동기 삽입을 활성화할 수 있습니다. 이 예시에서는 사용자
default를 사용합니다. 다른 사용자를 생성했다면 해당 사용자 이름으로 바꾸십시오. -
삽입 쿼리의 SETTINGS 절을 사용해 비동기 삽입 설정을 지정할 수 있습니다.
-
ClickHouse 프로그래밍 언어 클라이언트를 사용할 때는 연결 매개변수로 비동기 삽입 설정을 지정할 수도 있습니다.
예를 들어, ClickHouse Cloud에 연결할 때 ClickHouse Java JDBC 드라이버를 사용한다면 JDBC 연결 문자열에서 다음과 같이 설정할 수 있습니다.
비동기 삽입은
INSERT INTO ... SELECT 쿼리에는 적용되지 않습니다. 삽입에 SELECT 절이 포함된 경우에는 async_insert 설정과 관계없이 쿼리가 항상 동기식으로 실행됩니다.종료 시 버퍼 플러시
Buffer 테이블과의 비교
- DDL 변경이 필요하지 않습니다. 비동기 삽입은 투명하게 동작하므로 추가 테이블을 생성할 필요 없이 설정만 활성화하면 됩니다.
- 쿼리 shape별 버퍼링. 비동기 삽입은 고유한 각 쿼리 shape와 설정 조합마다 별도의 버퍼를 유지하므로 세분화된 플러시 정책을 적용할 수 있습니다. Buffer 테이블은 대상 테이블마다 단일 버퍼를 사용합니다.
- 내구성. 기본 모드(
wait_for_async_insert=1)에서는 클라이언트가 확인 응답을 받기 전에 데이터가 디스크에 기록된 것이 보장됩니다. Buffer 테이블은 fire-and-forget 방식으로 동작하므로 장애가 발생하면 버퍼링된 데이터가 손실됩니다. - 클러스터 동작. 클러스터에서는 비동기 삽입 버퍼가 노드별로 유지됩니다. Buffer 테이블은 각 노드에 명시적으로 생성해야 합니다.