Mở rộng PostgreSQL cho dữ liệu Time-Series với TimescaleDB: Đánh giá 6 tháng chạy Production

Database tutorial - IT technology blog
Database tutorial - IT technology blog

Vấn đề thực tế: Điểm nghẽn dữ liệu đo từ xa IoT

Sáu tháng trước, đội ngũ kỹ thuật của chúng tôi vấp phải rào cản hiệu năng nghiêm trọng với nền tảng giám sát IoT. Chúng tôi theo dõi dữ liệu đo từ xa—nhiệt độ, độ ẩm, điện áp và độ rung—từ khoảng 5.000 gateway công nghiệp được triển khai tại bốn nhà xưởng. Với mỗi thiết bị gửi dữ liệu định kỳ mỗi 5 giây, tầng ingest liên tục đẩy 1.000 lượt ghi mỗi giây trực tiếp vào một bảng PostgreSQL tiêu chuẩn.

Thời gian đầu, cơ sở dữ liệu vẫn đáp ứng tốt mà không gặp trở ngại nào. Máy chủ 8 core, 32 GB RAM dễ dàng tiếp nhận 10 triệu dòng đầu tiên. Tuy nhiên, sang tháng thứ hai, bảng telemetry đã phình to vượt mốc 150 triệu dòng. Đó là lúc độ trễ bắt đầu xuống dốc trên toàn bộ hệ thống:

  • Độ trễ ghi tăng vọt: Thời gian phản hồi ingest tăng từ 8ms lên hơn 450ms. Các tác vụ ghi theo batch thường xuyên bị timeout trong những ca làm việc buổi sáng cao điểm của nhà máy.
  • Dashboard bị timeout: Các dashboard Grafana tính toán giá trị trung bình 5 phút của toàn bộ thiết bị giảm hiệu năng từ phản hồi dưới một giây xuống mức ì ạch hơn 25 giây, liên tục trả về lỗi HTTP 504 Gateway Timeouts.
  • Nghẽn I/O nghiêm trọng: Các chu kỳ VACUUM định kỳ và autovacuum vắt kiệt thông lượng ổ đĩa NVMe ở mức 450 MB/s, khiến các transaction đang hoạt động bị đói tài nguyên I/O.

Phân tích nguyên nhân gốc rễ: Tại sao PostgreSQL tiêu chuẩn bị nghẽn với Time-Series

Chúng tôi đã đào sâu vào execution plan, tỷ lệ hit buffer và thống kê I/O của kernel trong PostgreSQL để chẩn đoán sự cố. Ba điểm nghẽn kiến trúc nhanh chóng lộ diện:

1. B-Tree Index bị đẩy khỏi RAM

Khi các bảng tiêu chuẩn tăng lên hàng trăm triệu dòng, B-Tree index cũng phình to theo tương ứng. Composite index trên (device_id, recorded_at) của chúng tôi vượt mốc 32 GB, áp đảo hoàn toàn vùng nhớ shared_buffers 8 GB. Các lượt ghi mới không còn có thể cập nhật các trang lá của index trên bộ nhớ. Thay vào đó, mỗi thao tác insert đều buộc phải đọc ngẫu nhiên từ ổ đĩa, biến các thao tác ghi tuần tự thành nút thắt cổ chai I/O.

2. Quét dữ liệu tốn kém cho các truy vấn tổng hợp phân tích

Các truy vấn dashboard tính toán dữ liệu tổng hợp theo giờ hoặc theo ngày phải quét hàng triệu dòng thô trong mỗi lần làm mới. PostgreSQL phải nạp hàng nghìn heap page chỉ để tính toán các phép trung bình cộng đơn giản. Điều này làm lãng phí CPU và băng thông bộ nhớ cho lượng dữ liệu lịch sử vốn không hề thay đổi trong nhiều tuần.

3. Tích tụ Dead Tuple và phình bảng (Table Bloat)

Để dọn dẹp dữ liệu cũ hơn 90 ngày, chúng tôi phải chạy các batch DELETE theo lịch trình. Do PostgreSQL chỉ đánh dấu các dòng đã xóa là dead tuple thay vì thu hồi dung lượng ngay lập tức, bảng dữ liệu của chúng tôi phình to nhanh chóng. Các tiến trình autovacuum phát sinh sau đó đã khóa các page của bảng và làm suy giảm hiệu quả bộ nhớ đệm trong giờ cao điểm.

Đánh giá các giải pháp

Chúng tôi đã cân nhắc ba hướng kiến trúc khác nhau để xử lý khối lượng công việc time-series của mình:

Giải pháp Ưu điểm Nhược điểm Kết luận
Native Partitioning của PostgreSQL Tích hợp sẵn trực tiếp trong PostgreSQL; không yêu cầu extension của bên thứ ba. Cần viết script tùy chỉnh để tạo chunk, thiếu tính năng rollup tự động và không có nén dạng cột (columnar compression) tích hợp. Chi phí vận hành và bảo trì quá lớn về lâu dài.
TSDB chuyên dụng (ví dụ: InfluxDB) Được thiết kế chuyên biệt cho metrics; khả năng nén vượt trội ngay từ đầu. Phải vận hành thêm một cơ sở dữ liệu mới. Đồng thời phá vỡ các liên kết relational join với metadata của thiết bị và tài khoản khách hàng. Chi phí migration cao và phức tạp khi phải ghi đồng thời vào hai database (dual-write).
Extension TimescaleDB Extension native với tính năng tự động phân vùng (Hypertables), continuous aggregates và nén dạng cột. Cần cài đặt và tinh chỉnh một extension PostgreSQL bên ngoài. Được chọn: Hoàn toàn tương thích SQL mà không cần viết lại schema.

Các bước triển khai từng bước với TimescaleDB

TimescaleDB chuyển đổi các bảng tiêu chuẩn thành Hypertables. Về bản chất, nó phân tách dữ liệu nạp vào thành các phân vùng theo thời gian gọi là chunk, trong khi vẫn hiển thị một bảng hợp nhất duy nhất cho tầng ứng dụng của bạn.

Bước 1: Cài đặt Extension và tinh chỉnh bộ nhớ

Trên máy chủ database Ubuntu 22.04 chạy PostgreSQL 15, chúng tôi thêm repository chính thức của Timescale và cài đặt package:

# Thêm repository TimescaleDB và cài đặt package
curl -s https://packagecloud.io/install/repositories/timescale/timescaledb/script.deb.sh | sudo bash
sudo apt-get install timescaledb-2-postgresql-15

# Chạy công cụ tự động tinh chỉnh bộ nhớ và worker
sudo timescaledb-tune --quiet --yes
sudo systemctl restart postgresql

Tiếp theo, chúng tôi kích hoạt extension bên trong cơ sở dữ liệu:

CREATE EXTENSION IF NOT EXISTS timescaledb;

Bước 2: Thiết kế Schema và tạo Hypertable

Chúng tôi định nghĩa bảng telemetry bằng SQL tiêu chuẩn, đảm bảo cột timestamp có ràng buộc NOT NULL rõ ràng:

CREATE TABLE sensor_readings (
    recorded_at TIMESTAMPTZ NOT NULL,
    device_id   INTEGER NOT NULL,
    temperature DOUBLE PRECISION,
    humidity    DOUBLE PRECISION,
    voltage     DOUBLE PRECISION
);

-- Chuyển đổi bảng thành Hypertable với chu kỳ chunk 1 ngày
SELECT create_hypertable(
    'sensor_readings',
    'recorded_at',
    chunk_time_interval => INTERVAL '1 day'
);

Chọn đúng chu kỳ chunk là yếu tố sống còn. Hãy đảm bảo các chunk đang hoạt động cùng với index của chúng chiếm không quá 25% dung lượng shared_buffers khả dụng. Với khối lượng ghi 86,4 triệu dòng mỗi ngày của chúng tôi, chu kỳ 1 ngày tạo ra các chunk dung lượng ~1,8 GB, nằm vừa vặn trong RAM.

Bước 3: Tăng tốc Dashboard với Continuous Aggregates

Continuous Aggregates tự động tính toán trước các số liệu thống kê tổng hợp theo cơ chế lũy tiến dưới nền. Khi có truy vấn gửi đến, TimescaleDB sẽ kết hợp các nhóm dữ liệu lịch sử đã tổng hợp sẵn với duy nhất các điểm dữ liệu thô mới nhất.

-- Tạo view tổng hợp theo giờ
CREATE MATERIALIZED VIEW device_hourly_stats
WITH (timescaledb.continuous) AS
SELECT
    time_bucket('1 hour', recorded_at) AS bucket,
    device_id,
    AVG(temperature) AS avg_temp,
    MAX(temperature) AS max_temp,
    MIN(temperature) AS min_temp,
    AVG(humidity) AS avg_humidity,
    COUNT(*) AS reading_count
FROM sensor_readings
GROUP BY bucket, device_id
WITH NO DATA;

-- Tự động làm mới dữ liệu tổng hợp mỗi 30 phút
SELECT add_continuous_aggregate_policy('device_hourly_stats',
    start_offset => INTERVAL '3 days',
    end_offset   => INTERVAL '1 hour',
    schedule_interval => INTERVAL '30 minutes'
);

Bước 4: Bật nén dạng cột và chính sách lưu trữ tự động

Dữ liệu đo từ xa hầu như không bao giờ thay đổi sau khi đã ghi. Tính năng nén dạng cột (columnar compression) của TimescaleDB sắp xếp lại các dòng lịch sử thành các mảng cột, mang lại hiệu quả tiết kiệm dung lượng lưu trữ đáng kể.

-- Cấu hình nén theo cột theo thiết bị
ALTER TABLE sensor_readings SET (
    timescaledb.compress,
    timescaledb.compress_segmentby = 'device_id',
    timescaledb.compress_orderby = 'recorded_at DESC'
);

-- Tự động nén các chunk cũ hơn 7 ngày
SELECT add_compression_policy('sensor_readings', INTERVAL '7 days');

-- Xóa các partition chunk thô cũ hơn 180 ngày
SELECT add_retention_policy('sensor_readings', INTERVAL '180 days');

Sáu tháng trên Production: Những con số biết nói

Trải qua sáu tháng xử lý 1,2 tỷ bản ghi telemetry thô, các chỉ số của chúng tôi đã ổn định toàn diện:

  • Độ trễ Insert: Độ trễ ghi P95 giảm từ 450ms xuống chỉ còn 6ms, loại bỏ hoàn toàn tình trạng timeout ở phía client.
  • Tốc độ Dashboard: Các truy vấn 30 ngày trên toàn bộ thiết bị trong Grafana giảm ngoạn mục từ 25 giây xuống 42 mili-giây nhờ đọc từ device_hourly_stats.
  • Dung lượng ổ đĩa: Nén dạng cột giúp giảm dung lượng lưu trữ telemetry thô từ 480 GB xuống còn 62 GB—tiết kiệm đến 87% không gian đĩa.
  • Không còn gánh nặng Vacuum: Các chính sách data retention xóa trực tiếp các file chunk bên dưới thông qua thao tác trên filesystem. Cơ chế này bỏ qua việc xóa từng dòng và triệt tiêu hoàn toàn hiện tượng bloat do autovacuum.

Nếu hệ thống PostgreSQL của bạn đang phải vật lộn với tần suất ghi cao hoặc các bảng metric ngày càng phình to, TimescaleDB là một giải pháp cân bằng tuyệt vời. Bạn vừa có được hiệu năng vượt trội cho dữ liệu time-series vừa giữ nguyên vẹn mô hình dữ liệu quan hệ, hệ thống backup hiện có cũng như toàn bộ hệ sinh thái ORM.

Share: