# ClickHouse vs BigQuery Real Time Telemetry: 2026 Query Latency, Cost per Million Events & Migration Pitfalls
For clickhouse vs bigquery real time telemetry pipelines, ClickHouse provides hardware-level SIMD vectorization that delivers sub-100ms analytical query latencies and predictable compute costs under continuous dashboard polling. In contrast, Google BigQuery provides serverless scaling and ANSI SQL mutations, though its decoupled Dremel execution engine introduces a multi-second orchestration floor that escalates costs under high-frequency polling.
---
The Architectural Paradigm Split: Local Vectorized Compute vs. Serverless Distributed Shuffle
Engineering leadership evaluating real-time observability, Internet of Things (IoT) monitoring, and application telemetry pipelines faces a fundamental architectural trade-off: tightly coupled, hardware-optimized columnar engines versus decoupled, serverless analytical compute fabrics.
Telemetry workloads impose distinct mechanical demands on database storage and execution layers. They are characterized by massive, append-only write streams, monotonically advancing time attributes, wide sparse metadata maps (frequently structured via Entity-Attribute-Value patterns), and high-frequency automated polling queries generated by operational dashboards (such as Grafana or custom internal APMs) and continuous anomaly-detection workers.
flowchart TD
subgraph ClickHouseArch["ClickHouse Architecture (Vectorized / Local NVMe Tiered)"]
CH_Ingest["Kafka / Vector Ingestion Stream"] --> CH_Buffer["In-Memory Write Buffer (async_insert)"]
CH_Buffer --> CH_Parts["Immutable Compressed Parts (ZSTD / Gorilla)"]
CH_Parts --> CH_NVMe["Hot Tier: Local NVMe SSD Cache"]
CH_Parts --> CH_S3["Warm/Cold Tier: S3 / GCS Object Storage"]
CH_Query["High-Concurrency Dashboard Query (<100ms)"] --> CH_Exec["SIMD-Vectorized Execution Engine (LLVM JIT)"]
CH_Exec --> CH_NVMe
end
subgraph BigQueryArch["Google BigQuery Architecture (Decoupled Compute & Colossus)"]
BQ_Ingest["Storage Write API Stream"] --> BQ_Buffer["Managed Streaming Buffer"]
BQ_Buffer --> BQ_Colossus["Colossus Distributed Storage (Capacitor Columnar)"]
BQ_Query["Dashboard Query (1.5s - 4s)"] --> BQ_Dremel["Dremel Query Engine (Dynamic Slot Allocation)"]
BQ_Dremel --> BQ_Shuffle["Petabit Jupiter Network Shuffle Fabric"]
BQ_Shuffle --> BQ_Colossus
endThe divergence between ClickHouse and Google BigQuery stems from their underlying computing foundations:
ClickHouse: Hardware-Centric Vectorized Execution
ClickHouse is engineered around modern hardware mechanics. It executes analytical queries using vectorized SIMD (Single Instruction, Multiple Data) processing, compiling expression trees via LLVM JIT into direct machine instructions. ClickHouse stores columnar data on locally attached NVMe solid-state storage (or cached local disk backed by remote object stores like AWS S3 or Google Cloud Storage).
Because primary index lookups utilize a sparse index resident in memory, queries skip large ranges of physical blocks without disk seeks. Aggregations execute across millions of integers per CPU core per second, enabling typical analytical telemetry queries targeting sorted keys to return in tens of milliseconds directly from RAM and local NVMe caches.
BigQuery: Decoupled Multi-Tenant Massively Parallel Processing (MPP)
Google BigQuery executes on a multi-tenant, fully disaggregated compute-storage fabric. Query execution is driven by the Dremel processing engine, which provisions compute dynamically from a vast pool of multi-tenant Borg workers ("slots"). Storage is decoupled and persisted across Google's distributed file system, Colossus, formatted in Capacitor columnar blocks.
Communication between Dremel execution slots and Colossus disk blocks relies on Google's Jupiter petabit-scale datacenter networking fabric. While this separation enables practically infinite horizontal scaling for petabyte-scale batch transformations without capacity provisioning, it incurs inherent orchestration overhead. As documented in Google's published Dremel systems research (Melnik et al., VLDB) and Google Cloud architectural guides, query parsing, dynamic slot allocation across Borg worker pools, metadata consensus, and network round-trip transport introduce an observable baseline latency floor—typically between 1.5 and 3.0 seconds for ad-hoc queries—regardless of how few rows are processed.
---
Event Ingestion Architecture: Continuous Streaming Pipelines Compared
Telemetry architectures require predictable write performance under sustained ingestion pressure. The architectural mechanics of how each engine absorbs streaming events directly govern ingestion latency, buffer memory footprints, and pipeline stability.
flowchart LR
subgraph Producers["Telemetry Sources"]
App["App Servers"]
Kube["Kubernetes DaemonSets"]
IoT["Edge & IoT Devices"]
end
subgraph Transport["Streaming Transport Tier"]
Kafka["Apache Kafka / Redpanda Cluster"]
end
subgraph ClickHousePipe["ClickHouse Pipeline"]
VectorCH["Vector / Kafka Engine"] -->|Micro-Batches 10k-50k rows| CH_Table["ReplacingMergeTree Engine"]
CH_Table -->|Async Background Merge| CH_Merged["Merged Solid Parts"]
end
subgraph BigQueryPipe["BigQuery Pipeline"]
BQ_Worker["Pub/Sub + Dataflow / Direct Write"] -->|Storage Write API| BQ_Storage["BigQuery Capacitor Table"]
end
App --> Kafka
Kube --> Kafka
IoT --> Kafka
Kafka --> VectorCH
Kafka --> BQ_WorkerIngestion Throughput and Buffering Mechanics
- ClickHouse Streaming Mechanics: According to published ClickHouse engineering benchmarks, an optimized multi-core node (such as 16 to 32 vCPUs on bare metal or cloud instances equipped with local NVMe SSDs) can sustain ingestion throughput of several hundred thousand to over 1,000,000 records per second when ingesting structured tabular telemetry (modeled at 100 to 200 bytes per event) grouped into batches of 10,000 to 50,000 rows. The underlying
MergeTreestorage engine writes incoming batches to disk as discrete, immutable directory structures called "parts." These parts are subsequently combined into larger, sorted parts in the background through an asynchronous merge cycle. However, ClickHouse requires client-side buffering or server-side asynchronous batching (async_insert=1). Sending unbuffered, single-event HTTP POST requests directly to ClickHouse forces the engine to create an individual part per write, rapidly triggering safety write locks. - BigQuery Storage Write API Mechanics: BigQuery ingests streaming data through its managed Storage Write API, which supports
COMMITTED,PENDING, andDEFAULTstream types. It delivers high ingestion throughput (hundreds of megabytes per second per table) with guaranteed exactly-once delivery when stream offsets are tracked. Google manages write buffering behind its managed service: records land in an optimized in-memory streaming buffer before being persisted into permanent Capacitor columnar storage files on Colossus. The operational advantage is zero infrastructure management; the trade-off is an ongoing $0.025 per gigabyte streaming ingestion surcharge beyond the 2 TB monthly allowance on Google Cloud.
---
6-Criteria Technical Comparison Matrix
The table below contrasts ClickHouse and BigQuery across the primary architectural dimensions governing telemetry platforms, evaluating both mechanical database criteria and enterprise decision pillars:
| Architectural Evaluation Criteria | Category Mapping | ClickHouse (v24.x - v26.x / Cloud) | Google BigQuery (Editions / Serverless) | Decisive Architecture Advantage |
|---|---|---|---|---|
| 1. Continuous Streaming Ingestion Throughput | Performance & Scalability | High sustained throughput per node (hundreds of thousands of records/sec on 16+ vCPU nodes with NVMe) when writes are batched (10k+ rows or async_insert=1) to avoid part fragmentation. | 100k–500k+ rows/sec per stream via Storage Write API; fully managed streaming buffer with exactly-once semantics. | BigQuery for zero-ops ingestion ergonomics; ClickHouse for raw write throughput and zero-per-byte streaming ingestion surcharge. |
| 2. High-Concurrency Analytical Query Latency | Performance | Sub-100ms p95 latency on indexed, sorted partition keys for metric aggregations (verified in ClickBench suite profiles); SIMD vectorization and local NVMe caching serve concurrent dashboards efficiently. | 1.5s–3.5s+ baseline floor; decoupled network shuffle, Borg slot scheduling, and Dremel worker dispatch establish an architectural orchestration floor on ad-hoc scans. | ClickHouse: Significantly lower tail latency; suited for live user-facing dashboards and real-time operational alerting. |
| 3. Cost Predictability Under Continuous Dashboard Polling | Total Cost of Ownership | Fixed provisioned cost. Hardware or Cloud Compute Units (CCUs) run 24/7. Polling dashboards every 10–15s incurs $0 incremental fee beyond provisioned capacity. | Variable or commitment-based. On-Demand models bill $6.25/TB scanned; continuous dashboard queries scanning multiple gigabytes every few seconds escalate costs unless high-capacity Editions slots are reserved. | ClickHouse: Amortized compute eliminates billing volatility from automated observability tooling. |
| 4. Data Compression Ratios & Storage Efficiency | Scalability & FinOps | 6:1 to 12:1+ compression on repetitive time-series metrics. Granular column codecs (DoubleDelta, Gorilla, T64, ZSTD, LZ4) optimize temporal and floating-point telemetry. | 3:1 to 5:1 compression on analytical data. Capacitor proprietary columnar encoding (RLE, dictionary). Physical storage billed at $0.04/GB (active) or $0.02/GB (long-term). | ClickHouse: Greater disk footprint reduction for numerical telemetry; native tiering to object storage (S3/GCS) at standard cloud object rates. |
| 5. Schema Evolution & Mutation Ergonomics | Developer Velocity | Append-only focus. Metadata-only column additions; record updates and deletions execute as asynchronous disk-rewriting ALTER ... UPDATE mutations. | Full ANSI SQL ACID DML (UPDATE, DELETE, MERGE). Supports concurrent transactional updates and automatic schema evolution. | BigQuery: Far superior DML mutation ergonomics; ClickHouse requires specialized deduplication engines. |
| 6. Infrastructure Management & Operational Overhead | Maintenance Burden & Vendor Lock-in | Moderate to High. Self-hosted requires ClickHouse Keeper consensus, OS kernel tuning, and merge monitoring; ClickHouse Cloud abstracts infra but requires table-engine tuning. Portable across any cloud or on-premise hardware. | Zero operational overhead. Fully serverless. No instance sizing, OS patching, index rebuilding, or disk capacity planning. Managed within Google Cloud ecosystem. | BigQuery for zero maintenance burden; ClickHouse for open-source multi-cloud portability and avoiding vendor lock-in. |
---
Deep Technical Teardown Across Key Telemetry Dimensions
1. High-Concurrency Query Latency: SIMD Vectorization vs. Dremel Scheduling
Query response times reveal the starkest operational contrast between the two systems. ClickHouse processes data directly through CPU vector registers. A query aggregating telemetry data points filters columns using sparse primary keys (evaluating one index mark per 8,192 physical rows by default). The engine streams matching columnar data into SIMD-enabled registers, executing instructions across multiple values simultaneously:
$$\text{Throughput}_{\text{ClickHouse}} \approx \frac{\text{CPU Cycles}}{\text{SIMD Vector Width (256/512 bits)}} \times \text{Core Count}$$
This architecture routinely yields analytical query latencies in the 15 to 80 millisecond range on warm NVMe and page caches. For an engineering organization building live application performance monitoring (APM) interfaces or customer-facing telemetry dashboards where users adjust time windows dynamically, ClickHouse responds within human visual perception thresholds.
In contrast, BigQuery’s Dremel engine is built for massive aggregate throughput across distributed workers rather than low-latency responsiveness. When an analytical query is dispatched to BigQuery:
1. The query string is parsed, optimized into an execution graph, and validated by the central coordinator.
2. The coordinator calculates the required execution stages and requests compute allocation from the shared multi-tenant Borg worker pool.
3. The assigned worker containers initialize network connections to Google Colossus over the Jupiter fabric.
4. Intermediate join and aggregation stages shuffle data across network boundaries.
As documented in Google Cloud's performance optimization documentation, this multi-step orchestration pipeline imposes a baseline execution latency—generally between 1.5 and 3.0 seconds—even if the query targets a well-partitioned table scanning only a few megabytes. Under high dashboard concurrency (such as dozens of engineers refreshing monitoring screens simultaneously), BigQuery queries must compete for slot capacity, which can extend tail latencies unless dedicated slot reservations are provisioned.
2. Cost Predictability Under Continuous Dashboard Polling
The financial model of a telemetry platform is determined by query execution frequency. Operational monitoring dashboards poll databases on automated intervals—typically every 10 to 60 seconds per active panel.
Consider an engineering department monitoring 25 services across 10 shared dashboards refreshing every 15 seconds. This generates:
$$\frac{10 \text{ dashboards} \times 4 \text{ queries}}{15 \text{ seconds}} \times 3,600 \text{ seconds/hr} = 9,600 \text{ queries per hour} \approx 6.9 \text{ million queries per month}$$
Under BigQuery On-Demand Pricing ($6.25 per TB scanned):
Even if strict timestamp partitioning and column pruning restrict each dashboard query to scan an average of 2 GB of data:
$$\text{Data Scanned} = 6,900,000 \times 2 \text{ GB} = 13,800 \text{ TB}$$
$$\text{Monthly Query Cost} = 13,800 \text{ TB} \times \$6.25/\text{TB} = \$86,250/\text{month}$$
Switching to BigQuery Editions (Standard or Enterprise) caps per-query scan costs by purchasing continuous slot-hours (for example, Enterprise Edition slots at published rates of ~$0.06 per slot-hour). However, ensuring 50 concurrent dashboard queries execute without queuing requires a minimum baseline of 100 to 200 committed slots, establishing a fixed compute expense of $4,320 to $8,640 per month regardless of actual query activity.
Under ClickHouse Architecture:
Based on published AWS on-demand list pricing (US East), a 3-node ClickHouse cluster utilizing compute-optimized instances (such as m6i.4xlarge with 16 vCPUs and 64 GB RAM at ~$0.768/hour each) costs approximately $1,660 per month in base compute. Adding 1 TB of provisioned gp3 SSD storage per node ($0.08/GB-month) adds roughly $240 per month, totaling approximately $1,900 per month for unreserved infrastructure (or $1,100 to $1,300 per month under 1-year or 3-year Savings Plans). Because continuous dashboard queries evaluate sparse primary keys (PRIMARY KEY (tenant_id, service_name, metric_name)) and scan only active hot-window data cached in memory, the hardware handles thousands of dashboard queries per hour at low CPU utilization. The marginal compute cost of additional dashboard queries is zero, because compute is fixed and amortized.
3. Columnar Compression and Storage Tiering
Telemetry data consists primarily of repeating timestamps, low-cardinality strings (hostnames, environment names, metric namespaces), and numerical measurement readings (CPU load, memory bytes, latency floats).
ClickHouse provides column-level encoding declarations that routinely achieve compression ratios between 6:1 and 12:1+ on structured metric datasets:
- Timestamps: By applying the
DoubleDeltacodec, ClickHouse stores the difference between successive time differences rather than absolute timestamps. For periodic metrics emitted at fixed intervals, the second-order differences evaluate to zero, compressing to fractions of a bit per row. - Floating-Point Metrics: The
Gorillacodec (derived from Facebook's time-series database research, Pelkonen et al., VLDB 2015) XORs consecutive 64-bit floating-point values, stripping redundant leading and trailing zero bits. - Low-Cardinality Strings: The
LowCardinality(String)type automatically builds localized dictionary indexes, converting strings into compact 8-bit or 16-bit integer references.
These encoded streams are subsequently compressed with block-level algorithms like ZSTD or LZ4. Furthermore, ClickHouse natively supports storage tiering: tables automatically move data parts from fast local NVMe drives to AWS S3 or Google Cloud Storage buckets once data ages past a configurable threshold (e.g., 14 days), lowering retention storage costs to standard cloud object rates ($0.02 to $0.023 per gigabyte per month).
BigQuery stores data in Capacitor, applying automated dictionary encoding and run-length encoding (RLE). While Capacitor performs well for general-purpose analytics (typically 3:1 to 5:1 compression on raw JSON equivalents), it does not allow engineers to declare specialized numerical time-series codecs like Gorilla or DoubleDelta for granular floating-point bit manipulation. BigQuery physical storage pricing ($0.04 per GB-month for active, $0.02 for long-term compressed data) reduces storage expenses compared to legacy logical pricing, but total data volume on disk remains higher than an optimized ClickHouse deployment utilizing domain-specific codecs.
4. Schema Mutations and DML Mechanics
A major operational difference emerges when telemetry records must be altered or deleted to support data governance, data subject erasure requests, privacy compliance workflows, or trace corrections.
BigQuery functions as a standard ANSI SQL database with full transactional DML capabilities. Executing UPDATE telemetry SET user_id = 'anonymized' WHERE user_id = 'usr_123' or MERGE statements executes natively within ACID isolation guarantees, automatically updating Capacitor storage blocks.
In ClickHouse, mutations are structurally heavy operations. ClickHouse tables are optimized for immutable, append-only streams. Executing an ALTER TABLE ... UPDATE or ALTER TABLE ... DELETE statement does not perform an in-place modification. Instead, ClickHouse schedules a mutation task that rewrites entire immutable data parts on disk. Running continuous transactional updates or point deletes creates heavy disk I/O amplification, saturates storage controllers, and stalls analytical queries.
To handle record updates or deduplication safely in ClickHouse, engineering teams must use the ReplacingMergeTree or CollapsingMergeTree table engines, passing an incremental version number or sign flag. Deduplication occurs asynchronously during background part merges, or at query time using the FINAL modifier or argMax() aggregations.
5. Infrastructure Maintenance and Operational Burden
The operational surface area of the two platforms represents an inverse trade-off between engineering effort and cloud expenditures:
BigQuery requires minimal systems engineering maintenance. There are no virtual machine instances to monitor, no operating system kernels to patch, no storage volumes to resize, and no database replication clusters to manage. Google manages high availability, multi-zone replication, and failure recovery behind a clean web API.
Self-hosted ClickHouse requires active database reliability engineering:
- Coordination Consensus: ClickHouse Keeper (or Apache ZooKeeper) must be deployed as a 3-node quorum to manage distributed table metadata, replication logs, and DDL synchronization.
- Operating System Tuning: Linux kernel parameters (
vm.max_map_count, memory overcommit flags, network backlog buffers, disk I/O schedulers) require rigorous tuning to prevent kernel panics under out-of-memory (OOM) conditions. - Merge Hygiene: Engineers must actively monitor system tables (
system.parts,system.mutations,system.replication_queue) to detect unmerged part accumulation, lagging replica nodes, or runaway disk consumption.
Managed ClickHouse Cloud alleviates infrastructure provisioning, node failure healing, and storage-compute separation, yet database administrators still must design proper partition keys, primary sorting keys, and memory limits.
---
3-Year Total Cost of Ownership (TCO) Scenario Model
To evaluate the economic trade-offs across varying operational scales, the following scenario model compares 3-year projected costs across three representative telemetry tiers:
- Tier 1 (50 Million Events/Month): ~25 GB uncompressed raw payload per month (~0.9 TB raw over 3 years, assuming 500 bytes/event). Low-frequency ad-hoc query activity (100,000 queries/month).
- Tier 2 (500 Million Events/Month): ~250 GB uncompressed raw payload per month (~9.0 TB raw over 3 years). Continuous automated dashboard polling (1.5 million queries/month).
- Tier 3 (5 Billion Events/Month): ~2.5 TB uncompressed raw payload per month (~90.0 TB raw over 3 years). Enterprise-scale observability and automated anomaly detection (8 million queries/month).
Cost Modeling Methodology & Pricing Assumptions
1. Streaming Ingestion: Modeled using Google Cloud BigQuery Storage Write API list pricing ($0.025 per GB for ingestion volume exceeding the 2 TB/month free tier). In ClickHouse, streaming ingestion throughput is absorbed directly by the provisioned compute instances at $0 per-gigabyte surcharge.
2. Storage Pricing: BigQuery storage is modeled at physical storage list rates ($0.04/GB-month active, $0.02/GB-month long-term unmutated, with an estimated 4:1 compression ratio). ClickHouse storage is modeled using a two-tier architecture: 14 days of hot local SSD storage ($0.08/GB-month) tiered to remote S3/GCS object storage ($0.02/GB-month average), with an estimated 8:1 compression ratio achieved via Gorilla and DoubleDelta codecs.
3. Query Compute: BigQuery query costs are modeled using On-Demand scan rates ($6.25 per TB scanned) for Tier 1, and dedicated BigQuery Enterprise Edition slot commitments (at published rates of ~$0.06 per slot-hour) for Tiers 2 and 3 to reflect realistic enterprise FinOps practices that avoid unbounded per-byte scan billing. ClickHouse compute is modeled using dedicated self-hosted AWS/GCP node configurations sized for the workload based on official cloud list pricing.
4. Platform Engineering / SRE Allocation: BigQuery requires minimal infrastructure administration ($0 modeled SRE infrastructure allocation). ClickHouse self-hosted clusters require ongoing maintenance (OS patches, Keeper consensus monitoring, merge tuning), modeled as an allocation of 2% to 10% of a senior platform engineer's annual fully-loaded compensation (~$160,000–$180,000/year industry benchmark).
Modeled 3-Year TCO Matrix (All figures in USD)
| Cost Component | 50M Events/Mo: BigQuery | 50M Events/Mo: ClickHouse | 500M Events/Mo: BigQuery | 500M Events/Mo: ClickHouse | 5B Events/Mo: BigQuery | 5B Events/Mo: ClickHouse |
|---|---|---|---|---|---|---|
| Streaming Ingestion Fees | $0 (Within 2 TB free tier) | $0 (Absorbed by compute) | $0 (Within 2 TB free tier) | $0 (Absorbed by compute) | $150/yr ($450 total) | $0 (Absorbed by compute) |
| Cumulative Storage (3-Year Avg) | $120/yr ($360 total) | $45/yr ($135 total) | $900/yr ($2,700 total) | $320/yr ($960 total) | $9,000/yr ($27,000 total) | $3,100/yr ($9,300 total) |
| Query Execution Compute | $1,250/yr ($3,750 total) | $3,600/yr ($10,800 total) | $28,800/yr ($86,400 total) | $10,800/yr ($32,400 total) | $86,400/yr ($259,200 total) | $38,400/yr ($115,200 total) |
| Infrastructure / SRE Overhead | $0 (Fully serverless) | $3,200/yr ($9,600 total) | $3,200/yr ($9,600 total) | $6,400/yr ($19,200 total) | $6,400/yr ($19,200 total) | $16,000/yr ($48,000 total) |
| Total 3-Year Projected TCO | $4,110 | $20,535 | $98,700 | $52,560 | $305,850 | $172,500 |
| Net Financial Delta | BigQuery saves ~$16,425 | Baseline | Baseline | ClickHouse saves ~$46,140 | Baseline | ClickHouse saves ~$133,350 |
xychart-beta
title "Modeled 3-Year Total Cost of Ownership ($USD) Across Telemetry Scale"
x-axis ["50M Events/Mo", "500M Events/Mo", "5B Events/Mo"]
y-axis "Total 3-Year TCO ($)" 0 --> 350000
bar [4110, 98700, 305850]
bar [20535, 52560, 172500]Economic Tipping Point Dynamics
At 50 million events per month, BigQuery is the clear economic and operational winner. The total monthly data volume falls within free streaming tiers, and low query volume ensures on-demand query costs remain negligible. Deploying a dedicated 3-node ClickHouse cluster creates unnecessary idle compute expenditure and operational distraction for a small engineering team.
The economic tipping point typically emerges around 150 million to 300 million events per month when continuous dashboard polling is introduced:
1. Continuous dashboard queries executed against BigQuery either generate substantial on-demand scan bills or necessitate fixed slot-hour commitments on BigQuery Editions.
2. Ingestion volume approaches and eventually exceeds free-tier limits.
3. ClickHouse's fixed-cost compute model becomes financially advantageous, as the hardware easily absorbs millions of monthly dashboard refreshes without incremental cost per query.
At 5 billion events per month, the fixed-compute efficiency of ClickHouse yields substantial savings over 3 years, even after budgeting for SRE operational overhead or managed cloud margins.
---
Production DDL: Optimized ClickHouse ReplacingMergeTree Schema
To achieve double-digit millisecond query response times and optimal compression on real-time telemetry, table structures must exploit specialized columnar codecs, appropriate sparse index ordering, and partition sizing.
-- Production-Ready Telemetry Table for Multi-Tenant Services
CREATE TABLE telemetry.service_metrics_v2
(
-- Multi-tenant identifier: essential first sorting key for pruning
tenant_id UInt32 CODEC(DoubleDelta, LZ4),
-- Low-cardinality metadata strings converted to internal integer dictionaries
environment LowCardinality(String),
service_name LowCardinality(String),
metric_name LowCardinality(String),
-- Microsecond-precision timestamp with delta-of-delta compression
timestamp DateTime64(3, 'UTC') CODEC(DoubleDelta, ZSTD(1)),
-- Unique event identifier used for deduplication tie-breaking
device_id UUID,
event_id UUID,
-- Numerical telemetry reading using Gorilla floating-point XOR compression
metric_value Float64 CODEC(Gorilla, ZSTD(1)),
-- Secondary measurement metrics
metric_count UInt32 CODEC(T64, ZSTD(1)),
-- Dynamic dimensions stored as a typed Map with ZSTD compression
tags Map(LowCardinality(String), String) CODEC(ZSTD(3)),
-- Record version for ReplacingMergeTree deduplication
event_version UInt64 CODEC(DoubleDelta, LZ4)
)
ENGINE = ReplacingMergeTree(event_version)
-- Monthly partitioning prevents excessive partition count while keeping part sizes manageable
PARTITION BY toYYYYMM(timestamp)
-- Sparse primary key ordering: High pruning cardinality -> Coarse temporal bucket -> Granular IDs
PRIMARY KEY (tenant_id, service_name, metric_name)
ORDER BY (tenant_id, service_name, metric_name, toStartOfHour(timestamp), device_id, event_id)
-- Tier older data automatically from local NVMe drives to S3-compatible cold object storage
TTL timestamp + INTERVAL 14 DAY TO VOLUME 's3_cold',
timestamp + INTERVAL 365 DAY DELETE
SETTINGS
index_granularity = 8192,
parts_to_throw_insert = 300,
max_parts_in_total = 100000;High-Performance Aggregation Query Example
The query below aggregates the p95 metric value and total request volume over a rolling 1-hour window across millions of rows, returning rapidly by pruning on the sparse primary key:
SELECT
toStartOfMinute(timestamp) AS time_bucket,
service_name,
count() AS sample_count,
round(quantile(0.95)(metric_value), 2) AS p95_latency_ms
FROM telemetry.service_metrics_v2
WHERE tenant_id = 4102
AND service_name = 'auth-gateway'
AND metric_name = 'http_request_duration_ms'
AND timestamp >= now() - INTERVAL 1 HOUR
GROUP BY
time_bucket,
service_name
ORDER BY
time_bucket ASC;---
Concrete Migration Failure Modes and Architectural Defenses
Teams migrating telemetry workloads from BigQuery to ClickHouse frequently encounter operational pitfalls by treating ClickHouse like a traditional relational database or a serverless warehouse.
flowchart TD
subgraph Failures["Common ClickHouse Failure Modes"]
F1["Failure 1: Unbuffered Micro-Inserts"] --> R1["Error: 'Too Many Parts in Table' (Write Rejection)"]
F2["Failure 2: OLTP-Style Record Mutations"] --> R2["Error: Disk I/O Saturation & High Write Amplification"]
F3["Failure 3: High-Cardinality Primary Keys"] --> R3["Error: Sparse Index Bloat & Complete Scan Fallback"]
F4["Failure 4: Unconstrained Distributed Joins"] --> R4["Error: 'Memory Limit Exceeded' on Coordinator Node"]
endFailure Mode 1: Unbuffered High-Frequency Tiny Batch Inserts
- The Root Cause: BigQuery developers accustomed to calling the streaming ingestion API with single records or small 10-row arrays replicate this pattern against ClickHouse. Every single
INSERTstatement generates an independent, immutable physical part directory on disk. If an ingestion worker sends 400 single-row inserts per second, ClickHouse creates 400 part directories per second. Because background merge threads consolidate parts at roughly 10 to 25 parts per second, parts accumulate until hittingparts_to_throw_insert = 300, triggering:
DB::Exception: Too many parts in all data parts in table (301). Merges are processing significantly slower than inserts.- Architectural Defense:
1. Deploy Vector or Kafka Consumers: Enforce micro-batching at the ingestion transport layer. Collect telemetry records into batches of 15,000 to 50,000 rows (or flush every 2.0 seconds, whichever threshold is reached first) before dispatching an HTTP or native TCP insert.
2. Enable ClickHouse Asynchronous Inserts: If client-side batching cannot be implemented in upstream producers, configure server-side asynchronous batching in the user profile:
SET async_insert = 1;
SET wait_for_async_insert = 1;
SET async_insert_busy_timeout_ms = 1000;
SET async_insert_max_data_size = 10485760; -- 10MBThis instructs ClickHouse to accumulate individual incoming inserts into an internal memory ring-buffer, flushing them as a single consolidated part once every second.
Failure Mode 2: Attempting OLTP-Style Record Updates Causing Disk I/O Thrashing
- The Root Cause: Upstream systems frequently need to update telemetry records (e.g., flagging an alert as acknowledged or updating an error resolution status). In BigQuery, developers issue
UPDATE telemetry SET resolved = true WHERE alert_id = 'xxx'. In ClickHouse, issuingALTER TABLE telemetry UPDATE resolved = true WHERE alert_id = 'xxx'initiates an asynchronous disk mutation. - ClickHouse does not update records in place; it clones and rewrites the entire data part containing that row. Executing frequent mutations across a large table forces the storage subsystem to continuously read and rewrite gigabytes of unchanged data, saturating NVMe IOPS and starving analytical read queries.
- Architectural Defense:
1. Avoid running ALTER TABLE UPDATE for operational data flow. Use the ReplacingMergeTree engine with a monotonic event_version column. When a state change occurs, append the updated record as a new row with an incremented version number.
2. At query time, resolve duplicates using argMax() aggregations:
SELECT
device_id,
argMax(resolved, event_version) AS current_status
FROM telemetry.service_metrics_v2
WHERE tenant_id = 4102
GROUP BY device_id; 3. Reserve ALTER TABLE UPDATE/DELETE mutations exclusively for infrequent lifecycle and data governance maintenance (such as scheduled batch data pruning or customer privacy corrections), executing during off-peak maintenance windows.
Failure Mode 3: Improper Sorting Key Design Destroying Query Pruning
- The Root Cause: In BigQuery, clustering on columns like
(event_uuid, timestamp)allows the optimizer to handle flexible filtering. In ClickHouse, theORDER BYclause strictly establishes the physical sorting order of rows on disk and determines the structure of the in-memory sparse primary index. - Placing a high-cardinality, uniformly distributed unique identifier (such as
event_id UUIDortrace_id) at the beginning of theORDER BYtuple negates primary key filtering. Because every row has a distinct UUID, subsequent columns in the sorting tuple (such astimestamportenant_id) will never be sorted relative to one another. A query filtering fortenant_id = 10 AND timestamp >= now() - INTERVAL 1 HOURcannot prune physical data blocks and falls back to a full table scan of all terabytes on disk. - Architectural Defense:
Construct primary keys using strict monotonicity and filtering hierarchy:
1. First: Tenant or customer isolation key (e.g., tenant_id UInt32).
2. Second: High-level categorization filters (e.g., service_name, metric_name).
3. Third: Coarse temporal aggregation bucket (e.g., toStartOfHour(timestamp)).
4. Last: High-cardinality identifiers (e.g., device_id, event_id).
This ensures that range queries on tenant and time prune over 95% of physical data blocks before reading columns off disk.
Failure Mode 4: Distributed Join Memory Exhaustion
- The Root Cause: BigQuery handles complex multi-table joins transparently by orchestrating a distributed hash shuffle across thousands of remote Borg workers over Google's Jupiter network fabric.
- ClickHouse is fundamentally an analytical columnar aggregation engine rather than a distributed relational shuffle engine. When a developer executes a distributed
SELECT ... FROM telemetry_distributed AS t JOIN metadata_distributed AS m ON t.device_id = m.device_id, ClickHouse sends the subquery to all remote shards, collects the entire right-side table (m) into local RAM on the query coordinator node, and builds an in-memory hash table. If the dimension table exceeds available physical memory, the query terminates:
DB::Exception: Memory limit (total) exceeded: would use 64.20 GiB (limit: 60.00 GiB).- Architectural Defense:
1. Denormalize at Ingestion: Flatten dimensional attributes (e.g., customer tier, host architecture, container image name) directly into the telemetry event payload before writing to ClickHouse.
2. Utilize ClickHouse Dictionaries: Load slowly changing dimension tables into memory-mapped ClickHouse Dictionaries (CREATE DICTIONARY ... LAYOUT(FLAT) or LAYOUT(HASHED)). Lookups execute in memory using the dictGet() function without triggering distributed shuffle operations.
3. Use `GLOBAL JOIN` or External Aggregation: When right-side tables must be joined, specify GLOBAL JOIN to broadcast the subquery once, or configure max_bytes_before_external_join to spill hash buckets to disk.
---
Falsifiability Checks: Operational Metrics & Verification Traces
To verify whether a ClickHouse deployment is functioning within healthy operational boundaries or deteriorating toward one of the failure modes described above, platform teams should configure automated alerts against the following system tables:
-- Diagnostic Metric 1: Active Part Count Explosion Check
-- Alert condition: Part count > 200 on any active partition indicates merge starvation
SELECT
table,
partition,
count() AS active_parts_count
FROM system.parts
WHERE active AND database = 'telemetry'
GROUP BY table, partition
HAVING active_parts_count > 200;
-- Diagnostic Metric 2: Unfinished Disk Mutation Backlog
-- Alert condition: Mutations pending > 5 indicates dangerous OLTP-style update patterns
SELECT
table,
count() AS pending_mutations,
sum(parts_to_do) AS parts_remaining_to_mutate
FROM system.mutations
WHERE is_done = 0
GROUP BY table
HAVING pending_mutations > 5;
-- Diagnostic Metric 3: Memory Thrashing and Heavy Query Spills
-- Alert condition: Queries exceeding 70% of max server RAM
SELECT
query_id,
user,
elapsed,
formatReadableSize(memory_usage) AS peak_memory,
query
FROM system.query_log
WHERE event_time >= now() - INTERVAL 10 MINUTE
AND type = 'QueryFinish'
AND memory_usage > (64 * 1024 * 1024 * 1024 * 0.70)
ORDER BY memory_usage DESC
LIMIT 5;If query latency spikes or memory limits trigger, evaluate system.part_log to confirm whether part merges are CPU-throttled or disk IOPS-bound.
---
Strategic Migration Framework: BigQuery to ClickHouse Playbook
Transitioning mission-critical production telemetry from BigQuery to ClickHouse requires a phased cutover strategy that avoids data loss and validates analytical fidelity.
flowchart LR
Phase1["Phase 1: Dual Ingestion"] --> Phase2["Phase 2: Historical Backfill"]
Phase2 --> Phase3["Phase 3: Shadow Query Validation"]
Phase3 --> Phase4["Phase 4: Dashboard Traffic Cutover"]
Phase4 --> Phase5["Phase 5: BigQuery Telemetry Sunsetting"]1. Phase 1: Dual Ingestion via Streaming Fabric: Route telemetry events from Apache Kafka or Google Cloud Pub/Sub to both BigQuery (via existing Dataflow or Storage Write API pipelines) and the target ClickHouse cluster simultaneously using Vector or a dedicated consumer group.
2. Phase 2: Partitioned Historical Backfill: Export historical BigQuery partitions to Google Cloud Storage in Parquet format. Ingest Parquet files directly into ClickHouse using the s3() or gcs() table function:
INSERT INTO telemetry.service_metrics_v2
SELECT * FROM gcs('https://storage.googleapis.com/telemetry-backfill-bucket/2026/*.parquet', 'ACCESS_KEY', 'SECRET_KEY');3. Phase 3: Shadow Query Validation & Parity Benchmarking: Mirror production dashboard queries to both backends. Compare p95 response latencies, data consistency, and aggregation accuracy across window functions.
4. Phase 4: Read Traffic Cutover: Switch Grafana datasources, APM API endpoints, and internal operational dashboards to point directly to ClickHouse.
5. Phase 5: Sunsetting & Cold Archival: Retain BigQuery for high-level business intelligence reporting or query ClickHouse directly from BigQuery using BigQuery Omni / federated queries if cross-system analytical joins are required.
Executing a high-throughput database migration while preserving analytical SLAs requires experienced distributed systems engineering. Engaging with data analytics consulting services provides your engineering organization with architectural audits, customized schema optimization, and resilient pipeline deployment to ensure cost reduction without operational downtime.
---
Frequently Asked Questions
When should an enterprise retain BigQuery rather than migrate to ClickHouse for telemetry?
An enterprise should keep telemetry in BigQuery if monthly event volume is below 50 million records, if analytical queries are executed infrequently by human analysts rather than continuous automated dashboards, or if the engineering organization lacks dedicated database infrastructure staff. BigQuery is also preferable when telemetry data must be frequently joined against complex relational business data (such as Salesforce CRM records or billing tables) in multi-table SQL queries, or when strict corporate standards demand a zero-management serverless operating model.
How does ClickHouse Cloud compare to self-hosted ClickHouse on AWS or GCP in total operational cost?
ClickHouse Cloud decouples compute from object storage and provides automated cluster scaling, multi-AZ high availability, and automated maintenance. Billed through ClickHouse Compute Units (CCUs) and managed storage, ClickHouse Cloud compute and storage rates carry an infrastructure markup—typically 2x to 3.5x higher than raw underlying unmanaged cloud virtual machines and raw object storage alone. However, for engineering organizations with lean platform teams, ClickHouse Cloud often yields a lower Total Cost of Ownership because it eliminates the need to allocate dedicated Site Reliability Engineers (SREs) or database administrators to manage ClickHouse Keeper consensus, Linux kernel tuning, and disk rebalancing.
Does ClickHouse completely replace an enterprise data warehouse?
ClickHouse is specialized for high-volume time-series, log, trace, and event analytics requiring sub-second latency. It does not replace a general-purpose enterprise data warehouse (such as BigQuery, Snowflake, or Databricks) for complex relational modeling, deep hierarchical joins across dozens of dimensional tables, or ERP/financial reporting. Leading data engineering teams deploy a two-tier architecture: ClickHouse powers real-time operational telemetry and client-facing dashboards, while curated subsets or batch summaries are replicated to BigQuery for cross-functional business intelligence.
What is the query performance penalty of using the FINAL modifier in ReplacingMergeTree?
The FINAL modifier forces ClickHouse to perform on-the-fly deduplication across all unmerged data parts during query execution. While this guarantees deduplicated data accuracy, it bypasses certain parallel block reading optimizations and can increase query latency by 2x to 5x on partitions containing dozens of unmerged parts. To mitigate this penalty, ensure writes are properly buffered to keep part counts low, utilize argMax() aggregations where feasible, or enable the do_not_merge_across_partitions_select_final setting in modern ClickHouse releases.
---
Primary Engineering References & Standards
- ClickHouse Architecture and Storage Engine Documentation – MergeTree internals, codecs, and sparse indexing.
- Google Cloud BigQuery Storage Write API Specification – Ingestion stream mechanics and exactly-once semantics.
- Gorilla: A Fast, Scalable, In-Memory Time Series Database – Pelkonen et al., VLDB 2015. Floating-point XOR compression algorithms.
- Dremel: Interactive Analysis of Web-Scale Datasets – Melnik et al., Google Systems Research. Multi-level execution trees and slot scheduling.
- ClickHouse Asynchronous Inserts Guide – Buffer management and part consolidation strategies.
- Google Cloud Architecture Center: BigQuery Cost Optimization – On-demand versus Editions slot allocation FinOps playbooks.
- ClickBench: Analytical DBMS Benchmark – Reproducible query latency profiles on multi-billion row datasets across cloud architectures.
- AWS EC2 and EBS On-Demand Pricing – Hardware instance and storage volume cost references.