§ blog · Server & Database08/06/2026
← Tất cả bài viết

Kiến trúc database cho hệ thống IoT: khi nào dùng TimescaleDB, khi nào dùng ClickHouse

Hai engine time-series phổ biến nhất cho dữ liệu cảm biến — tiêu chí chọn, cách thiết kế schema, và chiến lược lifecycle dữ liệu để chi phí không 'nổ' theo số lượng thiết bị.

Server & DatabaseTimescaleDBClickHouseIoT7 phút đọc
FIG.B-01 · STORAGE TIERS · TIME-SERIES10× COMPRESSIONHOT · TimescaleDB hypertable chunks7 NGÀY GẦN NHẤT · UNCOMPRESSEDRETENTION POLICYCOLD · ClickHouse / ParquetNHIỀU NĂM LỊCH SỬ · COMPRESSEDQUERY p99 — HOT 12ms · COLD ~1s

Một trong những câu hỏi đầu tiên khi thiết kế hệ thống IoT là: dữ liệu cảm biến — hàng triệu điểm đo mỗi ngày, từ hàng trăm hoặc hàng nghìn thiết bị — sẽ được lưu ở đâu? Câu trả lời sai ở giai đoạn này thường không gây lỗi ngay, mà âm thầm tích lũy thành chi phí hạ tầng và độ trễ truy vấn sau 6-12 tháng vận hành. Bài viết này trình bày cách chúng tôi tiếp cận bài toán này khi thiết kế một Pilot Build cho hệ thống giám sát cảm biến công nghiệp — không phải lý thuyết suông, mà là kiến trúc cụ thể có thể áp dụng ngay.

Hai ứng viên hàng đầu: TimescaleDB và ClickHouse

TimescaleDB là một extension của PostgreSQL, biến một bảng thông thường thành "hypertable" — tự động partition theo thời gian. Ưu điểm lớn nhất là bạn vẫn có toàn bộ PostgreSQL: ràng buộc khóa ngoại, transaction, JOIN với bảng metadata (danh sách thiết bị, vị trí lắp đặt, ngưỡng cảnh báo), và một ecosystem ORM/driver quen thuộc với mọi backend engineer.

ClickHouse là một columnar database được thiết kế riêng cho phân tích — đọc nhanh trên hàng tỷ dòng, nén dữ liệu rất tốt (thường 5-10× so với row-based), nhưng đánh đổi: không có transaction thật, UPDATE/DELETE chậm và phức tạp, và JOIN giữa bảng lớn không phải là điểm mạnh của nó.

Tiêu chí chọn — không phải "cái nào tốt hơn" mà là "cái nào đúng bài toán"

  • Số lượng thiết bị < 5,000 và tần suất ghi < 1 điểm/giây/thiết bị → TimescaleDB thường đủ, và đơn giản hóa vận hành vì chỉ cần một Postgres instance
  • Số lượng thiết bị lớn (chục nghìn+) hoặc cần lưu dữ liệu thô tần suất cao (rung động, âm thanh, vài chục Hz) trong thời gian dài → ClickHouse cho hiệu năng đọc và tỷ lệ nén tốt hơn rõ rệt
  • Cần dashboard real-time với độ trễ thấp cho vận hành (operator xem số liệu trực tiếp) → TimescaleDB với continuous aggregates đáp ứng tốt mà không cần thêm hệ thống
  • Cần phân tích lịch sử dài hạn, BI, training mô hình ML trên dữ liệu thô → ClickHouse là lựa chọn tự nhiên cho data warehouse layer

Trong nhiều trường hợp, câu trả lời thực tế là cả hai — nhưng ở hai vai trò khác nhau trong cùng một kiến trúc, không phải thay thế nhau.

Một kiến trúc tham khảo: hot path + cold path

Cách chúng tôi thường đề xuất cho một hệ thống giám sát cảm biến công nghiệp (ví dụ: rung động máy, nhiệt độ, áp suất từ vài nghìn cảm biến) là tách thành hai lớp lưu trữ với vai trò rõ ràng:

Hot path — TimescaleDB

Dữ liệu 7-30 ngày gần nhất, đã được downsample về độ phân giải hợp lý cho dashboard vận hành (ví dụ 1 điểm/phút thay vì 1 điểm/giây). Continuous aggregates của Timescale tính sẵn các giá trị trung bình/min/max theo khung giờ, giúp dashboard load tức thì mà không cần quét dữ liệu thô. Đây là lớp mà operator, alert engine, và API của ứng dụng tương tác trực tiếp.

Cold path — ClickHouse hoặc object storage (Parquet trên S3/R2)

Dữ liệu thô đầy đủ, lưu lâu dài cho mục đích phân tích, audit, và làm dữ liệu training cho mô hình dự báo/anomaly detection. Một job batch (chạy hằng ngày hoặc hằng giờ) đẩy dữ liệu đã "nguội" từ Timescale sang ClickHouse hoặc xuống Parquet, sau đó xóa khỏi hot path để giữ kích thước bảng nóng nhỏ và truy vấn nhanh.

Nguyên tắc thiết kế: lớp nào phục vụ vận hành real-time phải nhỏ và nhanh; lớp nào phục vụ phân tích/training có thể lớn và rẻ — nhưng không cần nhanh tức thì.

Schema design — vài điểm dễ mắc lỗi

  • Luôn có một bảng devices riêng (metadata: vị trí, model, ngưỡng cảnh báo, firmware version) và bảng time-series chỉ chứa device_id, timestamp, metric, value. Tránh nhồi metadata vào mỗi dòng đo — vừa tốn dung lượng, vừa khó cập nhật khi thiết bị đổi cấu hình
  • Dùng kiểu dữ liệu hẹp nhất có thể cho value — float4 thay vì float8/numeric nếu độ chính xác cho phép, vì sự khác biệt nhân với hàng tỷ dòng là rất đáng kể
  • Đặt retention policy ngay từ ngày đầu, không phải khi disk đầy. TimescaleDB có add_retention_policy() tự động xóa chunk cũ; ClickHouse có TTL ở mức bảng/partition
  • Index theo (device_id, timestamp) là gần như bắt buộc — đây là pattern truy vấn phổ biến nhất ("lấy dữ liệu của thiết bị X trong khoảng thời gian Y")

Chi phí — yếu tố thường bị bỏ qua đến khi quá muộn

Một sai lầm phổ biến là thiết kế hệ thống hoạt động tốt ở quy mô pilot (vài chục thiết bị) nhưng chi phí storage tăng tuyến tính — hoặc tệ hơn, siêu tuyến tính do index phình to — khi scale lên hàng nghìn thiết bị. Compression của Timescale (column-oriented storage cho chunk cũ) có thể giảm dung lượng 90%+ so với heap thông thường, nhưng phải được cấu hình rõ ràng (compress_chunk policy), không tự động bật mặc định ở mọi version.

Kết luận

Không có một câu trả lời đúng cho mọi hệ thống IoT — nhưng có một câu hỏi đúng cần trả lời trước: dữ liệu này sẽ được đọc bởi ai, với độ trễ nào, và trong bao lâu? Trả lời câu hỏi đó trước khi chọn database giúp tránh việc phải di trú toàn bộ hệ thống lưu trữ giữa chừng — một trong những công việc tốn kém và rủi ro nhất trong vận hành hệ thống dữ liệu. Đây cũng là một phần của Discovery Sprint mà chúng tôi thực hiện trước mỗi Pilot Build: vẽ ra luồng dữ liệu và khối lượng dự kiến trước khi viết một dòng infrastructure-as-code nào.

Nếu team bạn đang thiết kế hoặc đang gặp vấn đề về chi phí/độ trễ với một hệ thống time-series hiện có, đây chính là loại bài toán nằm trong lớp Server & Database của vòng lặp khép kín mà KonexForge xây dựng — tương tự cách ClickHouse đóng vai trò cold path trong hệ thống giám sát chất lượng nước cho 12 quận mà chúng tôi đã triển khai.

Có một bài toán tương tự đang cần giải?

Liên hệ team