Truy vết phân tán khối lượng lớn bằng OpenTelemetry, Kafka & ClickHouse

Mục lục bài viết(22 mục)
Hệ thống truy vết phân tán ở quy mô petabyte đòi hỏi một pipeline mạnh mẽ, hiệu quả và tiết kiệm chi phí. Hướng dẫn này trình bày chi tiết việc xây dựng một hệ thống như vậy bằng cách sử dụng OpenTelemetry để đo đạc, Apache Kafka để vận chuyển đáng tin cậy và ClickHouse để lưu trữ và truy vấn phân tích hiệu suất cao. Trọng tâm là triển khai thực tế, cấp độ sản xuất, bao gồm thiết kế lược đồ tối ưu, chiến lược lấy mẫu thông minh và các mẫu truy vấn hiệu quả.
Tổng quan kiến trúc
Kiến trúc được đề xuất bao gồm:
- OpenTelemetry SDKs: Đo đạc ứng dụng để tạo ra các trace.
- OpenTelemetry Collector: Nhận, xử lý và xuất dữ liệu trace. Hoạt động như một lớp đệm và xử lý quan trọng.
- Apache Kafka: Một bus thông báo có thông lượng cao, chịu lỗi để thu thập dữ liệu trace thô từ các Collector.
- ClickHouse Kafka Connectors / Consumers: Thu thập dữ liệu từ Kafka vào ClickHouse.
- ClickHouse: Cơ sở dữ liệu phân tích dạng cột để lưu trữ và truy vấn các span trace.
Cấu hình OpenTelemetry Collector
OpenTelemetry Collector đóng vai trò then chốt trong việc quản lý khối lượng trace và đảm bảo tính toàn vẹn của dữ liệu trước khi đưa vào Kafka. Các thành phần chính bao gồm receivers, processors và exporters.
Cấu hình Receiver
Chúng ta sẽ sử dụng OTLP receiver để thu thập giao thức OpenTelemetry tiêu chuẩn.
# otel-collector-config.yaml
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317 # Standard OTLP gRPC port
http:
endpoint: 0.0.0.0:4318 # Standard OTLP HTTP port
Cấu hình Processor: Batching, Memory Limiting và Tail-Based Sampling
Các processor rất quan trọng để tối ưu hóa luồng dữ liệu.
- Batch Processor: Giảm chi phí mạng bằng cách tổng hợp các span thành các batch.
- Memory Limiter: Ngăn Collector tiêu thụ quá nhiều bộ nhớ, rất quan trọng dưới tải cao.
- Tail-Based Sampler: Thực hiện các quyết định lấy mẫu thông minh sau khi tất cả các span cho một trace đã được nhận. Điều này cho phép các chính sách như "luôn giữ các trace lỗi" trong khi lấy mẫu mạnh mẽ các trace "khỏe mạnh".
# otel-collector-config.yaml
processors:
batch:
send_batch_size: 1024 # Target batch size
timeout: 5s # Max time to wait for a batch
memory_limiter:
check_interval: 1s
limit_mib: 2048 # Max 2GB memory usage
spike_limit_mib: 512 # Max 512MB spike
tail_sampling:
decision_wait: 10s # How long to wait for a trace to complete before making a sampling decision
num_traces: 100000 # Max number of traces to hold in memory for sampling
expected_new_traces_per_sec: 10000 # Expected new traces per second
policies:
[
{
name: "always-sample-errors",
type: status_code,
status_code: {
status_codes: [ERROR, UNSET], # Keep traces with ERROR or UNSET status
drop_nested_spans: false
}
},
{
name: "drop-ok-traces",
type: probabilistic,
probabilistic: {
sampling_percentage: 5 # Keep 5% of all other traces (e.g., 200 OK)
}
}
]
Cấu hình lấy mẫu dựa trên đuôi này đảm bảo rằng:
- Bất kỳ trace nào chứa một span có mã trạng thái
ERRORhoặcUNSETluôn được giữ lại. Điều này rất quan trọng để gỡ lỗi các vấn đề sản xuất. - Đối với tất cả các trace khác (thường là các phản hồi
200 OKkhỏe mạnh), chỉ 5% được giữ lại. Điều này làm giảm đáng kể khối lượng dữ liệu cho các trace không quan trọng.
Cấu hình Exporter: Kafka
Kafka exporter gửi các batch đã xử lý đến một topic Kafka. Quan trọng là, hãy cấu hình sending_queue để có khả năng phục hồi.
# otel-collector-config.yaml
exporters:
kafka:
brokers: ["kafka-broker-1:9092", "kafka-broker-2:9092"]
topic: "opentelemetry-traces"
encoding: "otlp_json" # Export in OTLP JSON format for easier consumption
protocol_version: "2.0.0"
# Essential for production: buffer spans if Kafka is unavailable
sending_queue:
enabled: true
queue_size: 5000 # Number of batches to buffer (e.g., 5000 * 1024 spans)
# Optional: Configure retries for transient Kafka issues
retry_on_failure:
enabled: true
initial_interval: 5s
max_interval: 30s
max_elapsed_time: 5m
Service Pipeline
Cuối cùng, tập hợp các thành phần vào một service pipeline.
# otel-collector-config.yaml
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, tail_sampling, batch]
exporters: [kafka]
Thiết kế lược đồ ClickHouse cho Traces
Một lược đồ ClickHouse được tối ưu hóa là nền tảng cho việc truy vết ở quy mô petabyte. Chúng ta cần thu thập nhanh, lưu trữ hiệu quả và hiệu suất truy vấn dưới một giây cho các mẫu phân tích trace phổ biến.
Cấu trúc bảng
Chúng ta sẽ sử dụng engine MergeTree, phù hợp cho các ghi có khối lượng lớn và các truy vấn phân tích. Các cân nhắc chính:
trace_id: Định danh chính cho một trace. Nên được lập chỉ mục.span_id: Định danh duy nhất cho một span trong một trace.parent_span_id: Để tái tạo các hệ thống phân cấp trace.service_name: Quan trọng cho việc lọc và tổng hợp. Sử dụngLowCardinality(String)để tăng hiệu quả.operation_name: Tương tự nhưservice_name.start_time_us,end_time_us: Dấu thời gian với độ chính xác micro giây.duration_us: Thời lượng được tính toán trước để truy vấn nhanh hơn.status_code,status_message: Để phân tích lỗi.attributes: Lưu trữ các thuộc tính span dưới dạngMap(String, String)hoặcArray(Tuple(String, String))để linh hoạt.Mapthường tiện lợi hơn cho việc truy vấn.resource_attributes: Các thuộc tính từ tài nguyên OpenTelemetry (ví dụ: host, environment). Cũng làMap(String, String).
CREATE TABLE IF NOT EXISTS traces.spans
(
`trace_id` String CODEC(ZSTD(1)),
`span_id` String CODEC(ZSTD(1)),
`parent_span_id` String CODEC(ZSTD(1)),
`trace_state` String CODEC(ZSTD(1)),
`service_name` LowCardinality(String) CODEC(ZSTD(1)),
`operation_name` LowCardinality(String) CODEC(ZSTD(1)),
`kind` LowCardinality(String) CODEC(ZSTD(1)),
`start_time_us` UInt64 CODEC(Delta, ZSTD(1)),
`end_time_us` UInt64 CODEC(Delta, ZSTD(1)),
`duration_us` UInt64 CODEC(Delta, ZSTD(1)),
`status_code` UInt8 CODEC(ZSTD(1)),
`status_message` String CODEC(ZSTD(1)),
`attributes` Map(LowCardinality(String), String) CODEC(ZSTD(1)),
`resource_attributes` Map(LowCardinality(String), String) CODEC(ZSTD(1)),
`events.time_unix_nano` Array(UInt64) CODEC(Delta, ZSTD(1)),
`events.name` Array(LowCardinality(String)) CODEC(ZSTD(1)),
`events.attributes` Array(Map(LowCardinality(String), String)) CODEC(ZSTD(1)),
`links.trace_id` Array(String) CODEC(ZSTD(1)),
`links.span_id` Array(String) CODEC(ZSTD(1)),
`links.trace_state` Array(String) CODEC(ZSTD(1)),
`links.attributes` Array(Map(LowCardinality(String), String)) CODEC(ZSTD(1)),
`ingestion_time_ms` DateTime64(3, 'UTC') DEFAULT now() CODEC(Delta, ZSTD(1))
)
ENGINE = MergeTree
PARTITION BY toYYYYMMDD(toDateTime(start_time_us / 1000000))
ORDER BY (service_name, start_time_us, trace_id, span_id)
PRIMARY KEY (service_name, start_time_us)
TTL toDateTime(start_time_us / 1000000) + INTERVAL 30 DAY
SETTINGS index_granularity = 8192, merge_with_ttl_timeout = 3600;
-- Secondary index for faster trace_id lookups
ALTER TABLE traces.spans ADD INDEX trace_id_idx trace_id TYPE bloom_filter GRANULARITY 1;
Giải thích các lựa chọn lược đồ:
CODEC(ZSTD(1)): Nén ZSTD với cấp độ 1 mang lại sự cân bằng tốt giữa tỷ lệ nén và mức sử dụng CPU.LowCardinality(String): Quan trọng đối với các trường nhưservice_name,operation_name,kindvà các khóa map. Nó lưu trữ một từ điển các giá trị duy nhất, giảm đáng kể dung lượng lưu trữ và cải thiện hiệu suất truy vấn cho việc lọc và nhóm.Deltacodec cho dấu thời gian và thời lượng: Nén hiệu quả các chuỗi tăng hoặc giảm đơn điệu.Map(LowCardinality(String), String): Đối vớiattributesvàresource_attributes. ClickHouse có thể truy vấn trực tiếp các khóa và giá trị map. Sử dụngLowCardinalitycho các khóa map là một tối ưu hóa đáng kể.PARTITION BY toYYYYMMDD(...): Phân vùng dữ liệu theo ngày, cho phép giữ lại dữ liệu hiệu quả (TTL) và cắt tỉa cho các truy vấn theo phạm vi thời gian.ORDER BY (service_name, start_time_us, trace_id, span_id): Định nghĩa thứ tự vật lý của dữ liệu trong các phân vùng. Thứ tự này rất quan trọng cho các truy vấn phạm vi trênstart_time_usvà lọc theoservice_name.trace_idvàspan_idđược bao gồm để đảm bảo tính duy nhất và tra cứu hiệu quả trong một dịch vụ/phạm vi thời gian.PRIMARY KEY (service_name, start_time_us): Khóa chính là tiền tố của khóaORDER BY. Nó định nghĩa chỉ mục thưa thớt được sử dụng để bỏ qua dữ liệu nhanh chóng.TTL toDateTime(start_time_us / 1000000) + INTERVAL 30 DAY: Tự động xóa dữ liệu cũ hơn 30 ngày dựa trên thời gian bắt đầu của span.ALTER TABLE ... ADD INDEX trace_id_idx trace_id TYPE bloom_filter GRANULARITY 1: Một chỉ mục bloom filter thứ cấp trêntrace_idtăng tốc đáng kể các truy vấnWHERE trace_id = '...', vốn phổ biến để truy xuất toàn bộ trace.GRANULARITY 1có nghĩa là chỉ mục được xây dựng cho mọi phần dữ liệu, cung cấp khả năng lọc chi tiết.
Thu thập từ Kafka vào ClickHouse
ClickHouse có thể trực tiếp tiêu thụ từ Kafka bằng cách sử dụng engine bảng Kafka. Điều này đơn giản hóa pipeline thu thập bằng cách loại bỏ nhu cầu về một ứng dụng consumer riêng biệt.
CREATE TABLE IF NOT EXISTS traces.spans_kafka_raw
(
`trace_id` String,
`span_id` String,
`parent_span_id` String,
`trace_state` String,
`service_name` String,
`operation_name` String,
`kind` String,
`start_time_us` UInt64,
`end_time_us` UInt64,
`duration_us` UInt64,
`status_code` UInt8,
`status_message` String,
`attributes` String, -- Raw JSON string for attributes
`resource_attributes` String, -- Raw JSON string for resource attributes
`events.time_unix_nano` Array(UInt64),
`events.name` Array(String),
`events.attributes` Array(String), -- Raw JSON string for event attributes
`links.trace_id` Array(String),
`links.span_id` Array(String),
`links.trace_state` Array(String),
`links.attributes` Array(String) -- Raw JSON string for link attributes
)
ENGINE = Kafka
SETTINGS
kafka_broker_list = 'kafka-broker-1:9092,kafka-broker-2:9092',
kafka_topic_list = 'opentelemetry-traces',
kafka_group_name = 'clickhouse_otel_consumer_group',
kafka_format = 'JSONEachRow', -- OTLP JSON is essentially JSONEachRow
kafka_num_consumers = 4, -- Number of parallel consumers
kafka_max_block_size = 1048576, -- Max bytes per block
kafka_skip_broken_messages = 10; -- Skip up to 10 broken messages per block
Sau đó, sử dụng một materialized view để chuyển đổi và chèn dữ liệu từ bảng Kafka vào bảng spans đã được tối ưu hóa. Điều này cho phép chuyển đổi kiểu dữ liệu (ví dụ: chuỗi JSON sang Map), gán giá trị mặc định và tính toán trước duration_us.
CREATE MATERIALIZED VIEW IF NOT EXISTS traces.spans_mv TO traces.spans AS
SELECT
JSONExtractString(message, 'resource.attributes.service.name') AS service_name, -- Extract service name from resource attributes
JSONExtractString(message, 'span_id') AS span_id,
JSONExtractString(message, 'trace_id') AS trace_id,
JSONExtractString(message, 'parent_span_id') AS parent_span_id,
JSONExtractString(message, 'trace_state') AS trace_state,
JSONExtractString(message, 'name') AS operation_name,
JSONExtractString(message, 'kind') AS kind,
JSONExtractUInt(message, 'start_time_unix_nano') / 1000 AS start_time_us,
JSONExtractUInt(message, 'end_time_unix_nano') / 1000 AS end_time_us,
(JSONExtractUInt(message, 'end_time_unix_nano') - JSONExtractUInt(message, 'start_time_unix_nano')) / 1000 AS duration_us,
JSONExtractUInt(message, 'status.code') AS status_code,
JSONExtractString(message, 'status.message') AS status_message,
JSONExtract(message, 'attributes', 'Map(LowCardinality(String), String)') AS attributes,
JSONExtract(message, 'resource.attributes', 'Map(LowCardinality(String), String)') AS resource_attributes,
arrayMap(x -> JSONExtractUInt(x, 'time_unix_nano'), JSONExtractArrayRaw(message, 'events')) AS `events.time_unix_nano`,
arrayMap(x -> JSONExtractString(x, 'name'), JSONExtractArrayRaw(message, 'events')) AS `events.name`,
arrayMap(x -> JSONExtract(x, 'attributes', 'Map(LowCardinality(String), String)'), JSONExtractArrayRaw(message, 'events')) AS `events.attributes`,
arrayMap(x -> JSONExtractString(x, 'trace_id'), JSONExtractArrayRaw(message, 'links')) AS `links.trace_id`,
arrayMap(x -> JSONExtractString(x, 'span_id'), JSONExtractArrayRaw(message, 'links')) AS `links.span_id`,
arrayMap(x -> JSONExtractString(x, 'trace_state'), JSONExtractArrayRaw(message, 'links')) AS `links.trace_state`,
arrayMap(x -> JSONExtract(x, 'attributes', 'Map(LowCardinality(String), String)'), JSONExtractArrayRaw(message, 'links')) AS `links.attributes`
FROM traces.spans_kafka_raw;
Lưu ý về cấu trúc JSON của OTLP: Mã hóa otlp_json từ Kafka exporter của OpenTelemetry Collector tạo ra một đối tượng JSON cho mỗi span. Các hàm JSONExtract được sử dụng để phân tích cấu trúc này. service_name thường được tìm thấy trong resource.attributes.
Truy vấn Trace SQL dưới một giây
Tận dụng lược đồ được tối ưu hóa, các truy vấn trace phổ biến có thể đạt hiệu suất dưới một giây.
1. Tìm một Trace theo ID
SELECT
trace_id,
span_id,
parent_span_id,
service_name,
operation_name,
kind,
start_time_us,
duration_us,
status_code,
status_message,
attributes,
resource_attributes
FROM traces.spans
WHERE trace_id = 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6'
ORDER BY start_time_us ASC;
Truy vấn này được hưởng lợi từ chỉ mục bloom_filter trên trace_id.
2. Tìm các Trace lỗi cho một Service trong một Phạm vi thời gian
SELECT
trace_id,
service_name,
operation_name,
start_time_us,
duration_us,
status_code,
status_message
FROM traces.spans
WHERE service_name = 'my-critical-service'
AND status_code != 0 -- OTLP status_code 0 is OK
AND toDateTime(start_time_us / 1000000) BETWEEN '2023-10-26 00:00:00' AND '2023-10-26 23:59:59'
ORDER BY start_time_us DESC
LIMIT 100;
Truy vấn này tận dụng mệnh đề PRIMARY KEY và ORDER BY trên service_name và start_time_us để lọc và sắp xếp hiệu quả.
3. Top N Thao tác chậm nhất cho một Service
SELECT
operation_name,
avg(duration_us) AS avg_duration_us,
count() AS total_spans
FROM traces.spans
WHERE service_name = 'my-api-gateway'
AND toDateTime(start_time_us / 1000000) BETWEEN now() - INTERVAL 1 HOUR AND now()
GROUP BY operation_name
ORDER BY avg_duration_us DESC
LIMIT 10;
LowCardinality(operation_name) làm cho các thao tác GROUP BY rất nhanh.
4. Traces với các thuộc tính cụ thể
SELECT
trace_id,
service_name,
operation_name,
attributes['http.method'] AS http_method,
attributes['http.status_code'] AS http_status_code
FROM traces.spans
WHERE service_name = 'my-web-app'
AND attributes['http.method'] = 'POST'
AND attributes['http.status_code'] = '500'
AND toDateTime(start_time_us / 1000000) BETWEEN now() - INTERVAL 1 DAY AND now()
LIMIT 50;
ClickHouse truy vấn các khóa và giá trị map một cách hiệu quả.
So sánh Kiến trúc & Đánh đổi
| Tính năng | OpenTelemetry Collector + Kafka + ClickHouse | Nền tảng Tracing SaaS (ví dụ: Datadog, Honeycomb) | Jaeger/Zipkin + Elasticsearch |
|---|---|---|---|
| Chi phí | Thấp (cơ sở hạ tầng + kỹ thuật) | Cao (mỗi GB/span) | Trung bình (cơ sở hạ tầng + kỹ thuật) |
| Khả năng mở rộng | Quy mô petabyte, khả năng mở rộng cao | Tuyệt vời, được quản lý | Tốt, nhưng ES có thể gặp khó khăn với tính đa dạng cao |
| Lưu giữ dữ liệu | Hoàn toàn có thể tùy chỉnh (TTL) | Các cấp độ do nhà cung cấp định nghĩa | Có thể tùy chỉnh, nhưng lưu trữ ES đắt tiền |
| Hiệu suất truy vấn | Dưới một giây cho các truy vấn phân tích phức tạp | Tuyệt vời, được tối ưu hóa cho tracing | Tốt cho các tra cứu đơn giản, chậm hơn cho các tổng hợp |
| Tính linh hoạt | Cao (lược đồ tùy chỉnh, lấy mẫu, xử lý) | Hạn chế đối với các tính năng của nền tảng | Trung bình (lược đồ, lấy mẫu) |
| Chi phí vận hành | Cao (quản lý Kafka, ClickHouse, OTel) | Thấp (dịch vụ được quản lý) | Trung bình (quản lý ES, Cassandra) |
| Kiểm soát lấy mẫu | Lấy mẫu dựa trên đuôi chi tiết | Thường là lấy mẫu dựa trên đầu hoặc lấy mẫu dựa trên đuôi hạn chế | Lấy mẫu dựa trên đầu, một số tùy chọn lấy mẫu dựa trên đuôi |
| Quyền sở hữu dữ liệu | Hoàn toàn | Do nhà cung cấp kiểm soát | Hoàn toàn |
Các vấn đề và khắc phục sự cố trong sản xuất
-
Áp lực ngược Kafka / Tràn bộ đệm OTel Collector:
- Triệu chứng: Nhật ký OpenTelemetry Collector hiển thị lỗi
sending_queueđầy, số liệudropped_spanstăng. Độ trễ của Kafka consumer tăng. - Nguyên nhân: Các broker Kafka chậm, hoặc việc thu thập của ClickHouse không thể theo kịp Kafka.
- Khắc phục:
- Mở rộng Kafka: Thêm nhiều broker, tăng số phân vùng topic.
- Mở rộng ClickHouse: Thêm nhiều node ClickHouse (cho các bảng phân tán), tối ưu hóa các hợp nhất của ClickHouse.
- Tối ưu hóa lược đồ ClickHouse: Đảm bảo
LowCardinalityđược sử dụng khi thích hợp, các chỉ mục hiệu quả. - Tăng
sending_queuecủa OTel Collector: Cung cấp thêm bộ đệm, nhưng chỉ trì hoãn điều không thể tránh khỏi nếu hệ thống hạ nguồn thực sự bị tắc nghẽn. - Lấy mẫu mạnh mẽ: Nếu tất cả các cách khác đều thất bại, hãy tăng tỷ lệ lấy mẫu trong OTel Collector để giảm tổng khối lượng.
- Triệu chứng: Nhật ký OpenTelemetry Collector hiển thị lỗi
-
Kích thước từ điển
LowCardinalitycủa ClickHouse bị vượt quá:- Triệu chứng: Nhật ký ClickHouse hiển thị các lỗi như
Too many unique values for LowCardinality type. Các truy vấn có thể thất bại hoặc trở nên cực kỳ chậm. - Nguyên nhân: Một cột
LowCardinalityđược sử dụng cho dữ liệu có tính đa dạng rất cao (ví dụ: ID yêu cầu duy nhất, URL đầy đủ không có mẫu đường dẫn). - Khắc phục:
- Xác định thủ phạm: Truy vấn
SELECT column_name, count(DISTINCT column_name) FROM traces.spans GROUP BY column_name ORDER BY count() DESC; - Thay đổi kiểu dữ liệu: Đối với các trường có tính đa dạng thực sự cao, hãy chuyển từ
LowCardinality(String)sangString. - Tiền xử lý dữ liệu: Trong OTel Collector hoặc Materialized View, chuẩn hóa các thuộc tính có tính đa dạng cao (ví dụ:
http.urlthànhhttp.route).
- Xác định thủ phạm: Truy vấn
- Triệu chứng: Nhật ký ClickHouse hiển thị các lỗi như
-
Tra cứu
trace_idchậm trong ClickHouse:- Triệu chứng: Các truy vấn như
SELECT ... WHERE trace_id = '...'mất vài giây. - Nguyên nhân: Chỉ mục
bloom_filtertrêntrace_idkhông hiệu quả, hoặc khối lượng dữ liệu trên mỗi phân vùng quá cao. - Khắc phục:
- Xác minh việc tạo chỉ mục: Đảm bảo
ALTER TABLE traces.spans ADD INDEX trace_id_idx trace_id TYPE bloom_filter GRANULARITY 1;đã được thực thi. - Kiểm tra
GRANULARITY: Đối với các phân vùng rất lớn,GRANULARITYlà tối ưu cho các bloom filter trên các ID duy nhất. - Tăng
index_granularity: Nếuindex_granularityquá thấp, bloom filter có thể không bỏ qua đủ dữ liệu. Thử nghiệm với các giá trị như 8192 hoặc 16384. - Kiểm tra các hợp nhất
MergeTree: Đảm bảo ClickHouse đang hợp nhất các phần một cách hiệu quả. Các phần chưa hợp nhất có thể làm giảm hiệu suất truy vấn.
- Xác minh việc tạo chỉ mục: Đảm bảo
- Triệu chứng: Các truy vấn như
-
Độ trễ
Materialized Viewcủa ClickHouse:- Triệu chứng: Dữ liệu xuất hiện trong
spans_kafka_rawnhưng mất nhiều thời gian để hiển thị trongtraces.spans. - Nguyên nhân: Materialized view đang gặp khó khăn trong việc theo kịp tốc độ thu thập của Kafka, hoặc có vấn đề với các hàm
JSONExtract. - Khắc phục:
- Tối ưu hóa
JSONExtract: Đảm bảo các đường dẫn JSON chính xác và không quá phức tạp. - Tăng tài nguyên ClickHouse: Thêm CPU/bộ nhớ cho máy chủ ClickHouse.
- Điều chỉnh các tham số engine Kafka: Tăng
kafka_num_consumers,kafka_max_block_sizetrong bảngspans_kafka_rawđể cho phép ClickHouse kéo các batch lớn hơn từ Kafka. - Giám sát
system.kafka_consumers: Kiểm tra lỗi hoặc độ trễ của consumer.
- Tối ưu hóa
- Triệu chứng: Dữ liệu xuất hiện trong
Các câu hỏi thường gặp
Q1: Làm cách nào để đảm bảo 100% các trace lỗi được ghi lại mà không làm quá tải bộ nhớ?
A1: Triển khai lấy mẫu dựa trên đuôi trong OpenTelemetry Collector. Cấu hình chính sách status_code để luôn lấy mẫu các trace có trạng thái ERROR hoặc UNSET. Đối với tất cả các trace khác, áp dụng chính sách lấy mẫu xác suất (ví dụ: giữ lại 5%). Điều này đảm bảo dữ liệu lỗi quan trọng không bao giờ bị bỏ qua trong khi giảm đáng kể khối lượng các trace khỏe mạnh.
Q2: Tác động của LowCardinality(String) đến hiệu suất và bộ nhớ là gì?
A2: LowCardinality(String) là một tối ưu hóa đáng kể cho các trường có số lượng giá trị duy nhất hạn chế (ví dụ: tên dịch vụ, phương thức HTTP). Nó lưu trữ một từ điển các giá trị duy nhất và sử dụng ID số nguyên trong dữ liệu chính, giảm đáng kể dung lượng lưu trữ và cải thiện hiệu suất truy vấn cho việc lọc, nhóm và sắp xếp trên các cột này. Tuy nhiên, việc sử dụng nó cho dữ liệu có tính đa dạng cao (ví dụ: URL đầy đủ, ID người dùng) có thể dẫn đến sự phát triển từ điển quá mức, các vấn đề về bộ nhớ và suy giảm hiệu suất.
Q3: Làm cách nào để xử lý sự phát triển lược đồ cho các thuộc tính trace trong ClickHouse?
A3: Kiểu Map(LowCardinality(String), String) cho attributes và resource_attributes cung cấp tính linh hoạt tuyệt vời. Các thuộc tính mới có thể được thêm bởi các ứng dụng mà không yêu cầu thay đổi lược đồ trong ClickHouse. Các truy vấn có thể truy cập các thuộc tính mới này bằng cách sử dụng attributes['new.attribute.key']. Nếu một thuộc tính trở nên quan trọng và được truy vấn thường xuyên, hãy cân nhắc trích xuất nó vào một cột chuyên dụng để có hiệu suất và lập chỉ mục tốt hơn.
Q4: Các truy vấn ClickHouse của tôi chậm mặc dù đã tối ưu hóa lược đồ. Tôi nên kiểm tra gì trước?
A4:
- Phạm vi thời gian: Đảm bảo các truy vấn của bạn bao gồm một phạm vi thời gian hẹp (
start_time_us) phù hợp với các khóaPARTITION BYvàORDER BY. Điều này cho phép ClickHouse cắt tỉa các phân vùng và sử dụng chỉ mục chính một cách hiệu quả. - Truy vấn
EXPLAIN: Sử dụngEXPLAINđể hiểu kế hoạch truy vấn. Tìm kiếm các lần quét bảng đầy đủ hoặc các phép nối không hiệu quả. - Sử dụng chỉ mục: Xác minh rằng các chỉ mục thứ cấp (như
bloom_filtertrêntrace_id) đang được sử dụng. - Tính đa dạng: Kiểm tra tính đa dạng của các cột được sử dụng trong các mệnh đề
WHEREhoặcGROUP BY. Nếu một cộtLowCardinalityđã phát triển quá lớn, nó có thể làm giảm hiệu suất. - Số liệu ClickHouse: Giám sát các số liệu máy chủ ClickHouse (CPU, I/O đĩa, hợp nhất, truy vấn đang hoạt động) để xác định các nút thắt cổ chai tài nguyên.
Q5: Làm cách nào để đảm bảo tính bền vững của dữ liệu và khả năng chịu lỗi cho pipeline này?
A5:
- OpenTelemetry Collector: Cấu hình
sending_queueđể đệm vàretry_on_failurecho các vấn đề mạng tạm thời. Triển khai nhiều collector phía sau bộ cân bằng tải. - Kafka: Triển khai một cụm Kafka với sao chép (ví dụ: 3 broker, hệ số sao chép 3) và cấu hình
acksthích hợp cho các producer. - ClickHouse: Sử dụng một cụm ClickHouse với sao chép (ví dụ: engine
ReplicatedMergeTree) và phân mảnh để có khả năng mở rộng theo chiều ngang và khả năng chịu lỗi. Các materialized view có khả năng phục hồi; nếu ClickHouse khởi động lại, chúng sẽ tiếp tục tiêu thụ từ Kafka. - Giám sát: Triển khai giám sát toàn diện cho tất cả các thành phần (OTel Collector, Kafka, ClickHouse) để phát hiện sớm các vấn đề.
Free In-Browser Developer Tools
Clean AI CLI logs, build cron expressions, decode JWTs, and calculate chmod permissions offline.
Related Articles

Kho dữ liệu phân tích Serverless với BigQuery & Cloud Run: Từ luồng GA4 đến cảnh báo SEO tự động
Tìm hiểu cách xây dựng kho dữ liệu phân tích serverless tự động với BigQuery, Google Analytics 4 và Cloud Run: mô hình hóa lược đồ, chuyển đổi SQL theo lịch trình, chi phí không tải và cảnh báo truy vấn SEO tự động.
Read more
Những cạm bẫy tiềm ẩn của kiến trúc Serverless
Khám phá những cạm bẫy tiềm ẩn của kiến trúc serverless vào năm 2026: độ trễ cold start, cạn kiệt kết nối database, hóa đơn đám mây bất ngờ và các biện pháp khắc phục.
Read more
Các phương pháp hay nhất để quản lý Terraform State
Bảo mật và mở rộng quy mô quản lý Terraform state trong môi trường production: triển khai backend S3 từ xa, khóa state bằng DynamoDB, cô lập workspace và phân loại state drift.
Read more