ClickHouse 데이터베이스 엔진 및 테이블 엔진 이해

ClickHouse 개요

OLTP(온라인 트랜잭션 처리 시스템)

MySQL과 같은 관계형 데이터베이스는 소량의 데이터 저장 시 빠른 조회 및 분석이 가능합니다. OLTP는 논리적 개념으로, 주로 데이터의 삽입, 삭제, 수정 작업을 중심으로 하는 데이터베이스를 의미합니다. 이 시스템의 데이터는 자주 변경됩니다.

OLAP(온라인 분석 처리 시스템)

데이터가 장기간 유지되며 대용량의 역사적 데이터를 포함하고, 실시간 분석이 가능한 시스템입니다. 삽입, 삭제, 수정 작업은 거의 발생하지 않습니다.

OLAP 시스템 아키텍처 특징

  1. 대부분 읽기 요청으로 구성됨
  2. 데이터는 대량 배치(>1000행)로 업데이트되거나 단일 행 업데이트 없음
  3. 한 번 추가된 데이터는 수정 불가능
  4. 읽기 시 많은 행을 추출하지만 열은 일부만 선택
  5. 넓은 테이블 구조(많은 열 포함)
  6. 쿼리 수는 상대적으로 적음(서버당 초당 수백 회 이하)
  7. 단순 쿼리의 지연 허용 범위 약 50밀리초
  8. 열 데이터는 상대적으로 작음(숫자 및 짧은 문자열)
  9. 단일 쿼리 처리 시 높은 처리량 필요(서버당 초당 수십억 행)
  10. 트랜잭션 필수 아님
  11. 데이터 일관성 요구사항 낮음

테이블 조작

데이터 타입

주의사항:

  1. 테이블 생성 시 데이터 타입은 대소문자를 엄격히 구분해야 함
  2. 테이블 생성 시 반드시 테이블 엔진 지정 필요
  1. 정수 타입
UInt8, UInt16, UInt32, UInt64, UInt128, UInt256, Int8, Int16, Int32, Int64, Int128, Int256
  1. 문자열 타입
String: 가변 길이 문자열
FixedString(길이): 고정 길이 문자열, 바이트 수를 파라미터로 받으며 String보다 실행 효율이 높음
  1. 날짜 타입
Date: 연-월-일
Date32: 연-월-일
DateTime: 연-월-일 시-분-초
DateTime64: 연-월-일 시-분-초.밀리초
  1. UUID 타입
generateUUIDv4() 함수를 통해 00000000-0000-0000-0000-000000000000 형식의 식별자 생성
예시: bee32020-a6cb-49a6-a10b-427381b11613
  1. 널 허용 타입 Nullable
예: 테이블 생성 시 id 필드가 Int32 타입인데 값이 불확실할 경우 null로 채워야 하며 기본값 0 사용 불가
Nullable(Int32)
  1. 배열 타입 Array(T)
필드 타입이 배열인 경우, 테이블 생성 시 데이터 타입 지정 필요
주의: MergeTree 테이블 엔진에서는 배열 중첩 허용 안됨
array() 함수를 사용하여 요소들을 배열로 구성해야 하며, toTypeName() 함수로 열의 데이터 타입 확인 가능
정수 타입 배열의 경우 주소 값을 반환함
  1. 소수 타입
# Decimal(P,S), Decimal32(S), Decimal64(S), Decimal128(S)
부호 있는 고정 소수점 수로, 덧셈, 뺄셈, 곱셈 연산 중 정밀도 유지
나눗셈의 경우 최하위 유효 숫자는 버림 처리(반올림 없음)
P - 정밀도. 유효 범위: [1:38], 전체 십진수 자리수 결정(소수부 포함)
S - 스케일. 유효 범위: [0:P], 소수부 자릿수 결정

1. 테이블 생성 문법

create table users_table (
    user_id Int8,
    user_name FixedString(12),
    gender Nullable(FixedString(3)),
    class_group String
) ENGINE = TinyLog;

2. 데이터 삽입

# 기본 형식:
INSERT INTO [db.]table [(col1, col2, col3)] VALUES (val11, val12, val13), (val21, val22, val23), ...

# 예시
insert into student_records values 
(1001,'홍길동','남','1반'),
(1002,'김철수','남','2반'),
(1003,'이영희','여','3반');

# 테이블 구조 확인
desc 테이블명

엔진

1. 데이터베이스 엔진

  1. Atomic

ClickHouse 데이터베이스 생성 시 기본 지정되는 데이터베이스 엔진

비차단 DROP TABLE 및 RENAME TABLE 쿼리와 원자적 EXCHANGE TABLES t1 AND t2 쿼리 지원
기본적으로 Atomic 데이터베이스 엔진 사용
  1. Mysql

MySQL 엔진은 원격 MySQL 서버의 테이블을 ClickHouse에 매핑하여 INSERT와 SELECT 쿼리를 수행할 수 있게 해주며, ClickHouse와 MySQL 간 데이터 교환을 용이하게 합니다.

MySQL 데이터베이스 엔진은 쿼리를 MySQL 문법으로 변환하여 전송하므로 SHOW TABLES 또는 SHOW CREATE TABLE 등의 작업이 가능합니다.

다음 작업은 수행 불가:

  • RENAME
  • CREATE TABLE
  • ALTER
# ClickHouse에서 데이터베이스 생성 및 원격 MySQL 서비스 지정
# 데이터베이스 생성 문법
CREATE DATABASE [IF NOT EXISTS] db_name [ON CLUSTER cluster]
ENGINE = MySQL('host:port', ['database' | database], 'user', 'password')

# 예시
create database IF NOT EXISTS mysql_bridge 
ENGINE = MySQL('192.168.160.100:3306','source_db','admin','password123');

# 파라미터 설명:
host:port — MySQL 서비스 주소(IP 또는 호스트명, 호스트명 사용 시 hosts 매핑 필요)
database — MySQL 데이터베이스 이름
user — MySQL 사용자명
password — MySQL 사용자 비밀번호

# 운영 주의사항
1. MySQL 데이터와 CK 데이터베이스 매핑 데이터는 거의 실시간 동기화
2. 어느 한쪽에서 데이터 추가 시 다른 쪽에서도 결과 확인 가능
3. 데이터 삭제는 MySQL 측에서만 가능하며 CK 측에서는 불가

2. 테이블 엔진

  1. 로그 엔진

a. Log

Log와 TinyLog의 차이점은 마크 파일이 열 파일과 함께 존재한다는 것입니다. 이러한 마크는 각 데이터 블록에 기록되며, 지정된 행 수를 건너뛰기 위한 오프셋 정보를 포함합니다. 이를 통해 다중 스레드에서 테이블 데이터를 읽을 수 있습니다. 동시 데이터 접근 시 여러 읽기 작업이 동시에 실행될 수 있지만, 쓰기 작업은 읽기 및 다른 쓰기 작업을 차단합니다. Log 엔진은 인덱스를 지원하지 않습니다. 또한 테이블에 대한 쓰기 실패 시 해당 테이블이 손상되어 읽기 시 오류가 발생합니다. Log 엔진은 임시 데이터, write-once 테이블, 테스트 또는 데모 목적에 적합합니다. 로그 엔진 테이블의 데이터는 삭제할 수 없습니다.

b. TinyLog

가장 간단한 테이블 엔진으로 디스크에 데이터를 저장합니다. 각 열은 별도의 압축 파일에 저장됩니다. 쓰기 시 데이터는 파일 끝에 추가됩니다.

동시 데이터 접근에 대한 제한이 없음:

  • 테이블에서 동시에 읽기와 쓰기가 이루어질 경우 읽기 작업에서 예외 발생
  • 여러 쿼리에서 동시에 테이블에 쓰기 시 데이터가 손상될 수 있음

이 테이블 엔진의 일반적인 사용 사례는 write-once 방식: 처음 한 번만 데이터를 쓰고 이후 필요에 따라 여러 번 읽습니다. 쿼리는 단일 스트림에서 실행됩니다. 즉, 이 엔진은 상대적으로 작은 테이블(권장 최대 1,000,000행)에 적합합니다. 많은 수의 작은 테이블이 있을 때 적절하며 Log 엔진보다 단순합니다(열어야 할 파일 수가 적음). 많은 작은 테이블이 있을 때 성능 저하가 발생할 수 있지만, 이미 다른 DBMS에서 사용 중이라면 TinyLog 유형 테이블로 전환하는 것이 더 쉬울 수 있습니다. 인덱스를 지원하지 않습니다.

c. StripeLog

데이터 쓰기:

StripeLog 엔진은 모든 열을 하나의 파일에 저장합니다. 각 Insert 요청 시 ClickHouse는 테이블 파일 끝에 데이터 블록을 추가하며, 열 단위로 기록합니다.

ClickHouse는 각 테이블에 대해 다음 파일을 생성합니다:

  • data.bin — 데이터 파일
  • index.mrk — 마크 파일. 삽입된 각 데이터 블록의 열 오프셋 정보 포함

StripeLog 엔진은 ALTER UPDATE 및 ALTER DELETE 작업을 지원하지 않습니다.

데이터 읽기:

마크 파일 덕분에 ClickHouse는 데이터를 병렬로 읽을 수 있습니다. 이는 SELECT 요청이 행을 반환하는 순서가 예측 불가능하다는 것을 의미합니다. ORDER BY 절을 사용하여 행을 정렬하세요.

  1. MergeTree 계열

MergeTree

ClickHouse에서 가장 강력한 테이블 엔진은 MergeTree(병합 트리) 엔진 및 해당 계열(*MergeTree)의 다른 엔진들입니다.

MergeTree 계열 엔진은 대용량 데이터를 테이블에 삽입하기 위해 설계되었습니다. 데이터는 데이터 조각 형태로 빠르게 하나씩 작성될 수 있으며, 백그라운드에서 특정 규칙에 따라 병합됩니다. 삽입 시 기존 저장 데이터를 지속적으로 수정(재작성)하는 것보다 이 전략이 훨씬 효율적입니다.

주요 특징:

  • 저장 데이터는 기본키로 정렬됨
  • 작은 희소 인덱스를 생성하여 데이터 검색 속도 향상
  • 파티션 키 지정 시 파티셔닝 지원
  • 동일 데이터셋 및 결과셋에서 파티션 관련 작업이 일반 작업보다 빠름
  • 쿼리에서 파티션 키 지정 시 자동으로 파티션 데이터 추출로 성능 향상
  • 데이터 복제 지원
  • ReplicatedMergeTree 계열 테이블은 데이터 복제 기능 제공
  • 데이터 샘플링 지원
  • 필요 시 테이블에 샘플링 방법 설정 가능
# 테이블 생성 문법 표준:
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
    column1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1] [TTL expr1],
    column2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2] [TTL expr2],
    ...
    INDEX idx_name1 expr1 TYPE type1(...) GRANULARITY value1,
    INDEX idx_name2 expr2 TYPE type2(...) GRANULARITY value2
) ENGINE = MergeTree()
ORDER BY expr
[PARTITION BY expr]
[PRIMARY KEY expr]
[SAMPLE BY expr]
[TTL expr [DELETE|TO DISK 'xxx'|TO VOLUME 'xxx'], ...]
[SETTINGS name=value, ...]
optimize table 테이블명 final;
-- 결과가 즉시 모든 동일 파티션을 병합하지 않으므로 빠른 결과 확인을 원할 경우 수동 병합 필요

개발 시 일반적으로 사용하는 테이블 엔진: 소량 데이터 테이블에는 TinyLog, 대용량 데이터 테이블에는 MergeTree 사용

주요 함수

1. 산술 함수

모든 산술 함수에서 결과 타입은 결과에 적합한 최소 수치 타입입니다(해당 타입이 존재할 경우). 최소 수치 타입은 비트 수, 부호 여부, 부동소수점 여부에 따라 결정됩니다. 충분한 비트가 없을 경우 최상위 비트 타입을 사용합니다. 간단히 말해, 값 크기에 따라 가장 적절한 데이터 타입을 자동으로 선택합니다.

# plus(a, b), a + b 연산자
수치 합계 계산. Date 또는 DateTime과 정수의 덧셈도 가능.
Date의 경우 정수 덧셈은 해당 일수 추가를 의미하며,
DateTime의 경우 해당 초수 추가를 의미함.

# minus(a, b), a - b 연산자
수치 차이 계산, 결과는 항상 부호 있음.
Date 또는 DateTime과 정수의 뺄셈도 가능. 위의 'plus' 참조.

# multiply(a, b), a * b 연산자
수치 곱셈 계산.

# divide(a, b), a / b 연산자
수치 나눗셈 계산. 결과 타입은 항상 부동소수점 타입.
정수 나눗셈이 아님. 정수 나눗셈은 'intDiv' 함수 사용.
0으로 나누기 시 'inf', '-inf' 또는 'nan' 반환.

# intDiv(a,b)
수치 나눗셈 계산, 내림하여 정수 반환(절댓값 기준).
0으로 나누기 또는 최소 음수를 -1로 나눌 때 예외 발생.

# greatest(a,b)
value1 — 첫 번째 값, Int/UInt 또는 Float 타입.
value2 — 두 번째 값, Int/UInt 또는 Float 타입.

2. 비교 함수

비교 함수는 항상 0 또는 1(UInt8)을 반환합니다.

다음 타입들 간 비교 가능:

  • 숫자
  • String 및 FixedString
  • 날짜
  • 날짜시간

각 그룹 내 타입들은 서로 비교 가능하지만, 서로 다른 그룹 간에는 비교 불가능합니다.

예를 들어 날짜와 문자열을 직접 비교할 수 없으며, 함수를 사용하여 문자열을 날짜로 변환하거나 그 반대의 변환이 필요합니다.

문자열은 바이트 단위로 비교됩니다. 짧은 문자열은 이를 시작으로 하고 최소한 하나의 문자를 포함하는 모든 문자열보다 작습니다.

같음, a=b 및 a==b 연산자
같지 않음, a!=b 및 a<>b 연산자
작음, < 연산자
큼, > 연산자
작거나 같음, <= 연산자
크거나 같음, >= 연산자

3. 데이터 타입 변환

값을 한 타입에서 다른 타입으로 변환할 때 이는 안전하지 않은 작업이며 데이터 손실이 발생할 수 있습니다. 데이터 손실은 일반적으로 큰 데이터 타입에서 작은 데이터 타입으로 변환하거나 서로 다른 데이터 타입 간 변환 시 발생합니다.

6월 21일 22:55에 게시됨