0

Elasticsearch Bài học 9: Write Flow - Luồng ghi dữ liệu (Routing, Translog, Refresh và Flush) diễn ra như thế nào?

Ở Bài 8, chúng ta đã kết thúc bằng một nghịch lý: Inverted Index là bất biến (Immutable). Nó không thể bị sửa chữa một khi đã được tạo ra. Vậy làm thế nào hệ thống của bạn có thể liên tục POST hàng nghìn log mỗi giây hoặc cập nhật trạng thái đơn hàng liên tục mà không bị nghẽn?

Bí mật của Elasticsearch là: Nó không bao giờ cố gắng "sửa" Inverted Index cũ. Thay vào đó, nó gom dữ liệu mới để liên tục tạo ra các Inverted Index "mini" (gọi là Segment).

Dưới đây là hành trình thực sự của một Document (ví dụ: một bản ghi log soát vé AFC) từ lúc API backend gửi lên cho đến khi nó nằm an toàn trên ổ SSD. Quá trình này chia làm 4 giai đoạn cốt lõi.

Giai đoạn 1: Định tuyến (Routing) - Dữ liệu đi về đâu?

Khi API backend gọi lệnh POST /afc_logs/_doc/123, request này sẽ bay đến một Node bất kỳ trong Cluster (lúc này đóng vai trò là Coordinating Node).

Coordinating Node không tùy tiện ném dữ liệu đi lung tung. Nó dùng một công thức Hash toán học để quyết định Document này phải thuộc về Primary Shard nào:

shard_num = hash(routing_key) % num_primary_shards

(Trong đó routing_key mặc định là Document ID). Sau khi tính toán xong (giả sử ra kết quả là Shard 2), Coordinating Node sẽ chuyển tiếp (forward) request đến exat Data Node đang giữ Primary Shard số 2.

Giai đoạn 2: Memory Buffer và Translog - Tốc độ đi kèm An toàn

Tại Data Node chứa Primary Shard 2, việc ghi thẳng dữ liệu xuống đĩa cứng (Disk I/O) để tạo Inverted Index là quá chậm. Để đạt hiệu năng cao, Elasticsearch ghi dữ liệu vào RAM trước.

Cụ thể, có 2 hành động diễn ra đồng thời:

  1. Ghi vào In-memory Buffer: Document được nạp vào một bộ đệm trên RAM. Tốc độ ghi vào đây gần như là tức thời. Tuy nhiên, lúc này Document vẫn chưa thể tìm kiếm được.

  2. Ghi vào Translog (Transaction Log): RAM rất dễ bay hơi. Nếu lúc này sập nguồn, dữ liệu trong Buffer sẽ mất sạch. Để phòng hờ, Elasticsearch đồng thời ghi nguyên xi request đó (dạng raw) vào một file log nối đuôi (append-only) trên đĩa cứng gọi là Translog. Việc ghi nối đuôi (sequential write) cực kỳ nhanh, không dính độ trễ của việc random I/O.

Lúc này, Elasticsearch trả về HTTP status 201 Created cho API của bạn. Hệ thống backend có thể yên tâm là dữ liệu đã được nhận.

Giai đoạn 3: Refresh - Trở thành "Near Real-Time"

Dữ liệu nằm trong In-memory Buffer thì không thể search được. Để user có thể tìm kiếm, Elasticsearch thực hiện một tiến trình gọi là Refresh.

  • Mặc định cứ 1 giây 1 lần, Elasticsearch lấy toàn bộ dữ liệu đang có trong In-memory Buffer, đóng gói lại và chuyển thành một Segment (một Inverted Index thu nhỏ).

  • Segment này được đẩy ra khỏi Buffer, ghi vào Filesystem Cache (bộ nhớ đệm của hệ điều hành, vẫn nằm trên RAM).

  • Ngay khi Segment nằm trong Filesystem Cache, nó được mở (open) để phục vụ tìm kiếm.

  • In-memory Buffer được làm sạch, sẵn sàng đón lứa Document mới.

Hệ quả cốt lõi: Quá trình Refresh tốn khoảng 1 giây này chính là lý do Elasticsearch được gọi là Near Real-Time (Thời gian thực gần) chứ không phải Real-Time như MySQL. Bạn ghi dữ liệu vào lúc 00:00:00, phải đến 00:00:01 câu query search mới nhìn thấy dữ liệu đó.

Giai đoạn 4: Flush - Hạ cánh an toàn xuống ổ cứng

Đến thời điểm hiện tại, dữ liệu tìm kiếm được (Segment) vẫn đang lơ lửng ở bộ nhớ tạm (Filesystem Cache). Nếu server sập lúc này, Elasticsearch khởi động lại sẽ đọc file Translog trên ổ đĩa để khôi phục lại các Segment chưa kịp lưu.

Nhưng Translog không thể phình to mãi. Đó là lúc tiến trình Flush xuất hiện.

  • Khi file Translog quá lớn (mặc định 512MB) hoặc cứ sau 30 phút, Elasticsearch sẽ kích hoạt Flush.

  • Lệnh fsync của hệ điều hành được gọi, ép toàn bộ các Segment đang lơ lửng trong Filesystem Cache phải ghi chết (commit) xuống ổ cứng vật lý (SSD/HDD).

  • Một file đánh dấu Commit Point được tạo ra để hệ thống ghi nhớ các Segment đã an toàn.

  • File Translog cũ bị xóa sạch (vì dữ liệu đã được bảo đảm), bắt đầu một file Translog mới tinh.

Giải mã bí ẩn: Update và Delete hoạt động thế nào?

Nếu Segment là bất biến (không thể sửa hay xóa), vậy lệnh DELETE hay UPDATE chạy kiểu gì?

  • Xóa (Delete): Elasticsearch không xóa dữ liệu trong Segment. Nó chỉ tạo ra một file tên là .del và ghi ID của Document đó vào. Khi có người tìm kiếm, Elasticsearch vẫn mò ra Document đó, nhưng trước khi trả về kết quả, nó đối chiếu với file .del và lọc Document đó ra (ẩn đi).

  • Sửa (Update): Bản chất của Update là sự kết hợp của Xóa và Thêm mới. Elasticsearch ghi ID cũ vào file .del, đồng thời đưa phiên bản Document mới vào In-memory Buffer để tạo thành một Segment mới toanh.

Segment Merging (Dọn rác): Theo thời gian, việc Refresh mỗi giây sẽ sinh ra hàng nghìn Segment nhỏ lẻ và rác từ các Document bị xóa. Ở background, Elasticsearch có một tiến trình dọn dẹp (Merge process). Nó sẽ gom các Segment nhỏ lại thành một Segment siêu to khổng lồ, đồng thời vứt bỏ hoàn toàn những Document có tên trong file .del để giải phóng dung lượng ổ đĩa.

Bí kíp tối ưu cho Backend Engineer

Hiểu luồng ghi này, bạn có thể giải quyết các bài toán thắt cổ chai cực kỳ dễ dàng: Nếu bạn viết một script để import hàng triệu sản phẩm từ MySQL sang Elasticsearch, việc hệ thống tự động Refresh mỗi giây một lần (tạo ra hàng triệu mini Segment) sẽ vắt kiệt CPU. Cách giải quyết: Trước khi chạy script import lượng lớn, hãy gọi API tắt Refresh ("refresh_interval": "-1"). Tốc độ import sẽ tăng lên gấp nhiều lần. Sau khi import xong, bật lại "refresh_interval": "1s".


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.