ClickHouse 사용 및 테이블 설계 입문
GitLab v19.5요약
소개 페이지는 ClickHouse를 개괄적으로 파악하기에 좋습니다. ClickHouse는 PostgreSQL 같은 전통적인 OLTP(online transaction processing) 데이터베이스와 여러 면에서 다릅니다.
PostgreSQL과의 차이점#
소개 페이지는 ClickHouse를 개괄적으로 파악하기에 좋습니다.
ClickHouse는 PostgreSQL 같은 전통적인 OLTP(online transaction processing) 데이터베이스와 여러 면에서 다릅니다. 기반 아키텍처가 다소 다르고, 처리 과정이 전통적인 데이터베이스보다 CPU에 훨씬 많이 의존합니다. ClickHouse는 불변성을 핵심 요소로 삼는 로그 중심 데이터베이스입니다. 이런 접근 방식의 장점은 잘 정리되어 있으며, 자세한 내용은 The rise of immutable data stores를 참고합니다. 다만 그만큼 업데이트는 훨씬 어렵습니다. UPDATE/DELETE를 지원하는 작업은 ClickHouse 문서를 참고합니다. 이런 작업은 자주 수행하지 않는 것을 전제로 한다는 점이 눈에 띕니다.
이 차이는 테이블을 설계할 때 중요합니다. 다음 둘 중 하나여야 합니다.
- 업데이트가 필요하지 않습니다(가장 좋은 경우)
- 업데이트가 필요하더라도 쿼리 실행 중에는 수행하지 않습니다.
ACID 호환성#
ClickHouse의 트랜잭션 지원은 조금 다릅니다. 보장 범위는 특정 테이블에 삽입된 데이터 블록 단위까지만 적용됩니다. 자세한 내용은 Transactional (ACID) support 문서를 참고합니다.
여러 테이블에 걸친 트랜잭션 지원은 구체화 뷰에서만 다루어지므로, 한 번의 쓰기에서 여러 건을 삽입하는 방식은 피합니다.
ClickHouse는 분석 쿼리를 최고 수준으로 지원하는 데 크게 치우쳐 있습니다. 집계 같은 작업이 매우 빠르고, 이 역량을 보강하는 기능도 여럿 제공합니다. ClickHouse는 집계의 세부 동작을 다루는 좋은 블로그 글도 제공합니다.
기본 인덱스, 정렬 인덱스, 딕셔너리#
ClickHouse의 인덱스를 이해하려면 "A practical introduction to primary indexes in ClickHouse"를 읽어 보기를 권장합니다.
특히 ClickHouse의 데이터베이스 인덱스 설계가 PostgreSQL 같은 트랜잭션 데이터베이스와 어떻게 다른지 확인합니다.
기본 인덱스 설계는 쿼리 성능에 매우 큰 영향을 주므로 신중하게 정해야 합니다. 전체 데이터 스캔은 시간이 오래 걸릴 수밖에 없으므로 거의 모든 쿼리가 기본 인덱스에 의존해야 합니다.
MergeTree 테이블 엔진(ClickHouse의 기본 테이블 엔진)에서 인덱스가 쿼리 성능에 어떤 영향을 주는지는 primary keys and indexes in queries 문서를 참고합니다.
ClickHouse의 보조 인덱스는 다른 시스템의 보조 인덱스와 다릅니다. 데이터 블록을 건너뛰는 데 쓰이므로 데이터 스키핑 인덱스라고도 합니다. data-skipping indexes 문서를 참고합니다.
ClickHouse는 외부 인덱스로 사용할 수 있는 "딕셔너리"도 제공합니다. 딕셔너리는 메모리에서 로드되며 쿼리 실행 시점에 값을 조회하는 데 사용할 수 있습니다.
데이터 유형과 파티셔닝#
ClickHouse는 SQL 호환 데이터 유형과 함께 다음과 같은 몇 가지 특수 데이터 유형을 제공합니다.
LowCardinality- UUID
- Maps
- Nested. 칼럼 안에 테이블을 두는 형태를 흉내 낸다는 점이 특징입니다.
테이블을 설계할 때 가장 먼저 마주하는 핵심 설계 요소는 파티셔닝 키입니다. 파티션은 임의의 식으로 정할 수 있지만 보통은 월, 일, 주 같은 기간을 사용합니다. ClickHouse는 가장 작은 파티션 집합을 사용해 읽는 데이터를 최소화하는 방식을 최대한 적용합니다.
참고 자료:
샤딩과 복제#
샤딩은 데이터를 여러 ClickHouse 노드로 나누어 처리량을 높이고 지연 시간을 줄이는 기능입니다. 샤딩 기능은 로컬 테이블을 기반으로 하는 분산 엔진을 사용합니다. 분산 엔진은 데이터를 저장하지 않는 "가상" 테이블이며, 데이터를 삽입하고 조회하는 인터페이스로 사용합니다.
ClickHouse 문서와 복제 및 샤딩 절을 참고합니다. ClickHouse는 합의를 유지하기 위해 Zookeeper를 사용하거나, ClickHouse Keeper라는 자체 호환 API 컴포넌트를 사용할 수 있습니다.
노드를 구성한 뒤에는 클라이언트에서 노드가 보이지 않게 되며, 쓰기와 읽기 쿼리를 어느 노드로든 보낼 수 있습니다.
대부분의 경우 클러스터는 고정된 수의 노드(샤드)로 시작합니다. 샤드 재분배는 운영 부담이 크고 철저한 테스트가 필요합니다.
복제는 MergeTree 테이블 엔진에서 지원하며, 정의 방법은 문서의 복제 절을 참고합니다. ClickHouse는 쿼럼에 참여하는 노드를 추적하기 위해 분산 조정 컴포넌트(Zookeeper 또는 ClickHouse Keeper)에 의존합니다. 복제는 비동기이며 다중 리더 방식입니다. 삽입은 어느 노드로든 보낼 수 있고, 다른 노드에는 약간의 지연을 두고 반영됩니다. 필요하다면 특정 노드에 고정하는 방식으로 읽기가 가장 최근에 기록된 데이터를 보도록 할 수 있습니다.
구체화 뷰#
ClickHouse를 특징짓는 기능 중 하나가 구체화 뷰입니다. 기능상으로는 ClickHouse의 삽입 트리거에 가깝습니다.
동작 방식을 더 잘 이해하려면 공식 문서의 뷰 절을 읽어 보기를 권장합니다.
문서에서 인용합니다.
ClickHouse의 구체화 뷰는 삽입 트리거에 가깝게 구현되어 있습니다. 뷰 쿼리에 집계가 있으면 새로 삽입된 데이터 배치에만 적용됩니다. 원본 테이블의 기존 데이터에 대한 변경(업데이트, 삭제, 파티션 드롭 등)은 구체화 뷰에 반영되지 않습니다.
안전하고 합리적인 기본값#
ClickHouse 인스턴스는 다음 보안 권장 사항을 따라야 합니다.
사용자#
파일: users.xml, config.xml.
| 주제 | 보안 요구 사항 | 이유 |
|---|---|---|
user_name/password |
사용자 이름은 비워 둘 수 없습니다. 비밀번호는 password_sha256_hex를 사용해야 하며 비워 둘 수 없습니다. |
plaintext와 password_double_sha1_hex는 안전하지 않습니다. 사용자 이름을 지정하지 않으면 비밀번호 없이 default가 사용됩니다. |
access_management |
서버 설정 파일 users.xml과 config.xml을 사용합니다. SQL 기반 워크플로는 피합니다. |
SQL 기반 워크플로는 최소 한 명의 사용자가 access_management를 갖는다는 뜻이며, 설정 파일을 쓰면 이를 피할 수 있습니다. "두 설정 방식으로 같은 액세스 엔터티를 동시에 관리할 수는 없습니다."라는 점을 고려하면 설정 파일이 감사와 모니터링에도 더 수월합니다. |
user_name/networks |
<ip>, <host>, <host_regexp> 중 최소 하나는 설정해야 합니다. 모든 네트워크에 액세스를 여는 <ip>::/0</ip>는 사용하지 않습니다. |
네트워크 통제입니다. (Trust cautiously 원칙) |
user_name/profile |
프로필을 사용해 여러 사용자에게 비슷한 속성을 지정하고 (사용자 인터페이스에서) 제한을 설정합니다. | 최소 권한 원칙과 제한입니다. |
user_name/quota |
가능한 경우 사용자에게 할당량을 설정합니다. | 일정 기간의 리소스 사용을 제한하거나 사용량을 추적합니다. |
user_name/databases |
데이터 액세스를 제한하고, 전체 액세스를 가진 사용자를 두지 않습니다. | 최소 권한 원칙입니다. |
네트워크#
파일: config.xml
| 주제 | 보안 요구 사항 | 이유 |
|---|---|---|
mysql_port |
꼭 필요한 경우가 아니면 MySQL 액세스를 비활성화합니다.<!-- <mysql_port>9004</mysql_port> -->. |
불필요한 포트와 기능 노출을 차단합니다. (Defense in depth 원칙) |
postgresql_port |
꼭 필요한 경우가 아니면 PostgreSQL 액세스를 비활성화합니다.<!-- <mysql_port>9005</mysql_port> --> |
불필요한 포트와 기능 노출을 차단합니다. (Defense in depth 원칙) |
http_port/https_port & tcp_port/tcp_port_secure |
SSL-TLS를 설정하고 SSL 이 아닌 포트를 비활성화합니다.<!-- <http_port>8123</http_port> --><!-- <tcp_port>9000</tcp_port> -->그리고 보안 포트를 활성화합니다. <https_port>8443</https_port><tcp_port_secure>9440</tcp_port_secure> |
전송 중 데이터를 암호화합니다. (Defense in depth 원칙) |
interserver_http_host |
ClickHouse를 클러스터로 구성했다면 interserver_http_host를 비활성화하고 interserver_https_host (<interserver_https_port>9010</interserver_https_port>)를 사용합니다. |
전송 중 데이터를 암호화합니다. (Defense in depth 원칙) |
스토리지#
| 주제 | 보안 요구 사항 | 이유 |
|---|---|---|
| 권한 | ClickHouse는 기본적으로 clickhouse 사용자로 실행됩니다. root로 실행할 필요는 전혀 없습니다. /etc/clickhouse-server, /var/lib/clickhouse, /var/log/clickhouse-server 폴더에는 최소 권한 원칙을 적용합니다. 이 폴더는 clickhouse 사용자와 그룹 소유여야 하며, 다른 시스템 사용자는 액세스할 수 없어야 합니다. |
기본 비밀번호, 포트, 규칙은 "열린 문"입니다. (Fail securely & use secure defaults 원칙) |
| 암호화 | RED 데이터를 처리한다면 로그와 데이터에 암호화된 스토리지를 사용합니다. Kubernetes에서는 사용하는 StorageClass가 암호화되어 있어야 합니다. GKE와 EKS는 이미 저장 데이터를 모두 암호화합니다. 이 경우 자체 키를 쓰는 편이 가장 좋지만 필수는 아닙니다. | 저장 데이터를 암호화합니다. (Defense in depth) |
로깅#
| 주제 | 보안 요구 사항 | 이유 |
|---|---|---|
logger |
Log와 errorlog를 정의해야 하며 clickhouse가 쓸 수 있어야 합니다. |
로그가 저장되도록 보장합니다. |
| SIEM | GitLab.com에 호스팅한다면 ClickHouse 인스턴스 또는 클러스터가 SIEM으로 로그를 전송해야 합니다(내부 링크). | GitLab은 정보 시스템의 중요 활동을 로그로 남깁니다. |
| 민감 데이터 로깅 | 민감한 데이터가 로그에 남을 수 있다면 쿼리 마스킹 규칙을 사용해야 합니다. 마스킹 규칙 예시를 참고합니다. | 칼럼 수준 암호화를 사용할 수 있는데, 이때 민감한 데이터(키)가 로그로 유출될 수 있습니다. |
마스킹 규칙 예시#
<query_masking_rules>
<rule>
<name>hide SSN</name>
<regexp>(^|\D)\d{3}-\d{2}-\d{4}($|\D)</regexp>
<replace>000-00-0000</replace>
</rule>
<rule>
<name>hide encrypt/decrypt arguments</name>
<regexp>
((?:aes_)?(?:encrypt|decrypt)(?:_mysql)?)\s*\(\s*(?:'(?:\\'|.)+'|.*?)\s*\)
</regexp>
<replace>\1(???)</replace>
</rule>
</query_masking_rules>