Từ 40 giây xuống 500ms: Xử lý hàng tỷ bản ghi Real-time với Apache Druid

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

Khi MySQL hay PostgreSQL “gục ngã” trước hàng tỷ bản ghi

Hai năm trước, mình từng “ăn hành” với một dự án Tracking cho sàn TMĐT lớn. Đề bài rất gắt: Hệ thống phải xử lý 500 triệu event mỗi ngày. Bộ phận Business yêu cầu xem báo cáo trên Dashboard gần như tức thì với độ trễ dưới 1 giây.

Lúc đầu, team vẫn tin dùng PostgreSQL. Khi dữ liệu dưới 100 triệu dòng, mọi thứ vẫn ổn. Nhưng khi bảng log chạm mốc 1 tỷ bản ghi, các câu lệnh COUNT(DISTINCT user_id) bắt đầu trở thành ác mộng. Dù đã đánh index và chia Partition, mỗi lần load Dashboard, anh em vẫn phải đi pha cafe đợi 30-40 giây mới thấy số nhảy. Khách hàng phàn nàn, sếp thì đứng ngồi không yên.

Tại sao các database truyền thống lại chậm?

Sau nhiều đêm thức trắng debug, mình nhận ra các database OLTP (như MySQL, Postgres) gặp ba rào cản lớn khi xử lý Big Data:

  • Lưu trữ theo dòng (Row-based): Để tính tổng doanh thu của 1 cột, nó phải đọc toàn bộ các cột khác từ ổ cứng. Việc này gây lãng phí I/O khủng khiếp.
  • Index quá tải: Với hàng tỷ dòng, index phình to vượt quá dung lượng RAM. Tốc độ ghi (ingestion) bị kéo tụt do phải cập nhật index liên tục.
  • Xử lý đơn luồng: Các query phức tạp thường không tận dụng được sức mạnh của toàn bộ nhân CPU trong cụm server.

Những phương án team mình từng cân nhắc

Trước khi “chốt” Druid, team mình đã thử qua vài hướng nhưng đều có kẽ hở:

  1. Pre-aggregation: Dùng Cronjob tổng hợp dữ liệu mỗi giờ. Cách này nhanh nhưng mất tính real-time. Sếp muốn xem số liệu của 5 phút trước thì chịu chết.
  2. Elasticsearch: Tìm kiếm text rất nhanh. Tuy nhiên, khi tính toán (aggregation) trên tập dữ liệu lớn, nó ngốn RAM kinh hoàng và đôi khi trả về kết quả không chính xác 100%.
  3. ClickHouse: Hiệu năng cực tốt. Nhưng tại thời điểm đó, khả năng tích hợp trực tiếp với Kafka của ClickHouse chưa mượt và ổn định bằng Druid.

Apache Druid – “Vũ khí” cho Real-time Analytics

Apache Druid không chỉ là database. Nó là sự kết hợp giữa Data Warehouse, Time-series database và Search Index. Druid lưu trữ dạng cột (Columnar) và tự động đánh index ngay khi nạp dữ liệu. Khả năng “nuốt” hàng triệu event mỗi giây từ Kafka là điểm ăn tiền nhất của nó.

Cơ chế thông minh của Druid nằm ở việc chia dữ liệu thành các Segments theo thời gian. Khi bạn query dữ liệu hôm nay, nó chỉ quét đúng các segment liên quan. Nó bỏ qua hoàn toàn dữ liệu cũ, giúp tốc độ đạt mức sub-second dễ dàng.

Hướng dẫn cài đặt nhanh (Micro-Quickstart)

Nếu muốn chạy thử trên máy cá nhân hoặc VPS tầm 4GB RAM, bản Micro-Quickstart là lựa chọn hợp lý nhất.

Bước 1: Chuẩn bị môi trường

Druid chạy tốt nhất trên Java 11. Bạn kiểm tra môi trường bằng lệnh:

java -version

Bước 2: Tải và giải nén

Tải bản phân phối mới nhất (ví dụ bản 28.0.1) từ mirror của Apache:

wget https://dlcdn.apache.org/druid/28.0.1/apache-druid-28.0.1-bin.tar.gz
tar -xzf apache-druid-28.0.1-bin.tar.gz
cd apache-druid-28.0.1

Bước 3: Khởi chạy

Trong thư mục gốc, hãy chạy script có sẵn để kích hoạt các service:

./bin/start-micro-quickstart

Đợi tầm 1 phút để các thành phần như Coordinator và Broker khởi động xong. Sau đó, bạn truy cập UI tại: http://localhost:8888.

Ingest dữ liệu và truy vấn thực tế

Druid không dùng INSERT INTO thông thường mà dùng “Ingestion Spec” bằng JSON. Đừng quá lo lắng, giao diện console sẽ hỗ trợ bạn tận răng.

Nạp dữ liệu mẫu

Giả sử bạn có file events.json với cấu trúc đơn giản:

{"time": "2023-10-27T10:00:00Z", "user_id": "u1", "event_type": "click", "value": 10}

Trên giao diện Druid Console:

  1. Vào Load data -> Local disk và nhập đường dẫn file.
  2. Tại bước Parse data, hãy chọn cột time làm Primary Timestamp. Đây là cột sống còn để Druid phân mảnh dữ liệu hiệu quả.
  3. Nhấn Submit. Khi Task báo SUCCESS là dữ liệu đã sẵn sàng.

Truy vấn bằng SQL

Druid hỗ trợ SQL chuẩn, rất dễ làm quen. Bạn có thể thử ngay câu lệnh này:

SELECT 
  event_type, 
  COUNT(*) as total_events
FROM "your_datasource"
WHERE __time >= CURRENT_TIMESTAMP - INTERVAL '1' DAY
GROUP BY 1

Kết quả sẽ trả về trong tích tắc nhờ công nghệ Bitmap index trên từng cột.

Kinh nghiệm “xương máu” khi vận hành

Sau một thời gian vận hành Druid ở môi trường production, mình rút ra 3 lưu ý quan trọng:

  • Rollup là cứu cánh: Nếu không cần soi chi tiết từng user, hãy bật Rollup. Nó tự động gộp các dòng trùng timestamp và dimension. Trong dự án của mình, Rollup giúp giảm dung lượng lưu trữ từ 100GB xuống còn 15GB.
  • Tránh High Cardinality: Đừng đưa các cột có quá nhiều giá trị duy nhất (như session_id) vào làm dimension. Nó sẽ làm hỏng khả năng Rollup và khiến query chậm đi đáng kể.
  • Cấu hình RAM: Druid cực kỳ “vã” RAM cho Direct Memory. Hãy nhớ set tham số -XX:MaxDirectMemorySize cẩn thận, nếu không hệ thống sẽ crash khi gặp query nặng.

Kết quả cuối cùng? Dashboard của team mình giảm từ 40 giây xuống còn chưa tới 500ms. Sếp hài lòng, anh em DevOps cũng thở phào vì không phải thức đêm tối ưu index nữa. Nếu bạn đang đối mặt với bài toán dữ liệu khổng lồ cần tốc độ real-time, Apache Druid là cái tên rất đáng để đầu tư.

Share: