ai

ClickHouse Analytics Service Mesh Setup —

ClickHouse Analytics Service Mesh Setup —

ClickHouse คืออะไรและทำไมถึงเร็วสำหรับ Analytics

ClickHouse Analytics Service Mesh Setup —

ClickHouse เป็น open source columnar database ที่ออกแบบมาสำหรับ OLAP (Online Analytical Processing) queries พัฒนาโดย Yandex สามารถ process billions of rows ต่อวินาทีได้ เหมาะสำหรับ real-time analytics, log analysis, time-series data และ business intelligence

ClickHouse เร็วเพราะใช้ columnar storage ที่เก็บข้อมูลเป็นคอลัมน์แทนแถว ทำให้ read เฉพาะคอลัมน์ที่ต้องการ compress ได้ดีกว่า vectorized query execution ที่ process ข้อมูลเป็น batch แทนทีละ row data skipping indexes ที่ข้าม data blocks ที่ไม่เกี่ยวข้อง และ parallel processing ที่ใช้ทุก CPU cores

Service Mesh เช่น Istio ช่วยจัดการ network communication ระหว่าง ClickHouse nodes และ client services ได้ดีขึ้น ให้ mutual TLS encryption, traffic management, observability และ circuit breaking โดยไม่ต้องเปลี่ยน application code

Use cases ที่เหมาะกับ ClickHouse ได้แก่ Web analytics (ทดแทน Google Analytics), Application Performance Monitoring, Log aggregation (ทดแทน Elasticsearch สำหรับ read-heavy workloads), IoT sensor data, Financial tick data และ Ad-tech real-time bidding

เนื้อหาเกี่ยวข้อง — Nbb 2026: ทุกสิ่งที่คุณต้องรู้เกี่ยวกับเทคโนโลยีและการเงินแห่งอนาคต

ติดตั้ง ClickHouse Cluster บน Kubernetes

Deploy ClickHouse cluster ด้วย Kubernetes Operator

# ติดตั้ง ClickHouse Operator

kubectl apply -f https://raw.githubusercontent.com/Altinity/clickhouse-operator/master/deploy/operator/clickhouse-operator-install-bundle.yaml



# ตรวจสอบ operator

kubectl get pods -n kube-system | grep clickhouse



# clickhouse-cluster.yaml — ClickHouse Cluster Definition

apiVersion: "clickhouse.altinity.com/v1"

kind: "ClickHouseInstallation"

metadata:

  name: analytics-cluster

spec:

  configuration:

    clusters:

      - name: analytics

        layout:

          shardsCount: 2

          replicasCount: 2

        templates:

          podTemplate: clickhouse-pod

          volumeClaimTemplate: data-volume

    zookeeper:

      nodes:

        - host: zookeeper-0.zookeeper.default.svc.cluster.local

          port: 2181

        - host: zookeeper-1.zookeeper.default.svc.cluster.local

          port: 2181

        - host: zookeeper-2.zookeeper.default.svc.cluster.local

          port: 2181

    settings:

      max_memory_usage: "10000000000"

      max_concurrent_queries: "200"

      max_connections: "4096"

      merge_tree/max_bytes_to_merge_at_max_space_in_pool: "161061273600"

    users:

      analytics/password_sha256_hex: "a]secret_hash_here"

      analytics/networks/ip: "::/0"

      analytics/profile: default

      analytics/quota: default

    profiles:

      default/max_memory_usage: "10000000000"

      default/max_execution_time: "60"

  templates:

    podTemplates:

      - name: clickhouse-pod

        spec:

          containers:

            - name: clickhouse

              image: clickhouse/clickhouse-server:24.1

              resources:

                requests:

                  cpu: "2"

                  memory: "8Gi"

                limits:

                  cpu: "4"

                  memory: "16Gi"

              ports:

                - containerPort: 8123

                  name: http

                - containerPort: 9000

                  name: native

                - containerPort: 9009

                  name: interserver

    volumeClaimTemplates:

      - name: data-volume

        spec:

          accessModes: ["ReadWriteOnce"]

          resources:

            requests:

              storage: 100Gi

          storageClassName: fast-ssd



# Deploy

# kubectl apply -f clickhouse-cluster.yaml



# ตรวจสอบ

# kubectl get chi

# kubectl get pods -l clickhouse.altinity.com/chi=analytics-cluster



# เข้าถึง ClickHouse

# kubectl exec -it chi-analytics-cluster-analytics-0-0-0 -- clickhouse-client

# SELECT version();

ออกแบบ Schema และ Data Ingestion Pipeline

สร้าง tables และ ingestion pipeline สำหรับ analytics

แนะนำเพิ่มเติม — อ่านเพิ่มเติมที่ SiamCafeBook

-- === ClickHouse Schema Design ===



-- Events table (main analytics table)

CREATE TABLE IF NOT EXISTS analytics.events ON CLUSTER analytics

(

    event_id UUID DEFAULT generateUUIDv4(),

    event_type LowCardinality(String),

    event_timestamp DateTime64(3),

    user_id UInt64,

    session_id String,

    

    -- Request data

    url String,

    referrer String,

    user_agent String,

    ip_address IPv4,

    country_code LowCardinality(FixedString(2)),

    city LowCardinality(String),

    

    -- Device

    device_type LowCardinality(String),

    browser LowCardinality(String),

    os LowCardinality(String),

    

    -- Custom properties

    properties Map(String, String),

    

    -- Partitioning

    event_date Date DEFAULT toDate(event_timestamp)

)

ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/events', '{replica}')

PARTITION BY toYYYYMM(event_date)

ORDER BY (event_type, event_date, user_id, event_timestamp)

TTL event_date + INTERVAL 90 DAY

SETTINGS index_granularity = 8192;



-- Distributed table

CREATE TABLE IF NOT EXISTS analytics.events_distributed ON CLUSTER analytics

AS analytics.events

ENGINE = Distributed(analytics, analytics, events, sipHash64(user_id));



-- Materialized View สำหรับ real-time aggregation

CREATE MATERIALIZED VIEW analytics.hourly_stats ON CLUSTER analytics

ENGINE = ReplicatedSummingMergeTree('/clickhouse/tables/{shard}/hourly_stats', '{replica}')

PARTITION BY toYYYYMM(hour)

ORDER BY (event_type, hour, country_code)

AS SELECT

    event_type,

    toStartOfHour(event_timestamp) AS hour,

    country_code,

    count() AS event_count,

    uniqExact(user_id) AS unique_users,

    uniqExact(session_id) AS unique_sessions

FROM analytics.events

GROUP BY event_type, hour, country_code;



-- === Python Ingestion Script ===

-- pip install clickhouse-connect



-- import clickhouse_connect

-- import json

-- from datetime import datetime

-- 

-- client = clickhouse_connect.get_client(

--     host='clickhouse-service', port=8123,

--     username='analytics', password='secret',

--     database='analytics'

-- )

-- 

-- # Batch insert

-- data = [

--     ['pageview', datetime.now(), 12345, 'sess_abc', '/home', '', 'Mozilla/5.0', '10.0.0.1', 'TH', 'Bangkok', 'desktop', 'Chrome', 'Windows', {}],

--     ['click', datetime.now(), 12345, 'sess_abc', '/products', '/home', 'Mozilla/5.0', '10.0.0.1', 'TH', 'Bangkok', 'desktop', 'Chrome', 'Windows', {'button': 'buy'}],

-- ]

-- 

-- client.insert('events', data, column_names=[

--     'event_type', 'event_timestamp', 'user_id', 'session_id',

--     'url', 'referrer', 'user_agent', 'ip_address',

--     'country_code', 'city', 'device_type', 'browser', 'os', 'properties'

-- ])



-- === Kafka Integration ===

CREATE TABLE analytics.events_kafka ON CLUSTER analytics

(

    event_type String,

    event_timestamp DateTime64(3),

    user_id UInt64,

    session_id String,

    url String,

    properties String

)

ENGINE = Kafka

SETTINGS

    kafka_broker_list = 'kafka:9092',

    kafka_topic_list = 'analytics-events',

    kafka_group_name = 'clickhouse-consumer',

    kafka_format = 'JSONEachRow',

    kafka_num_consumers = 4;



CREATE MATERIALIZED VIEW analytics.events_kafka_mv TO analytics.events AS

SELECT

    event_type,

    event_timestamp,

    user_id,

    session_id,

    url,

    '' AS referrer,

    '' AS user_agent,

    toIPv4('0.0.0.0') AS ip_address,

    '' AS country_code,

    '' AS city,

    '' AS device_type,

    '' AS browser,

    '' AS os,

    JSONExtract(properties, 'Map(String, String)') AS properties

FROM analytics.events_kafka;

รวม ClickHouse กับ Service Mesh (Istio)

ClickHouse Analytics Service Mesh Setup —

ตั้งค่า Istio Service Mesh สำหรับ ClickHouse

# === Istio Configuration สำหรับ ClickHouse ===



# 1. Enable sidecar injection สำหรับ ClickHouse namespace

# kubectl label namespace default istio-injection=enabled



# 2. DestinationRule — mTLS และ connection pool

apiVersion: networking.istio.io/v1beta1

kind: DestinationRule

metadata:

  name: clickhouse-destination

spec:

  host: clickhouse-analytics.default.svc.cluster.local

  trafficPolicy:

    tls:

      mode: ISTIO_MUTUAL

    connectionPool:

      tcp:

        maxConnections: 100

        connectTimeout: 10s

      http:

        h2UpgradePolicy: DO_NOT_UPGRADE

        maxRequestsPerConnection: 100

    outlierDetection:

      consecutive5xxErrors: 5

      interval: 30s

      baseEjectionTime: 60s

      maxEjectionPercent: 50

---

# 3. VirtualService — traffic routing

apiVersion: networking.istio.io/v1beta1

kind: VirtualService

metadata:

  name: clickhouse-routing

spec:

  hosts:

    - clickhouse-analytics

  http:

    - match:

        - headers:

            x-query-type:

              exact: "realtime"

      route:

        - destination:

            host: clickhouse-analytics

            port:

              number: 8123

      timeout: 10s

      retries:

        attempts: 2

        perTryTimeout: 5s

    - route:

        - destination:

            host: clickhouse-analytics

            port:

              number: 8123

      timeout: 60s

      retries:

        attempts: 3

        perTryTimeout: 30s

---

# 4. PeerAuthentication — enforce mTLS

apiVersion: security.istio.io/v1beta1

kind: PeerAuthentication

metadata:

  name: clickhouse-mtls

spec:

  selector:

    matchLabels:

      clickhouse.altinity.com/chi: analytics-cluster

  mtls:

    mode: STRICT

---

# 5. AuthorizationPolicy — access control

apiVersion: security.istio.io/v1beta1

kind: AuthorizationPolicy

metadata:

  name: clickhouse-access

spec:

  selector:

    matchLabels:

      clickhouse.altinity.com/chi: analytics-cluster

  rules:

    - from:

        - source:

            principals:

              - "cluster.local/ns/default/sa/analytics-api"

              - "cluster.local/ns/default/sa/grafana"

              - "cluster.local/ns/default/sa/airflow-worker"

      to:

        - operation:

            ports: ["8123", "9000"]

---

# 6. ServiceEntry สำหรับ external access (ถ้าจำเป็น)

apiVersion: networking.istio.io/v1beta1

kind: ServiceEntry

metadata:

  name: clickhouse-cloud

spec:

  hosts:

    - "clickhouse-cloud.example.com"

  ports:

    - number: 8443

      name: https

      protocol: HTTPS

  resolution: DNS

  location: MESH_EXTERNAL

Monitoring Dashboard ด้วย Grafana

สร้าง monitoring สำหรับ ClickHouse cluster

เนื้อหาเกี่ยวข้อง — อ่านต่อ: fear and greed index set

#!/usr/bin/env python3

# clickhouse_exporter.py — Custom Prometheus Metrics for ClickHouse

from prometheus_client import start_http_server, Gauge, Counter, Histogram

import clickhouse_connect

import time

import logging



logging.basicConfig(level=logging.INFO)

logger = logging.getLogger("ch_exporter")



# Prometheus Metrics

ch_query_count = Counter("clickhouse_queries_total", "Total queries", ["query_type"])

ch_active_queries = Gauge("clickhouse_active_queries", "Currently running queries")

ch_memory_usage = Gauge("clickhouse_memory_usage_bytes", "Memory usage in bytes")

ch_parts_count = Gauge("clickhouse_parts_count", "Number of active parts", ["table"])

ch_rows_total = Gauge("clickhouse_rows_total", "Total rows", ["table"])

ch_disk_usage = Gauge("clickhouse_disk_usage_bytes", "Disk usage", ["table"])

ch_replication_lag = Gauge("clickhouse_replication_lag", "Replication lag in seconds", ["table"])

ch_query_duration = Histogram("clickhouse_query_duration_seconds", "Query execution time",

                              buckets=[0.01, 0.05, 0.1, 0.5, 1, 5, 10, 30, 60])



class ClickHouseExporter:

    def __init__(self, host="localhost", port=8123, user="default", password=""):

        self.client = clickhouse_connect.get_client(

            host=host, port=port, username=user, password=password

        )

    

    def collect_metrics(self):

        try:

            # Active queries

            result = self.client.query("SELECT count() FROM system.processes")

            ch_active_queries.set(result.result_rows[0][0])

            

            # Memory usage

            result = self.client.query(

                "SELECT sum(value) FROM system.metrics WHERE metric = 'MemoryTracking'"

            )

            ch_memory_usage.set(result.result_rows[0][0])

            

            # Table stats

            result = self.client.query("""

                SELECT database, table, 

                       sum(rows) as total_rows,

                       sum(bytes_on_disk) as disk_bytes,

                       count() as parts

                FROM system.parts 

                WHERE active = 1 AND database = 'analytics'

                GROUP BY database, table

            """)

            for row in result.result_rows:

                db, table, rows, disk, parts = row

                full_name = f"{db}.{table}"

                ch_rows_total.labels(table=full_name).set(rows)

                ch_disk_usage.labels(table=full_name).set(disk)

                ch_parts_count.labels(table=full_name).set(parts)

            

            # Replication lag

            result = self.client.query("""

                SELECT database, table, absolute_delay

                FROM system.replicas

                WHERE database = 'analytics'

            """)

            for row in result.result_rows:

                db, table, lag = row

                ch_replication_lag.labels(table=f"{db}.{table}").set(lag)

            

            # Query types

            result = self.client.query("""

                SELECT query_kind, count()

                FROM system.query_log

                WHERE event_date = today() AND type = 'QueryFinish'

                GROUP BY query_kind

            """)

            for row in result.result_rows:

                ch_query_count.labels(query_type=row[0]).inc(row[1])

            

            logger.info("Metrics collected successfully")

            

        except Exception as e:

            logger.error(f"Failed to collect metrics: {e}")

    

    def run(self, port=9116, interval=15):

        start_http_server(port)

        logger.info(f"Exporter started on :{port}")

        

        while True:

            self.collect_metrics()

            time.sleep(interval)



# Grafana Dashboard JSON (key panels):

# {

#   "panels": [

#     {"title": "Active Queries", "type": "stat", "targets": [{"expr": "clickhouse_active_queries"}]},

#     {"title": "Memory Usage", "type": "gauge", "targets": [{"expr": "clickhouse_memory_usage_bytes / 1073741824"}]},

#     {"title": "Query Rate", "type": "graph", "targets": [{"expr": "rate(clickhouse_queries_total[5m])"}]},

#     {"title": "Rows by Table", "type": "table", "targets": [{"expr": "clickhouse_rows_total"}]},

#     {"title": "Replication Lag", "type": "graph", "targets": [{"expr": "clickhouse_replication_lag"}]},

#     {"title": "Disk Usage", "type": "bargauge", "targets": [{"expr": "clickhouse_disk_usage_bytes"}]}

#   ]

# }



if __name__ == "__main__":

    exporter = ClickHouseExporter(host="clickhouse-analytics", user="analytics", password="secret")

    exporter.run()

Performance Tuning และ Best Practices

เทคนิคการเพิ่มประสิทธิภาพ ClickHouse

-- === ClickHouse Performance Tuning ===



-- 1. เลือก Primary Key ที่เหมาะสม

-- ORDER BY ควรเรียงจาก low cardinality -> high cardinality

-- ดี: ORDER BY (event_type, event_date, user_id)

-- ไม่ดี: ORDER BY (event_id)  -- UUID มี cardinality สูงมาก



-- 2. ใช้ LowCardinality สำหรับ columns ที่มีค่าซ้ำมาก

-- LowCardinality(String) เร็วกว่า String 2-10x สำหรับ columns ที่มี < 10,000 unique values

ALTER TABLE analytics.events

    MODIFY COLUMN device_type LowCardinality(String);



-- 3. ใช้ Data Skipping Indexes

ALTER TABLE analytics.events

    ADD INDEX idx_user_id user_id TYPE minmax GRANULARITY 4;



ALTER TABLE analytics.events

    ADD INDEX idx_url url TYPE bloom_filter(0.01) GRANULARITY 4;



ALTER TABLE analytics.events

    ADD INDEX idx_country country_code TYPE set(100) GRANULARITY 4;



-- 4. Optimize queries

-- ใช้ PREWHERE แทน WHERE สำหรับ filter แรก

SELECT count() FROM analytics.events

PREWHERE event_type = 'pageview'

WHERE event_date >= '2024-01-01';



-- 5. ใช้ Approximate functions สำหรับ speed

SELECT uniqHLL12(user_id) AS approx_users  -- เร็วกว่า uniqExact 10x

FROM analytics.events_distributed

WHERE event_date >= today() - 7;



-- 6. Sampling

SELECT count() * 10 AS estimated_count

FROM analytics.events

SAMPLE 0.1  -- sample 10%

WHERE event_type = 'pageview';



-- 7. Settings สำหรับ Production

-- /etc/clickhouse-server/config.d/performance.xml

-- 

--   

--     

--       16

--       10000000000

--       5000000000

--       5000000000

--       partial_merge

--       1

--     

--   

--   

--     500

--     161061273600

--   

-- 



-- 8. Monitor slow queries

SELECT

    query,

    query_duration_ms,

    read_rows,

    read_bytes,

    memory_usage

FROM system.query_log

WHERE type = 'QueryFinish'

    AND event_date = today()

    AND query_duration_ms > 1000

ORDER BY query_duration_ms DESC

LIMIT 20;

FAQ คำถามที่พบบ่อย

Q: ClickHouse กับ Elasticsearch ต่างกันอย่างไร?

A: ClickHouse เป็น columnar database ที่เร็วมากสำหรับ aggregation queries (GROUP BY, COUNT, SUM) เหมาะสำหรับ analytics และ metrics Elasticsearch เป็น search engine ที่เก็บข้อมูลแบบ document เหมาะสำหรับ full-text search และ log exploration ClickHouse กิน storage น้อยกว่า 3-10x เพราะ compression ดีกว่า แต่ Elasticsearch ดีกว่าสำหรับ fuzzy search และ free-text queries

แนะนำเพิ่มเติม — เรียนเทรดกับ iCafeForex

เนื้อหาเกี่ยวข้อง — อ่านต่อ: Go Chi Router Container Orchestration — เปลี่ยนการจัดการ Container ของคุณให้เ…

Q: ClickHouse ต้องใช้ ZooKeeper ไหม?

A: สำหรับ standalone server ไม่ต้อง แต่สำหรับ replicated cluster ต้องใช้ ZooKeeper หรือ ClickHouse Keeper (built-in replacement ตั้งแต่ version 22.3) สำหรับ coordination ClickHouse Keeper แนะนำมากกว่าเพราะ lightweight กว่า ไม่ต้อง manage Java process แยก และ compatible กับ ZooKeeper protocol

Q: Service Mesh จำเป็นสำหรับ ClickHouse ไหม?

A: ไม่จำเป็นแต่มีประโยชน์มากสำหรับ production มี mTLS encryption ระหว่าง nodes โดยไม่ต้อง configure ClickHouse เอง มี traffic management ที่ route queries ไป replica ที่ healthy มี observability ที่เห็น latency และ error rate ต่อ connection และมี access control ที่กำหนดว่า service ไหนเข้าถึง ClickHouse ได้

เนื้อหาเกี่ยวข้อง — แนะนำให้อ่าน เงิน 2 ล้านลงทุนอะไรดี

Q: ClickHouse รองรับ data ขนาดไหน?

A: ClickHouse รองรับ petabytes ของข้อมูลได้ Yandex Metrica ใช้ ClickHouse เก็บข้อมูล 20+ trillion rows Cloudflare ใช้สำหรับ DNS analytics 30+ million requests ต่อวินาที สำหรับ setup ทั่วไป single node รองรับ 1-10 TB ได้สบาย cluster 3-5 nodes รองรับ 10-100 TB ถ้าต้องการมากกว่านั้นใช้ sharding เพิ่ม nodes

XM Legend · เทรดเดอร์ & ผู้สอน Forex 13 ปี

ผู้ก่อตั้ง SiamCafe ตั้งแต่ปี 1997 · เทรดเดอร์สาย Forex มากกว่า 13 ปี ได้รับการยกย่องเป็น XM Legend · แบ่งปันความรู้ Forex, ไอที, AI และการเทรด จากประสบการณ์จริงในตลาดจริง