How we made ClickStack 5x faster for ClickHouse observability
Bài tóm tắt ClickHouse 2026 3. Quy mô lớn Độ khó: Khó
ClickHouse · 2026 · 3. Quy mô lớn · Độ khó: Khó — bài gốc
Bài của nhà cung cấp về sản phẩm của họ (ClickStack: log/trace trên ClickHouse); benchmark do họ tự dựng. Các con số là theo ClickHouse.
Bối cảnh
- Schema log/trace mặc định của ClickStack thừa hưởng từ exporter ClickHouse của OpenTelemetry Collector, do cộng đồng đóng góp, chưa dùng tính năng mới của ClickHouse, và chưa từng được benchmark theo workload thật.
- Ở quy mô petabyte, với khách lớn trong private preview và beta: query chậm, Top N trên khoảng thời gian ngắn chậm, search và aggregation bị timeout. Cách xử lý trước đó là sửa vá từng trường hợp.
- Đầu 2026 lập nhóm chuyên: tìm mẫu truy vấn phổ biến trong sản phẩm (search kim-đáy-bể theo trace ID/user ID, search từ phổ biến trên thuộc tính, search trong body log, histogram trên trang search, tra một dòng log, liệt kê key/value cho autocomplete), rồi tối ưu theo chúng.
Cách đo
- Dùng công cụ ClickCannon (dựng song song để trả lời "cần bao nhiêu tài nguyên cho ClickStack") và một framework dựng instance ClickHouse Cloud tạm trên Kubernetes. Cấu hình cố định, chỉ ba biến: schema, workload query, phiên bản ClickHouse. Ingest 640 MiB/s (khoảng 1,5 PB/tháng), 3 replica × 24 core, dữ liệu sắp theo timestamp như thực tế.
- Ghi vào service cha, query trên service con (compute-compute separation) để đo riêng.
- Mỗi thay đổi đánh giá theo thứ tự: ingest (throughput, CPU, RAM, số part tối đa mỗi partition, tức merge có theo kịp insert), rồi tài nguyên phía đọc, rồi từng query; mỗi lượt chạy cả workload rộng để bắt hồi quy ở chỗ khác.
Thay đổi chính
- Primary key bắt đầu bằng bucket thời gian: khoá cũ
(ServiceName, TimestampTime)không cho tối ưuread_in_order(dừng sớm Top N khi đủLIMIT) vì không khớp thứ tự sắp theoTimestamp. Thử bucket 1 phút: kích hoạt được nhưng quá nhiều dải granule nhỏ, search và aggregation chậm đi khi khoảng thời gian dài. ChọntoStartOfFiveMinutes(Timestamp), rồiServiceName: giữ đượcread_in_ordermà dữ liệu vẫn liền khối, nén tốt. - Bỏ cột
TimestampTime: khi đã có bucket 5 phút, cột thời gian độ chính xác giây không còn giúp gì (benchmark không thấy khác), bỏ cho đơn giản. - Text index (inverted, kết quả chính xác) thay bloom filter cho body log và TraceId: bloom filter có dương tính giả nên vẫn phải đọc và kiểm granule; text index loại chính xác và cho
direct_read(trả lời một phần query ngay từ index, không đọc cột). TraceId nhanh hơn tới 22%; tốn thêm lúc insert và dung lượng index (nằm trên object storage nên rẻ). Tra một dòng log lại chậm đi vì chi phí đánh giá index, được bù bằng thay đổi sau. _block_number+_block_offset: trước đây xem chi tiết một dòng log phải filter bằng mọi cột đã trả về (primary key không duy nhất). Bật lưu hai cột ảo này thì tra bằng primary key + vị trí chính xác.- Materialized view cho autocomplete: liệt kê key/value trước đây quét thẳng bảng log/trace (chậm, hoặc lấy mẫu thì thiếu giá trị). Nay ship sẵn rollup
AggregatingMergeTreetheo bucket 15 phút (key, value, tần suất) tính lúc insert; autocomplete không còn tranh tài nguyên với search, xếp gợi ý theo độ phổ biến. - Cột ALIAS
key=valuecho Map + text index: thuộc tính động lưu bằngMapnên tra một key phải đọc và parse cả map; bloom filter riêng cho key và value không biết hai thứ có cùng một cặp không. Ghép mỗi cặp thành chuỗikey=valuetrong một cột ALIAS (không lưu dữ liệu, chỉ để đánh index), viết lạiAttr['foo']='bar'thànhhas(AttrItems, 'foo=bar'): dùng được text index vàdirect_read, có query nhanh tới 10 lần. Index sắp theo token nên tra tiền tốfoo=cũng cho ra danh sách giá trị của một key. Đã cân nhắc kiểu JSON (tự tách đường dẫn hay dùng thành cột) nhưng theo họ chi phí insert hiếm khi đáng với thuộc tính thưa và luôn đổi.
Kết quả (theo ClickHouse)
- Latency các query đại diện giảm hơn 5 lần, trong khi ingest, dung lượng và vận hành vẫn cân bằng. Schema cuối khác hẳn schema ban đầu.
Vì sao
- Hiệu năng là tích của schema và query: UI sinh query khai thác bố cục lưu trữ, nên phải tối ưu cả hai cùng nhau (Query planner và hiệu năng truy vấn).
- Không có benchmark lặp lại được thì mỗi tối ưu có thể làm chậm một query khác mà không ai thấy (ví dụ tra một dòng log chậm đi khi đổi sang text index).
Bài học cho SWE
- Schema mặc định thừa hưởng từ cộng đồng là điểm khởi đầu, không phải điểm tối ưu; đo lại theo mẫu truy vấn thật của sản phẩm.
- Độ mịn của khoá sắp xếp là đánh đổi: mịn quá thì phân mảnh, thô quá thì mất dừng sớm.
- Cố định mọi biến trừ thứ đang thử, đo ingest trước rồi mới đo query, và luôn chạy cả workload để bắt hồi quy.
- Chuyển việc từ lúc query sang lúc insert (rollup, index) cho những truy vấn lặp đi lặp lại như autocomplete (Observability và monitoring).
Liên quan
#ClickStack#Observability#OpenTelemetry#Schema design#Primary key#read_in_order#Text index#Bloom filter#Materialized view#Benchmark
Được nhắc tới trong
Công ty (1)
Bài tóm tắt (1)