Trino 1 — Tổng quan & truy vấn liên hợp

13 thg 7, 2026 2 lượt xem
#sql
#query-engine
#federated-query
#trino
#presto

Trino 1 — Tổng quan & truy vấn liên hợp

Hình dung một tình huống rất thật ở ngân hàng: bộ phận phân tích rủi ro cần một báo cáo ghép giao dịch nằm trên data lake (Parquet trên S3/HDFS), hồ sơ khách hàng nằm trong PostgreSQL của core banking, và danh mục sản phẩm nằm trong MySQL của hệ thống thẻ. Cách truyền thống là dựng một pipeline ETL kéo cả ba nguồn về một kho chung rồi mới truy vấn — mất công, mất thời gian, và dữ liệu luôn trễ. Trino sinh ra để bỏ qua bước đó: bạn viết một câu SQL duy nhất JOIN thẳng qua ba hệ khác nhau, Trino tự đọc dữ liệu tại chỗ ở mỗi nguồn và ghép lại trong bộ nhớ. Đó là truy vấn liên hợp (federated query) — khả năng lõi làm nên tên tuổi Trino.

Bài này là nền móng của cả series. Mục tiêu không phải dạy tối ưu truy vấn ngay, mà dựng một mô hình tinh thần đúng: Trino là loại công cụ nào, giải bài toán gì, và quan trọng không kém — nó không giải bài toán gì. Có mô hình đó rồi, các bài sau — kiến trúc coordinator/worker, connector & catalog, SQL liên hợp, truy vấn lakehouse, tinh chỉnh hiệu năng, bảo mật & quản trị, triển khai & vận hành — sẽ ăn khớp thay vì rời rạc.

Lưu ý về sandbox: SQL sandbox của Knowledge Base chạy trên PostgreSQL, KHÔNG phải Trino. Trino có phương ngữ SQL riêng và quan trọng hơn — sức mạnh của nó là chạy trên nhiều nguồn liên hợp, không thể tái hiện trong một Postgres đơn lẻ. Vì vậy trong bài này chỉ có một khối SQL được đánh dấu "▶ Chạy được" — một câu ANSI SQL đơn giản, dùng để minh hoạ ý "cùng một câu SQL, Trino chạy được trên nhiều nguồn". Các khối SQL Trino khác chỉ mang tính minh hoạ và ghi rõ "(minh hoạ — Trino)".

Trino là gì

Trino là một query engine SQL phân tán, tương tác nhanh, mã nguồn mở, tối ưu cho việc chạy truy vấn phân tích trên khối dữ liệu lớn nằm rải rác ở nhiều nguồn. Ghép các tính từ đó lại sẽ ra bản chất:

  • Query engine (engine truy vấn): Trino KHÔNG lưu dữ liệu. Nó không phải một database. Nó nhận câu SQL, lập kế hoạch, rồi đọc dữ liệu tại chỗ ở nơi dữ liệu đang nằm (S3, HDFS, PostgreSQL, MySQL, Kafka...) và tính toán. Tách hoàn toàn phần tính toán (compute) khỏi phần lưu trữ (storage).
  • Phân tán (distributed / MPP): một truy vấn được xé nhỏ và chạy song song trên nhiều máy (worker). MPP — massively parallel processing, xử lý song song quy mô lớn — cho phép quét terabyte dữ liệu bằng cách chia việc cho hàng chục tới hàng trăm nhân.
  • Tương tác nhanh (interactive): Trino thiết kế cho truy vấn ad-hoc, độ trễ thấp — analyst gõ câu hỏi và mong có kết quả trong vài giây tới vài chục giây, chứ không phải một job batch chạy hàng giờ.
  • Mã nguồn mở: phát hành theo giấy phép Apache 2.0, self-hosted hoặc chạy trên cloud, không khoá vào một nhà cung cấp.

Một chút lịch sử: Presto → Trino

Trino có nguồn gốc từ PrestoDB, do Facebook phát triển năm 2012 để thay thế Hive (vốn chạy trên MapReduce, quá chậm cho truy vấn tương tác) cho kho dữ liệu khổng lồ của họ. Năm 2019, các thành viên sáng lập gốc tách ra và đổi tên dự án thành Trino (ban đầu gọi là "PrestoSQL"). Ngày nay tồn tại song song hai nhánh: Trino (nhánh do đội sáng lập gốc dẫn dắt, phát triển tích cực nhất, cộng đồng lớn) và PrestoDB (do Facebook/Linux Foundation duy trì). Trong series này ta nói về Trino; về mặt khái niệm lõi thì hai nhánh rất giống nhau, nên nhiều kiến thức áp dụng chung cho cả "Presto".

Điểm cần khắc cốt ngay từ đầu: Trino không phải nơi bạn cất dữ liệu. Nó là "bộ não SQL" đặt phía trên các kho dữ liệu có sẵn. Muốn hiểu vì sao điều này lại mạnh, phải bắt đầu từ triết lý tách compute khỏi storage.

Triết lý: engine không lưu dữ liệu

Hầu hết database "kinh điển" (PostgreSQL, MySQL, Oracle) gộp chung ba thứ trong một hệ thống: nơi lưu dữ liệu, cơ chế truy cập dữ liệu, và engine tính toán SQL. Trino tách khối thứ ba ra thành một tầng độc lập:

  • Storage nằm bên ngoài Trino. Dữ liệu vẫn ở nguyên chỗ của nó — file Parquet/ORC trên object storage (S3, GCS, Azure Blob, HDFS), bảng trong Postgres/MySQL, topic trong Kafka, chỉ mục trong Elasticsearch. Trino không sao chép, không nạp trước.
  • Trino chỉ mang compute. Khi có truy vấn, Trino đọc dữ liệu tại chỗ qua một connector (trình kết nối) chuyên biệt cho từng loại nguồn, kéo về đúng phần cần, tính trong bộ nhớ, trả kết quả.
  • Không có bước "load into Trino". Đây là khác biệt then chốt với data warehouse: với Snowflake/BigQuery bạn phải nạp (ingest) dữ liệu vào warehouse trước; với Trino, bạn chỉ cần khai báo connector trỏ tới nguồn là truy vấn được ngay.

Hệ quả của triết lý này rất sâu sắc:

  1. Không ETL gộp trước cho nhiều mục đích đọc. Dữ liệu không cần bị nhân bản vào một kho trung tâm chỉ để truy vấn được. Giảm độ trễ dữ liệu, giảm chi phí lưu trữ trùng lặp.
  2. Scale compute độc lập với storage. Cần nhanh hơn thì thêm worker; cần lưu nhiều hơn thì mở rộng S3 — hai việc không ràng buộc nhau.
  3. Một điểm truy vấn cho nhiều nguồn. Vì compute là một tầng riêng, nó có thể trỏ tới nhiều storage cùng lúc — dẫn thẳng tới truy vấn liên hợp.

Truy vấn liên hợp (federated query): khả năng lõi

Federated query — truy vấn liên hợp — nghĩa là một câu SQL duy nhất đọc và JOIN dữ liệu từ nhiều hệ lưu trữ khác nhau, như thể tất cả nằm trong cùng một database. Đây chính là thứ khiến Trino nổi bật so với mọi công cụ chỉ đọc một nguồn.

Trong Trino, mỗi nguồn được khai báo thành một catalog (dựa trên một connector). Bạn tham chiếu bảng theo cú pháp ba tầng catalog.schema.table. Ví dụ:

  • hive.lake.transactions — bảng transactions trong data lake, đọc qua connector Hive.
  • postgres.core.customers — bảng customers trong PostgreSQL của core banking.
  • mysql.cards.products — bảng products trong MySQL của hệ thống thẻ.

Và một câu SQL có thể JOIN thẳng cả ba (phương ngữ Trino, không chạy trong sandbox Postgres):

-- (minh hoạ — Trino: JOIN 3 nguồn khác nhau trong 1 câu)
SELECT
    c.full_name,
    p.product_name,
    SUM(t.amount) AS tong_chi_tieu
FROM hive.lake.transactions      t          -- data lake (Parquet trên S3)
JOIN postgres.core.customers     c ON c.id = t.customer_id   -- PostgreSQL
JOIN mysql.cards.products        p ON p.id = t.product_id    -- MySQL
WHERE t.created_at >= DATE '2026-01-01'
GROUP BY c.full_name, p.product_name
ORDER BY tong_chi_tieu DESC
LIMIT 100;

Không có bước ETL nào gộp ba nguồn lại trước. Trino tự chia việc: đẩy phần lọc/quét xuống từng nguồn (kỹ thuật predicate pushdown — đẩy điều kiện lọc về gần dữ liệu để đọc ít nhất), kéo về đúng phần cần, rồi ghép trong bộ nhớ. Các nguồn có thể được thêm vào (data lake, Elasticsearch cho log, Kafka cho stream...) mà truy vấn vẫn dùng cùng một phương ngữ SQL.

Cùng một SQL, chạy trên nhiều nguồn

Một điểm tinh tế nhưng cực mạnh: vì Trino nói SQL gần chuẩn ANSI, cùng một câu truy vấn phân tích có thể chạy trên bất kỳ catalog nào — chỉ cần đổi tiền tố catalog. Ví dụ câu tổng hợp chi tiêu khách hàng dưới đây là ANSI SQL thuần; nó chạy được trên PostgreSQL sandbox của chúng ta (nên đánh dấu "▶ Chạy được"), và cũng chính là dạng câu Trino sẽ chạy khi cùng dữ liệu đó nằm trên data lake. Đó là ý "SQL-on-anything": học một phương ngữ, áp lên mọi nguồn.

-- ▶ Chạy được
SELECT
    c.full_name,
    c.city,
    a.currency,
    COUNT(t.id)      AS so_giao_dich,
    SUM(t.amount)    AS tong_tien
FROM customers    c
JOIN accounts     a ON a.customer_id = c.id
JOIN transactions t ON t.account_id  = a.id
GROUP BY c.full_name, c.city, a.currency
ORDER BY tong_tien DESC
LIMIT 20;

Câu trên chỉ dùng 3 trong 5 bảng sandbox và đúng khoá join (transactions.account_id → accounts.id, accounts.customer_id → customers.id), nên nó chạy thật. Với Trino, bạn viết y hệt cấu trúc này nhưng đặt trước mỗi bảng một catalog.schema để chỉ ra nguồn — logic SQL không đổi.

Kiến trúc ở mức cao: coordinator, worker, connector

Để hình dung Trino thực thi truy vấn liên hợp thế nào, đây là bức tranh tối giản (bài kiến trúc sẽ đào sâu):

  • Coordinator: bộ não. Nhận SQL, phân tích cú pháp, lập kế hoạch thực thi phân tán, chia truy vấn thành các stagetask, giao cho worker, rồi gom kết quả trả về client.
  • Worker: các máy làm việc thật. Mỗi worker dùng connector phù hợp để đọc phần dữ liệu được giao từ nguồn tương ứng, xử lý (lọc, join, aggregate) trong bộ nhớ.
  • Connector: "phích cắm" cho từng loại nguồn. Mỗi connector biết cách liệt kê schema/bảng, đọc dữ liệu, và đẩy được phép lọc/aggregate nào xuống nguồn. Thêm một nguồn mới = thêm một connector + catalog, không đụng gì tới engine.

MPP & thực thi pipelined trong bộ nhớ

Sức mạnh tốc độ của Trino đến từ hai đặc điểm thực thi:

  • MPP (massively parallel processing): một truy vấn được xé thành nhiều task chạy song song trên nhiều worker và nhiều luồng. Quét 2 tỷ dòng Parquet không phải là một vòng lặp tuần tự mà là hàng trăm task đọc song song các phần khác nhau của dữ liệu.
  • Thực thi pipelined trong bộ nhớ (in-memory pipelined execution): dữ liệu chảy từ stage này sang stage kế tiếp qua bộ nhớ/mạng, không ghi ra đĩa giữa các stage. Đây là khác biệt lớn so với các engine batch kiểu Hive/MapReduce cũ vốn ghi kết quả trung gian xuống đĩa sau mỗi bước. Không chạm đĩa giữa stage → độ trễ thấp, rất hợp cho truy vấn tương tác.

Đánh đổi cần nhớ: vì giữ dữ liệu trung gian trong bộ nhớ, một job siêu lớn (join nhiều bảng khổng lồ, aggregate tạo trạng thái trung gian vượt RAM cụm) có thể hết bộ nhớ (OOM — out of memory) và fail. Trino mạnh cho interactive/ad-hoc; với các job ETL nặng, dài, cần khả năng chịu lỗi từng bước thì Spark thường phù hợp hơn. (Trino có chế độ fault-tolerant execution để giảm rủi ro này, nhưng triết lý cốt lõi vẫn là in-memory tốc độ cao.)

Định vị: Trino đứng ở đâu

Đừng nghĩ "cái nào tốt hơn" mà nghĩ "mỗi cái giải bài toán gì". Trino nằm ở khe giữa hai nhóm hay bị nhầm với nó: data warehouse và Spark.

So với data warehouse (Snowflake / BigQuery)

Data warehouse vừa lưu vừa tính: bạn nạp dữ liệu vào warehouse, nó lưu ở định dạng cột tối ưu của riêng nó và chạy truy vấn trên đó. Xem tổng quan BigQuery. Điểm mạnh: hiệu năng ổn định, ít vận hành (serverless), tối ưu sâu vì kiểm soát cả storage. Điểm yếu: dữ liệu phải được nạp vào (ETL/ELT), có yếu tố khoá nhà cung cấp, và không tự nhiên truy vấn xuyên nhiều hệ ngoài.

Trino chỉ tính, không lưu: đọc dữ liệu tại chỗ ở định dạng mở (Parquet/ORC) và ở nhiều nguồn cùng lúc. Điểm mạnh: truy vấn liên hợp, không khoá vào định dạng độc quyền, self-hosted kiểm soát chi phí/tuân thủ. Điểm yếu: bạn phải vận hành cụm, và hiệu năng phụ thuộc vào định dạng/bố cục dữ liệu ở nguồn.

So với Spark

Spark là engine xử lý dữ liệu đa năng, mạnh cho batch nặng, ETL, machine learning, xử lý phức tạp với khả năng chịu lỗi từng stage (ghi checkpoint, thử lại task hỏng). Xem Spark SQLtinh chỉnh Spark. Spark có thể chạy job hàng giờ trên petabyte một cách bền bỉ.

Trino tối ưu cho truy vấn SQL tương tác, độ trễ thấp — analyst gõ câu hỏi ad-hoc và cần trả lời nhanh. Nó không nhắm tới ETL nặng hay ML. Nhiều tổ chức dùng cả hai: Spark cho pipeline biến đổi dữ liệu nặng, Trino cho tầng truy vấn interactive/BI phía trên kết quả.

Bảng đối chiếu

Tiêu chíTrinoData warehouse (Snowflake/BigQuery)Spark
Lưu dữ liệu?Không (chỉ compute)Có (lưu + tính)Không (chỉ compute)
Bài toán chínhSQL tương tác, ad-hoc, liên hợpDW quản lý, báo cáo ổn địnhBatch nặng, ETL, ML
Truy vấn nhiều nguồn 1 câu SQLCó (federated)Hạn chếCó (nhưng thiên batch)
Độ trễ truy vấnThấp (interactive)Thấp–trungCao hơn (batch)
Chịu lỗi job dàiHạn chế (in-memory, có thể OOM)Được lo bởi nền tảngMạnh (checkpoint, retry)
Vận hànhSelf-hosted cụmServerless managedSelf-hosted / managed
Định dạng dữ liệuMở (Parquet/ORC...)Độc quyền của warehouseMở

Quy tắc ghi nhớ: nếu cần "gõ SQL ad-hoc trên data lake và/hoặc nhiều nguồn, có kết quả nhanh" → Trino. Nếu cần "kho quản lý sẵn, báo cáo ổn định, ít vận hành" → warehouse. Nếu cần "biến đổi dữ liệu nặng, ETL, ML, job dài chịu lỗi" → Spark.

Các use case điển hình

  • Truy vấn data lakehouse: đọc bảng mở (Iceberg, Delta, Hudi) trên object storage bằng SQL tương tác. Đây là lý do Trino gắn liền với kiến trúc lakehouse — xem lakehouse trong data engineering và bài truy vấn lakehouse trong series này.
  • Lớp truy vấn thống nhất (SQL-on-anything): một điểm SQL duy nhất cho analyst, đứng trên data lake + nhiều DB nghiệp vụ + stream, xoá bỏ việc phải học cú pháp riêng của từng hệ.
  • Phục vụ BI: kết nối công cụ BI (Superset, Tableau, Power BI...) tới Trino để dashboard truy vấn thẳng data lake mà không cần copy dữ liệu vào một warehouse riêng.
  • Migration & khảo sát dữ liệu: JOIN dữ liệu cũ và mới ở hai hệ khác nhau trong một câu SQL để đối chiếu, kiểm tra chất lượng khi di trú hệ thống.

Use case thực tế

Bối cảnh: NCB muốn một lớp truy vấn SQL thống nhất cho đội phân tích rủi ro và tuân thủ. Dữ liệu đang nằm rải ở ba nơi: (1) data lake chứa ~2 tỷ dòng log giao dịch/năm dạng Parquet trên object storage; (2) PostgreSQL core banking giữ hồ sơ khách hàng và tài khoản (nguồn sự thật, ACID); (3) Elasticsearch chứa log cảnh báo gian lận. Trước đây, mỗi báo cáo ghép ba nguồn phải qua một pipeline ETL riêng gom về một bảng trung gian — mỗi thay đổi yêu cầu mất 2–3 ngày dựng lại pipeline, và dữ liệu luôn trễ một chu kỳ.

Giải pháp với Trino:

  1. Dựng một cụm Trino nhỏ (1 coordinator + vài worker) self-hosted trong hạ tầng ngân hàng để dữ liệu nhạy cảm không rời on-premise.
  2. Khai báo ba catalog: hive (data lake), postgres (core banking), elastic (log gian lận). Không sao chép dữ liệu — chỉ trỏ connector.
  3. Analyst viết một câu SQL liên hợp JOIN log giao dịch (data lake) với hồ sơ khách hàng (Postgres) và cờ cảnh báo (Elasticsearch), lọc theo khoảng thời gian và ngưỡng số tiền, để tìm các khách hàng có giao dịch bất thường.
  4. Kết nối Superset vào Trino để đội tuân thủ tự khám phá dữ liệu, không phải đợi pipeline mới.

Kết quả (minh hoạ điển hình của loại triển khai này): thời gian ra một báo cáo ghép ba nguồn mới giảm từ 2–3 ngày (dựng ETL) xuống còn phút tới giờ (viết SQL trực tiếp); truy vấn ad-hoc trên vài trăm triệu dòng Parquet trả kết quả trong vài giây tới vài chục giây nhờ MPP + predicate pushdown; không phát sinh bản sao dữ liệu trung gian. Các bài sau sẽ dựng chi tiết: cách connector & catalog hoạt động, viết SQL liên hợp hiệu quả, và tinh chỉnh hiệu năng để tránh OOM.

Các con số ở phần "Kết quả" là minh hoạ theo bậc độ lớn thường gặp của loại use case này, không phải số đo chính thức của một hệ thống NCB cụ thể.

Ghi nhớ

  • Trino là query engine SQL phân tán, mã nguồn mởKHÔNG lưu dữ liệu, chỉ mang compute, đọc dữ liệu tại chỗ ở nhiều nguồn qua connector.
  • Nguồn gốc từ PrestoDB của Facebook; Trino là nhánh phát triển chính hiện nay (tách năm 2019, từng gọi PrestoSQL).
  • Khả năng lõi là truy vấn liên hợp (federated query): một câu SQL JOIN dữ liệu từ data lake + PostgreSQL + MySQL + Kafka + Elasticsearch... mà không cần ETL gộp trước.
  • Tham chiếu bảng theo catalog.schema.table; mỗi catalog dựa trên một connector trỏ tới một nguồn.
  • Kiến trúc: coordinator lập kế hoạch & gom kết quả, worker đọc và tính song song (MPP), connector kết nối từng loại nguồn.
  • Tốc độ đến từ MPP + thực thi pipelined trong bộ nhớ (không ghi đĩa giữa stage) → hợp interactive; đổi lại job siêu lớn có thể OOM.
  • Định vị: khác warehouse (Snowflake/BigQuery lưu + tính), khác Spark (batch/ETL/ML nặng, chịu lỗi job dài). Trino = SQL-on-anything, tương tác, ad-hoc.
  • Use case: query lakehouse, lớp truy vấn thống nhất trên nhiều nguồn, phục vụ BI. Trong ngân hàng: một điểm truy vấn SQL trên data lake + nhiều DB nghiệp vụ.
  • Trino nói SQL gần chuẩn ANSI nhưng chạy trên Trino — không đánh dấu chạy được trong sandbox Postgres (trừ đúng một câu ANSI thuần dùng minh hoạ).

Bài viết liên quan

Cách index hoạt động (B-Tree), đọc EXPLAIN, seq scan vs index scan và mẫu tối ưu truy vấn.

13 thg 7, 2026 4

Khoá, ràng buộc, quan hệ và chuẩn hoá 1NF/2NF/3NF — thiết kế lược đồ đúng từ đầu.

13 thg 7, 2026 4

Kết nhiều bảng đúng cách: các loại JOIN, bẫy thường gặp và phép hợp tập.

13 thg 7, 2026 3

Truy vấn lồng, CTE (WITH), CTE đệ quy và hàm cửa sổ — vũ khí cho phân tích nâng cao.

13 thg 7, 2026 3