InfoGrab Docs

ClickHouse 리뷰어 가이드라인

요약

이 페이지는 ClickHouse 리뷰어를 위한 입문 자료와 가이드를 제공합니다. ClickHouse 리뷰어는 ClickHouse OLAP 데이터베이스에 경험이 있는 도메인 전문가입니다. 리뷰어의 책임은 ClickHouse 관련 변경 사항을 검증하고 ClickHouse가 구성될 수 있는 모든 GitLab 환경에서 올바르게 작동하는지 확인하는 것입니다.

이 페이지는 ClickHouse 리뷰어를 위한 입문 자료와 가이드를 제공합니다.

ClickHouse 리뷰어 업무 범위#

ClickHouse 리뷰어는 ClickHouse OLAP 데이터베이스에 경험이 있는 도메인 전문가입니다. ClickHouse와 상호작용하는 애플리케이션 코드가 수정될 때마다 ClickHouse 데이터베이스 리뷰가 필요합니다. 예시는 다음과 같습니다:

  • 새 ClickHouse 마이그레이션 추가.
  • ClickHouse 쿼리를 실행하는 서비스 클래스 변경.
  • 새 ClickHouse 쿼리 도입.

리뷰어의 책임은 ClickHouse 관련 변경 사항을 검증하고 ClickHouse가 구성될 수 있는 모든 GitLab 환경에서 올바르게 작동하는지 확인하는 것입니다.

ClickHouse 리뷰어를 위한 리소스#

일반 가이드라인#

데이터베이스 스키마 일관성 보장#

현재 ClickHouse 데이터베이스 스키마는 단일 main.sql 파일에 저장됩니다. 이 파일은 ActiveRecord 마이그레이션과 유사하게 마이그레이션 실행 시 자동으로 업데이트됩니다.

때로는 main.sql 파일이 머지 리퀘스트에서 업데이트되거나 커밋되지 않아 마이그레이션으로 구축된 스키마와 커밋된 스키마 파일 간에 불일치가 발생할 수 있습니다. 이 문제를 감지하기 위해 CI 잡(clickhouse:check-schema)이 테스트 스테이지 중에 실행됩니다. 이 잡은 새로 구축된 스키마를 main.sql과 비교하고 차이가 있으면 실패합니다.

  • 이 잡은 실패해도 괜찮도록 허용되지 않습니다. 실패하면 MR 파이프라인이 실패합니다.
  • 리뷰어로서 잡 로그를 항상 확인하세요. 실패하는 경우 차이를 신중하게 검사하세요. 공백이 아닌 관련 차이는 MR 작성자와 논의해야 합니다.
  • 거짓 양성의 경우(예: ClickHouse 버전 불일치로 인한 차이), MR에 pipeline:skip-check-clickhouse-schema 라벨을 추가하여 이 검사를 건너뜁니다.

정당한 스키마 차이를 해결하기 위해 작성자는 모든 마이그레이션이 실행되도록 하고 스키마를 덤프할 수 있습니다:

bundle exec rake gitlab:clickhouse:migrate; bundle exec rake gitlab:clickhouse:schema:dump

데이터베이스 쿼리 리뷰#

GitLab의 ClickHouse 쿼리는 두 가지 방법으로 작성할 수 있습니다:

  1. Raw SQL 쿼리
  2. QueryBuilder – ActiveRecord와 유사한 추상화(문서)

Raw SQL 쿼리를 리뷰할 때 변수 보간에 주의를 기울이세요:

  • 권장: 민감한 데이터가 로그에 기록되지 않도록 변수는 ClickHouse의 플레이스홀더 구문을 사용해야 합니다:

    sql = 'SELECT * FROM events WHERE id > {min_id:UInt64}'
    
  • 고정 문자열 보간(예: 문자열이 Ruby 상수에 할당되는 경우)은 SQL 인젝션 또는 잘못된 형식의 쿼리를 방지하기 위해 항상 적절한 인용을 사용해야 합니다:

    SQL = "SELECT * FROM events WHERE type = #{ClickHouse::Client::Quoting.quote('Issue')}"
    

데이터베이스 쿼리 성능 리뷰#

ClickHouse는 대용량 데이터셋을 효율적으로 처리할 수 있지만 복잡한 집계에서도 쿼리 실행을 10초 이내로 유지하는 것을 목표로 합니다. 성능 기대치는 기능 사용량과 데이터셋 크기에 따라 다릅니다.

쿼리를 리뷰할 때:

  1. 코드에서 명확하게 보이지 않는 경우 작성자에게 Raw SQL 제공을 요청합니다.
  2. 테이블 구조를 검토합니다(SHOW CREATE TABLE table_name FORMAT raw) 파티셔닝과 기본 키를 이해하기 위해.
  3. 쿼리 필터가 테이블의 기본 키 또는 파티셔닝 열과 일치하는지 확인합니다.

쿼리 예시:

SELECT count(DISTINCT contributions.author_id) AS contributor_count
FROM (
  SELECT argMax(author_id, contributions.updated_at) AS author_id
  FROM contributions
  WHERE
    startsWith(contributions.path, {namespace_path:String})
    AND contributions.created_at BETWEEN {from:Date} AND {to:Date}
  GROUP BY id
) contributions

이 쿼리는 필터 열(path, created_at)이 기본 키에 포함된 경우 잘 동작합니다:

CREATE TABLE contributions (
  id UInt64,
  path String,
  author_id UInt64,
  target_type LowCardinality(String),
  action UInt8,
  created_at Date,
  updated_at DateTime64(6, 'UTC')
) ENGINE = ReplacingMergeTree
PARTITION BY toYear(created_at)
ORDER BY (path, created_at, author_id, id);

성능 검증 단계:

  • 대표적인 파라미터로 테스트합니다(예: namespace_path='9970/', 1개월 날짜 범위).

  • 쿼리를 실행하고 경과 시간과 읽은 행 수를 기록합니다:

    Elapsed: 0.062s
    Read: 1,111,111 rows (15.55 MB)
    
  • 스캔한 행을 전체 테이블 행과 비교합니다(SELECT COUNT(*) FROM contributions). 제대로 제한된 쿼리는 전체 행의 일부만 읽어야 합니다.

쿼리 플랜 검사:

EXPLAIN indexes=1을 사용하여 필터가 기본 키 인덱스를 사용하는지 확인합니다:

EXPLAIN indexes=1
SELECT count(DISTINCT author_id) FROM contributions ...
FORMAT raw

플랜에서의 발췌:

PrimaryKey
  Keys:
    path
    created_at
  Condition: and((created_at in (-Inf, 20361]), and((created_at in [20332, +Inf)), (path in ['9970/', '99700'))))
  Parts: 11/11
  Granules: 185/72937
  Search Algorithm: generic exclusion search

출력에서 PrimaryKey 섹션을 찾고 Granules 비율을 확인하세요. 예를 들어: 185/72937 그래뉼은 테이블의 작은 부분만 스캔되었음을 의미합니다 - 성능에 이상적입니다.

성능에 대한 논의를 제기할 때:

  • 쿼리가 1000만 행 이상을 스캔하는 경우.
  • 쿼리가 5~10초 실행 시간을 지속적으로 초과하는 경우.
  • 쿼리가 자주 실행될 경우.

성능 검증이 대규모 네임스페이스(예: gitlab-org 또는 gitlab-org/gitlab)의 실제(또는 합성) 데이터를 사용하도록 하세요.

새 구체화된 뷰 리뷰#

구체화된 뷰가 POPULATE 키워드로 생성되었는지 또는 대용량 데이터셋에 대한 백필 마이그레이션이 있는지 확인하세요.

테이블 엔진별 동작#

MergeTree 계열에서 기본 키(즉, ORDER BY)는 정렬/인덱스를 정의하며 고유성 제약이 아닙니다. 동일한 기본 키 값을 가진 행이 공존할 수 있습니다. 수집 파이프라인이 중복 또는 업데이트를 생성할 수 있는 경우 읽기 시간에 처리하거나(또는 버전을 축소하는 엔진을 선택해야 합니다).

MergeTree 엔진#

  • 자동 중복 제거 없음.
  • 데이터가 완전히 추가 전용이고 중복이 발생할 수 없는 경우(예: 불변 이벤트 로그) 사용합니다.
CREATE TABLE events
(
  event_id  UInt64,
  timestamp DateTime,
  user_id   UInt64,
  payload   String
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(timestamp)
ORDER BY (event_id, timestamp);

ReplacingMergeTree#

ReplacingMergeTree는 백그라운드 병합 중에 중복 기본 키를 축소할 수 있습니다(비결정적 타이밍). 병합이 발생할 때까지 읽기에서 여전히 여러 버전이 보일 수 있으므로 쿼리 시간 중복 제거가 권장됩니다.

모범 사례:

  • 버전 열(단조 증가, 일반적으로 DateTime64) 제공.
  • 소프트 삭제를 위한 선택적 삭제 플래그(Bool 유형).
Note

버전 파라미터를 생략하면 병합 후 중복 제거된 행은 임의적입니다.

CREATE TABLE items
(
  id         UInt64,
  name       String,
  status     LowCardinality(String),
  updated_at DateTime64(6), -- acts as the version
  deleted    Bool DEFAULT 0 -- deleted flag for marking a record deleted
)
ENGINE = ReplacingMergeTree(updated_at, deleted)
ORDER BY id

행을 중복 제거하려면 버전 열로 argMax를 사용하고 기본 키로 GROUP BY를 수행하세요:

SELECT *
FROM (
  SELECT
    id,
    argMax(name,       updated_at) AS name,
    argMax(status,     updated_at) AS status,
    argMax(deleted,    updated_at) AS deleted
  FROM items
  GROUP BY id
) AS items
WHERE deleted = false

ClickHouse 콘솔이나 테스트 케이스에서는 FINAL 수정자를 사용할 수 있습니다.

SELECT * FROM items FINAL;
Note

프로덕션 쿼리에서 FINAL을 피하세요. FINAL은 즉석에서 축소/병합을 강제하며 I/O 측면에서 매우 비쌀 수 있습니다. 위에서 언급한 쿼리 시간 중복 제거 패턴을 선호하세요.

ClickHouse 리뷰어 가이드라인

원문 보기
요약

이 페이지는 ClickHouse 리뷰어를 위한 입문 자료와 가이드를 제공합니다. ClickHouse 리뷰어는 ClickHouse OLAP 데이터베이스에 경험이 있는 도메인 전문가입니다. 리뷰어의 책임은 ClickHouse 관련 변경 사항을 검증하고 ClickHouse가 구성될 수 있는 모든 GitLab 환경에서 올바르게 작동하는지 확인하는 것입니다.

이 페이지는 ClickHouse 리뷰어를 위한 입문 자료와 가이드를 제공합니다.

ClickHouse 리뷰어 업무 범위#

ClickHouse 리뷰어는 ClickHouse OLAP 데이터베이스에 경험이 있는 도메인 전문가입니다. ClickHouse와 상호작용하는 애플리케이션 코드가 수정될 때마다 ClickHouse 데이터베이스 리뷰가 필요합니다. 예시는 다음과 같습니다:

  • 새 ClickHouse 마이그레이션 추가.
  • ClickHouse 쿼리를 실행하는 서비스 클래스 변경.
  • 새 ClickHouse 쿼리 도입.

리뷰어의 책임은 ClickHouse 관련 변경 사항을 검증하고 ClickHouse가 구성될 수 있는 모든 GitLab 환경에서 올바르게 작동하는지 확인하는 것입니다.

ClickHouse 리뷰어를 위한 리소스#

일반 가이드라인#

데이터베이스 스키마 일관성 보장#

현재 ClickHouse 데이터베이스 스키마는 단일 main.sql 파일에 저장됩니다. 이 파일은 ActiveRecord 마이그레이션과 유사하게 마이그레이션 실행 시 자동으로 업데이트됩니다.

때로는 main.sql 파일이 머지 리퀘스트에서 업데이트되거나 커밋되지 않아 마이그레이션으로 구축된 스키마와 커밋된 스키마 파일 간에 불일치가 발생할 수 있습니다. 이 문제를 감지하기 위해 CI 잡(clickhouse:check-schema)이 테스트 스테이지 중에 실행됩니다. 이 잡은 새로 구축된 스키마를 main.sql과 비교하고 차이가 있으면 실패합니다.

  • 이 잡은 실패해도 괜찮도록 허용되지 않습니다. 실패하면 MR 파이프라인이 실패합니다.
  • 리뷰어로서 잡 로그를 항상 확인하세요. 실패하는 경우 차이를 신중하게 검사하세요. 공백이 아닌 관련 차이는 MR 작성자와 논의해야 합니다.
  • 거짓 양성의 경우(예: ClickHouse 버전 불일치로 인한 차이), MR에 pipeline:skip-check-clickhouse-schema 라벨을 추가하여 이 검사를 건너뜁니다.

정당한 스키마 차이를 해결하기 위해 작성자는 모든 마이그레이션이 실행되도록 하고 스키마를 덤프할 수 있습니다:

bundle exec rake gitlab:clickhouse:migrate; bundle exec rake gitlab:clickhouse:schema:dump

데이터베이스 쿼리 리뷰#

GitLab의 ClickHouse 쿼리는 두 가지 방법으로 작성할 수 있습니다:

  1. Raw SQL 쿼리
  2. QueryBuilder – ActiveRecord와 유사한 추상화(문서)

Raw SQL 쿼리를 리뷰할 때 변수 보간에 주의를 기울이세요:

  • 권장: 민감한 데이터가 로그에 기록되지 않도록 변수는 ClickHouse의 플레이스홀더 구문을 사용해야 합니다:

    sql = 'SELECT * FROM events WHERE id > {min_id:UInt64}'
    
  • 고정 문자열 보간(예: 문자열이 Ruby 상수에 할당되는 경우)은 SQL 인젝션 또는 잘못된 형식의 쿼리를 방지하기 위해 항상 적절한 인용을 사용해야 합니다:

    SQL = "SELECT * FROM events WHERE type = #{ClickHouse::Client::Quoting.quote('Issue')}"
    

데이터베이스 쿼리 성능 리뷰#

ClickHouse는 대용량 데이터셋을 효율적으로 처리할 수 있지만 복잡한 집계에서도 쿼리 실행을 10초 이내로 유지하는 것을 목표로 합니다. 성능 기대치는 기능 사용량과 데이터셋 크기에 따라 다릅니다.

쿼리를 리뷰할 때:

  1. 코드에서 명확하게 보이지 않는 경우 작성자에게 Raw SQL 제공을 요청합니다.
  2. 테이블 구조를 검토합니다(SHOW CREATE TABLE table_name FORMAT raw) 파티셔닝과 기본 키를 이해하기 위해.
  3. 쿼리 필터가 테이블의 기본 키 또는 파티셔닝 열과 일치하는지 확인합니다.

쿼리 예시:

SELECT count(DISTINCT contributions.author_id) AS contributor_count
FROM (
  SELECT argMax(author_id, contributions.updated_at) AS author_id
  FROM contributions
  WHERE
    startsWith(contributions.path, {namespace_path:String})
    AND contributions.created_at BETWEEN {from:Date} AND {to:Date}
  GROUP BY id
) contributions

이 쿼리는 필터 열(path, created_at)이 기본 키에 포함된 경우 잘 동작합니다:

CREATE TABLE contributions (
  id UInt64,
  path String,
  author_id UInt64,
  target_type LowCardinality(String),
  action UInt8,
  created_at Date,
  updated_at DateTime64(6, 'UTC')
) ENGINE = ReplacingMergeTree
PARTITION BY toYear(created_at)
ORDER BY (path, created_at, author_id, id);

성능 검증 단계:

  • 대표적인 파라미터로 테스트합니다(예: namespace_path='9970/', 1개월 날짜 범위).

  • 쿼리를 실행하고 경과 시간과 읽은 행 수를 기록합니다:

    Elapsed: 0.062s
    Read: 1,111,111 rows (15.55 MB)
    
  • 스캔한 행을 전체 테이블 행과 비교합니다(SELECT COUNT(*) FROM contributions). 제대로 제한된 쿼리는 전체 행의 일부만 읽어야 합니다.

쿼리 플랜 검사:

EXPLAIN indexes=1을 사용하여 필터가 기본 키 인덱스를 사용하는지 확인합니다:

EXPLAIN indexes=1
SELECT count(DISTINCT author_id) FROM contributions ...
FORMAT raw

플랜에서의 발췌:

PrimaryKey
  Keys:
    path
    created_at
  Condition: and((created_at in (-Inf, 20361]), and((created_at in [20332, +Inf)), (path in ['9970/', '99700'))))
  Parts: 11/11
  Granules: 185/72937
  Search Algorithm: generic exclusion search

출력에서 PrimaryKey 섹션을 찾고 Granules 비율을 확인하세요. 예를 들어: 185/72937 그래뉼은 테이블의 작은 부분만 스캔되었음을 의미합니다 - 성능에 이상적입니다.

성능에 대한 논의를 제기할 때:

  • 쿼리가 1000만 행 이상을 스캔하는 경우.
  • 쿼리가 5~10초 실행 시간을 지속적으로 초과하는 경우.
  • 쿼리가 자주 실행될 경우.

성능 검증이 대규모 네임스페이스(예: gitlab-org 또는 gitlab-org/gitlab)의 실제(또는 합성) 데이터를 사용하도록 하세요.

새 구체화된 뷰 리뷰#

구체화된 뷰가 POPULATE 키워드로 생성되었는지 또는 대용량 데이터셋에 대한 백필 마이그레이션이 있는지 확인하세요.

테이블 엔진별 동작#

MergeTree 계열에서 기본 키(즉, ORDER BY)는 정렬/인덱스를 정의하며 고유성 제약이 아닙니다. 동일한 기본 키 값을 가진 행이 공존할 수 있습니다. 수집 파이프라인이 중복 또는 업데이트를 생성할 수 있는 경우 읽기 시간에 처리하거나(또는 버전을 축소하는 엔진을 선택해야 합니다).

MergeTree 엔진#

  • 자동 중복 제거 없음.
  • 데이터가 완전히 추가 전용이고 중복이 발생할 수 없는 경우(예: 불변 이벤트 로그) 사용합니다.
CREATE TABLE events
(
  event_id  UInt64,
  timestamp DateTime,
  user_id   UInt64,
  payload   String
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(timestamp)
ORDER BY (event_id, timestamp);

ReplacingMergeTree#

ReplacingMergeTree는 백그라운드 병합 중에 중복 기본 키를 축소할 수 있습니다(비결정적 타이밍). 병합이 발생할 때까지 읽기에서 여전히 여러 버전이 보일 수 있으므로 쿼리 시간 중복 제거가 권장됩니다.

모범 사례:

  • 버전 열(단조 증가, 일반적으로 DateTime64) 제공.
  • 소프트 삭제를 위한 선택적 삭제 플래그(Bool 유형).
Note

버전 파라미터를 생략하면 병합 후 중복 제거된 행은 임의적입니다.

CREATE TABLE items
(
  id         UInt64,
  name       String,
  status     LowCardinality(String),
  updated_at DateTime64(6), -- acts as the version
  deleted    Bool DEFAULT 0 -- deleted flag for marking a record deleted
)
ENGINE = ReplacingMergeTree(updated_at, deleted)
ORDER BY id

행을 중복 제거하려면 버전 열로 argMax를 사용하고 기본 키로 GROUP BY를 수행하세요:

SELECT *
FROM (
  SELECT
    id,
    argMax(name,       updated_at) AS name,
    argMax(status,     updated_at) AS status,
    argMax(deleted,    updated_at) AS deleted
  FROM items
  GROUP BY id
) AS items
WHERE deleted = false

ClickHouse 콘솔이나 테스트 케이스에서는 FINAL 수정자를 사용할 수 있습니다.

SELECT * FROM items FINAL;
Note

프로덕션 쿼리에서 FINAL을 피하세요. FINAL은 즉석에서 축소/병합을 강제하며 I/O 측면에서 매우 비쌀 수 있습니다. 위에서 언급한 쿼리 시간 중복 제거 패턴을 선호하세요.