0

Google BigQuery - Bài học 31: Search Indexes – Tối ưu hóa việc tìm kiếm một dòng cụ thể (Point Lookup) hoặc phân tích Log theo phong cách tương tự

Chào anh em! Ở các bài học trước trong Phần 4, chúng ta đã giải quyết các bài toán tối ưu quy mô lớn bằng Table Partitioning (chia theo thời gian), Table Clustering (sắp xếp đa chiều), BI Engine (tăng tốc In-memory) và Materialized Views (tính toán trước báo cáo tổng hợp).

Tuy nhiên, có một bài toán mang tính chất ngược lại hoàn toàn với báo cáo tổng hợp mà các lập trình viên Backend hoặc kỹ sư vận hành hệ thống (DevOps/SRE) thường xuyên phải đối mặt: Bài toán "Mò kim đáy bể" (Point Lookup & Log Analysis).

Hãy tưởng tượng kịch bản thực tế sau:

  • Hệ thống của bạn lưu trữ một bảng log ứng dụng khổng lồ (app_logs) chứa tới 5 Tỷ dòng (nặng khoảng 1 Terabyte), được phân vùng theo ngày.

  • Hôm nay có một khách hàng VIP phàn nàn rằng hệ thống báo lỗi vào lúc 14:30 chiều qua, và họ cung cấp cho bạn một mã giao dịch duy nhất: TXN-99887766.

  • Bạn phải tìm kiếm dòng log chứa chính xác mã giao dịch đó nằm ẩn giấu đâu đó bên trong 1 Terabyte dữ liệu.

Nếu không có công cụ hỗ trợ, dù bạn đã phân vùng theo ngày, Dremel vẫn buộc phải quét toàn bộ dung lượng của ngày hôm đó (vài trăm Gigabyte) chỉ để tìm ra... đúng 1 dòng duy nhất. Việc này vừa chậm, vừa lãng phí tài nguyên tính toán (Slot time) và đốt tiền vô ích!

Làm thế nào để BigQuery có thể tìm kiếm một dòng cụ thể hoặc quét log văn bản với tốc độ chớp nhoáng hệt như cơ chế Index trong MySQL? Câu trả lời chính là Search Indexes (Chỉ mục tìm kiếm). Hãy cùng mổ xẻ ngay nhé!

1. Điểm yếu của Full Table Scan khi tìm kiếm văn bản (Text Search)

Trong các hệ thống RDBMS truyền thống (như MySQL hay PostgreSQL), khi bạn tạo một bảng, bạn có thể dễ dàng chạy lệnh CREATE INDEX idx_txn ON orders(txn_id); để tạo một cây B-Tree Index. Khi có câu lệnh WHERE txn_id = '...', Database nhảy thẳng đến dòng đó trong vòng chưa tới 1 mili-giây.

Nhưng trong thế giới Data Warehouse phân tán như BigQuery:

  • Tại sao không có B-Tree Index truyền thống? Vì kiến trúc lưu trữ dạng cột (Capacitor) và phân tán trên hàng ngàn máy chủ của Colossus không được thiết kế cho việc cập nhật hoặc tìm kiếm từng dòng ngẫu nhiên (Random I/O) kiểu B-Tree.

  • Hệ quả: Khi bạn thực hiện các phép tìm kiếm chuỗi văn bản tự do (như WHERE text LIKE '%keyword%' hoặc REGEXP_CONTAINS), Dremel buộc phải thực hiện Full Table Scan (quét toàn bộ các khối Capacitor) vì không có cấu trúc chỉ mục nào hỗ trợ.

Search Indexes ra đời để lấp đầy khoảng trống này, mang sức mạnh tìm kiếm văn bản tốc độ cao (Inverted Index) vào thẳng Google BigQuery!

2. Search Indexes hoạt động như thế nào dưới gầm máy?

Khác với cấu trúc phân cấp của RDBMS, BigQuery sử dụng công nghệ Inverted Index (Chỉ mục đảo ngược) – cấu trúc kinh điển được dùng trong các công cụ tìm kiếm lớn như Elasticsearch hoặc Apache Lucene.

Cơ chế hoạt động:

  1. Khi bạn bật Search Index cho một cột (ví dụ cột payload hoặc toàn bộ cột kiểu JSON), background process của BigQuery sẽ tự động phân tích toàn bộ văn bản, bóc tách thành các từ khóa riêng lẻ (Tokens).

  2. Nó xây dựng một bảng ánh xạ ngược: Từ khóa nào xuất hiện ở những khối dữ liệu (Data Blocks) nào?

  3. Khi bạn chạy một câu lệnh tìm kiếm sử dụng hàm SEARCH():

    SQL

    SELECT * 
    FROM `my-project.logs.app_logs`
    WHERE SEARCH(payload, 'TXN-99887766');
    
    
  4. Dremel không cần quét toàn bộ bảng. Nó tra cứu ngay lập tức vào Search Index để biết chính xác TXN-99887766 nằm ở Khối số 42, và chỉ thò tay đọc đúng Khối số 42 đó trên Colossus. Thời gian phản hồi giảm từ vài phút xuống còn vài mili-giây!

3. Thực chiến: Tạo và Sử dụng Search Index trên BigQuery

Việc cấu hình Search Index trong GoogleSQL cực kỳ đơn giản với câu lệnh CREATE SEARCH INDEX.

Bước 1: Tạo Search Index trên một bảng log lớn

Bạn có thể tạo index cho toàn bộ các cột của bảng hoặc chỉ định một cột cụ thể (như cột payload hoặc cột JSON):

SQL

-- Tạo Search Index cho toàn bộ cột payload trên bảng app_logs
CREATE SEARCH INDEX idx_app_logs_payload 
ON `my-project.logs.app_logs`(payload);

(Lưu ý: Quá trình xây dựng index ban đầu diễn ra hoàn toàn ngầm trên hạ tầng của Google, không làm gián đoạn các câu lệnh truy vấn đang chạy của bạn).

Bước 2: Sử dụng hàm SEARCH() để tận dụng Index

Để trình tối ưu hóa truy vấn kích hoạt Search Index mà bạn vừa tạo, bạn bắt buộc phải sử dụng hàm SEARCH() thay vì dùng các hàm thông thường như LIKE hoặc REGEXP_CONTAINS:

SQL

-- Tìm kiếm siêu tốc một mã giao dịch hoặc từ khóa lỗi bất kỳ bên trong log
SELECT 
    created_at,
    severity,
    payload
FROM `my-project.logs.app_logs`
WHERE SEARCH(payload, 'TXN-99887766');

4. Những kiểu dữ liệu nào hỗ trợ Search Index?

Search Index trong BigQuery cực kỳ linh hoạt, hỗ trợ hầu hết các kiểu dữ liệu phức tạp mà chúng ta đã học ở Phần 2:

  1. Kiểu STRING: Tìm kiếm từ khóa, cụm từ chính xác.

  2. Kiểu JSON: Cho phép bạn tìm kiếm từ khóa ở bất kỳ trường nào bên trong một cục JSON payload hỗn loạn mà không cần biết trước cấu trúc schema!

  3. Kiểu ARRAY và STRUCT: Tự động index toàn bộ các phần tử nằm bên trong mảng lồng nhau.

5. Đánh đổi (Trade-offs): Khi nào nên và không nên dùng Search Index?

Dù cực kỳ mạnh mẽ, với tư cách là một kỹ sư hệ thống, bạn phải luôn ghi nhớ nguyên lý: "Không có giải pháp nào là miễn phí hoàn toàn trong kỹ thuật phần mềm".

  • Lợi ích: Tăng tốc độ truy vấn Point Lookup lên hàng trăm lần, biến BigQuery thành một công cụ phân tích log (Log Analytics) mạnh mẽ không thua kém gì các hệ thống chuyên dụng.

  • Chi phí đánh đổi:

    1. Chi phí lưu trữ (Storage Cost): Vì hệ thống phải lưu thêm các cấu trúc In-memory/Inverted Index phụ trợ trên Colossus, dung lượng lưu trữ của bảng sẽ tăng thêm một chút (thường từ 10% đến 30% tùy thuộc vào độ phức tạp của văn bản).

    2. Chi phí cập nhật (Write Amplification): Khi bạn liên tục INSERT hoặc UPDATE dữ liệu mới vào bảng, hệ thống mất thêm một chút tài nguyên nền để cập nhật lại Index. Do đó, chỉ nên tạo Search Index cho các bảng có tính chất đọc là chính (Read-heavy) như bảng Log, bảng Lịch sử sự kiện.

Tổng kết Bài học 31

Search Indexes là mảnh ghép hoàn hảo giúp BigQuery chinh phục các bài toán tìm kiếm chi tiết:

  1. Inverted Indexing: Mang sức mạnh tìm kiếm từ khóa cấp độ Elasticsearch vào thẳng Data Warehouse.

  2. Hàm SEARCH(): Cú pháp chuẩn mực để kích hoạt cơ chế nhảy cóc qua các khối dữ liệu (Block Pruning).

  3. Hỗ trợ JSON & ARRAY: Tối ưu hóa tuyệt vời cho việc phân tích log và payload động từ các API Microservices đổ về.

Tuy nhiên, trong suốt chặng đường từ Bài 1 đến Bài 31, chúng ta đã thấy không ít lần những câu lệnh viết sai quy cách có thể làm "nổ tung" hóa đơn tiền Cloud của công ty.

Những thói quen nào của lập trình viên khiến doanh nghiệp mất oan hàng ngàn đô la trên BigQuery? Hãy đón xem Bài học 32: 10 thói quen xấu gây "cháy túi" trên BigQuery – Tại sao SELECT * và LIMIT 10 không hề giúp bạn tiết kiệm tiền như trong MySQL?!


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí