Điểm giới hạn: Khi SQL “chạm tường”
Năm ngoái, nền tảng IoT của chúng tôi đã gặp phải rào cản hiệu suất mà không có biện pháp lập chỉ mục (indexing) nào có thể khắc phục được. Chúng tôi quản lý 1.200 cảm biến công nghiệp, mỗi cảm biến gửi dữ liệu nhiệt độ, độ ẩm và độ rung mỗi giây một lần. Đó là 72.000 lượt ghi mỗi phút, tương đương hơn 100 triệu dòng mới mỗi ngày. Ban đầu, chúng tôi đẩy mọi thứ vào một instance PostgreSQL tiêu chuẩn chạy trên máy chủ đám mây RAM 16GB. Nó vẫn ổn định trong tháng đầu tiên, nhưng sau đó mọi thứ bắt đầu đổ vỡ.
Các bảng điều khiển (dashboard) từng hiển thị ngay lập tức bỗng nhiên bị treo trong 20 giây. Các phép toán đơn giản—như tìm đỉnh rung trung bình trong một giờ qua—khiến CPU cơ sở dữ liệu luôn ở mức 100%, khóa quyền truy cập của những người dùng khác. Mặc dù tôi rất thích PostgreSQL cho dữ liệu quan hệ, tôi nhận ra rằng mình đang cố gắng dùng một chiếc sedan hạng sang để kéo 50 tấn sỏi. Chúng tôi cần một công cụ công nghiệp chuyên dụng được xây dựng cho việc nạp dữ liệu time-series.
Tại sao cơ sở dữ liệu tiêu chuẩn lại bị “nghẹt thở”
Vấn đề không phải là MySQL hay Postgres “chậm”. Vấn đề nằm ở cấu trúc cốt lõi của chúng. Các cơ sở dữ liệu truyền thống sử dụng B-Tree để lập chỉ mục. Điều này hoạt động tuyệt vời khi tìm kiếm một ID người dùng cụ thể, nhưng việc ghi dữ liệu IoT tần suất cao gây ra tình trạng phân mảnh chỉ mục (index fragmentation) nghiêm trọng. Mỗi lần đọc cảm biến gửi đến buộc cơ sở dữ liệu phải sắp xếp lại và cập nhật chỉ mục trên đĩa, dẫn đến vòng xoáy tử thần về IOPS.
Khả năng lưu giữ dữ liệu (data retention) là “kẻ giết người thầm lặng” khác. Trong thế giới IoT, bạn hiếm khi cần độ chính xác đến từng mili giây cho dữ liệu đã hai năm tuổi. Tuy nhiên, việc chạy lệnh DELETE trên một bảng SQL có hàng tỷ dòng về cơ bản là một cuộc tấn công DDoS tự thân. Nó khóa bảng, làm phình log giao dịch (transaction logs) và thường thất bại trong việc giải phóng không gian đĩa thực tế do cấu trúc của các tệp bên dưới.
Chọn đúng công cụ cho công việc
Trước khi quyết định di chuyển dữ liệu, tôi đã thử nghiệm ba kiến trúc khác nhau:
- TimescaleDB: Một extension ổn định của Postgres. Lựa chọn tốt nhất nếu các chỉ số của bạn gắn liền với các bảng quan hệ phức tạp. Tuy nhiên, dung lượng lưu trữ vẫn tương đối cao so với các engine chuyên về time-series.
- MongoDB: Tuyệt vời cho schema linh hoạt, nhưng cực kỳ tốn RAM cho dữ liệu time-series. Nó thiếu các hàm gốc cho những việc như tính đạo hàm hoặc trung bình động, buộc bạn phải xử lý nặng trong mã ứng dụng.
- InfluxDB: Đây là người chiến thắng. Nó sử dụng engine Time-Structured Merge Tree (TSM), được thiết kế đặc biệt for việc ghi dữ liệu tốc độ cao và nén dữ liệu mạnh mẽ.
Chúng tôi chọn InfluxDB vì agent “Telegraf” đã xử lý sẵn luồng nạp dữ liệu (ingestion pipeline) và khả năng nén hứa hẹn tiết kiệm hàng nghìn đô la chi phí lưu trữ hàng tháng.
Thiết lập nền tảng
Việc triển khai một instance sẵn sàng cho môi trường production chỉ mất vài phút với Docker. Chúng tôi đã triển khai image InfluxDB 2.7, tích hợp sẵn UI, storage engine và trình lập lịch tác vụ (task scheduler) vào một container.
docker run -d -p 8086:8086 \
--name influxdb_production \
-v /var/lib/influxdb2:/var/lib/influxdb2 \
influxdb:2.7
Sau khi truy cập http://localhost:8086, bạn sẽ thiết lập một **Organization** và một **Bucket**. Trong InfluxDB, một Bucket không chỉ là một cơ sở dữ liệu; nó là một container với ngày hết hạn được tích hợp sẵn. Bạn quyết định ngay từ ngày đầu tiên dữ liệu sẽ tồn tại trong bao lâu trước khi được tự động xóa bỏ.
Telegraf: Bí mật của sự ổn định
Đừng lãng phí thời gian viết các tập lệnh Python hoặc Node.js tùy chỉnh để đẩy dữ liệu. Chúng tôi đã sử dụng Telegraf, một agent nhẹ xử lý các phần “rắc rối” của mạng. Nó gom các điểm dữ liệu lại (batching), xử lý việc gửi lại nếu cơ sở dữ liệu bận và phân tích các luồng MQTT một cách trực tiếp.
Dưới đây là đoạn cấu hình chúng tôi sử dụng để kết nối MQTT broker với InfluxDB:
[[outputs.influxdb_v2]]
urls = ["http://influxdb:8086"]
token = "${INFLUX_TOKEN}"
organization = "industrial-iot"
bucket = "raw_sensor_data"
[[inputs.mqtt_consumer]]
servers = ["tcp://broker.internal:1883"]
topics = ["factory/+/metrics"]
data_format = "json"
Cách tiếp cận này đã loại bỏ gánh nặng xử lý lỗi cho nhóm phát triển firmware. Nếu cơ sở dữ liệu bị sập để bảo trì, Telegraf sẽ đơn giản là đệm dữ liệu vào bộ nhớ cho đến khi kết nối được khôi phục.
Ngôn ngữ truy vấn Flux: Sức mạnh và rào cản
InfluxDB 2.x sử dụng Flux, một ngôn ngữ truy vấn hàm (functional query language). Nếu bạn đã quen với SELECT * FROM, Flux sẽ giống như việc học cách viết mã ngược. Nó sử dụng các đường ống (|>) để truyền dữ liệu qua các bộ lọc và chuyển đổi. Lúc đầu có vẻ lạ lẫm, nhưng nó cực kỳ mạnh mẽ cho việc phân tích.
Để tính trung bình động 5 phút cho một cảm biến cụ thể, mã sẽ trông như thế này:
from(bucket: "raw_sensor_data")
|> range(start: -1h)
|> filter(fn: (r) => r["_measurement"] == "vibration")
|> filter(fn: (r) => r["machine_id"] == "CNC-04")
|> aggregateWindow(every: 5m, fn: mean)
|> yield(name: "smooth_average")
Thực hiện việc này trong SQL đòi hỏi các Window Function hoặc truy vấn con phức tạp. Trong Flux, đó là một đường ống tuyến tính dễ đọc.
Downsampling: Cách chúng tôi tiết kiệm 85% dung lượng đĩa
Phép màu thực sự xảy ra khi chúng tôi tự động hóa vòng đời dữ liệu. Chúng tôi không cần dữ liệu rung động theo từng giây từ ba tháng trước. Chúng tôi chỉ cần các xu hướng. Chúng tôi đã thiết lập một tác vụ để “downsample” dữ liệu của mình thành các cấp độ khác nhau.
- High-Res Bucket: Dữ liệu theo từng giây, tự động xóa sau 7 ngày.
- Archive Bucket: Trung bình mỗi 15 phút, được giữ trong 2 năm.
Dữ liệu thô của chúng tôi tiêu tốn khoảng 40GB đĩa mỗi ngày. Sau khi triển khai tác vụ downsample 15 phút này, tốc độ tăng trưởng lưu trữ dài hạn giảm xuống chỉ còn 150MB mỗi tuần. Đó là sự cắt giảm khổng lồ về chi phí hạ tầng mà không làm mất đi các phân tích lịch sử mà nhóm kinh doanh cần.
Kết luận sau 6 tháng
Chuyển sang một cơ sở dữ liệu time-series chuyên dụng là một bước ngoặt cho sự ổn định của chúng tôi. Thời gian truy vấn giảm từ những giây dài đằng đẵng xuống còn dưới 100ms. Quan trọng hơn, chúng tôi không còn lo lắng về việc cơ sở dữ liệu bị sập khi thêm nhiều cảm biến hơn. Nếu dự án của bạn liên quan đến log, dữ liệu tài chính hoặc chỉ số IoT, hãy ngừng ép nó vào khuôn mẫu quan hệ. Hãy sử dụng một công cụ được thiết kế để chịu được áp lực đó.

