BigQuery 13 — Đọc Execution Graph (biểu đồ thực thi)

15 thg 7, 2026 5 lượt xem
#data-engineering
#bigquery
#performance
#query-plan
#execution-graph

Vì sao cần một biểu đồ thay vì một bảng số

bài trước — Query plan & Execution details bạn đã học cách đọc query plan dưới dạng bảng: mỗi stage một hàng, với các cột recordsRead, slotMs, waitMs, computeMs… Dạng bảng rất tốt để tra cứu con số chính xác, nhưng nó có một nhược điểm: bạn không thấy được hình dạng của query. Stage 5 phụ thuộc stage nào? Dữ liệu chảy từ đâu tới đâu? Chỗ nào là nút thắt cổ chai? Muốn biết, bạn phải tự ghép inputStages trong đầu — mệt và dễ sai với query nhiều chục stage.

BigQuery console thế hệ mới bổ sung tab Execution graph (biểu đồ thực thi): cùng dữ liệu query plan đó, nhưng vẽ thành một DAG trực quan — mỗi node là một stage, mỗi cạnh là luồng dữ liệu (shuffle) giữa các stage. Chỉ liếc qua, bạn thấy ngay cây thực thi: bảng nào được đọc, join xảy ra ở đâu, node nào "đỏ" vì tốn slot. Đây là cách nhanh nhất để hiểu query đang làm gì trước khi soi vào từng con số.

Mô hình tinh thần: Dạng bảng (bq-12) trả lời "bao nhiêu"; Execution graph trả lời "hình dạng ra sao, chảy đi đâu". Hai tab bổ trợ nhau — graph để định hướng, bảng để đo đạc. Bài này tập trung vào graph; đọc sâu từng thông số node để lại cho bài kế — Các pha & thông số của stage.

DAG: node là stage, cạnh là shuffle

Trước khi nhìn graph thật, cần nắm đúng ngữ nghĩa của hai thành phần:

  • Node = một stage. Mỗi stage là một nhóm công việc thực hiện song song trên nhiều worker (parallel inputs). Node hiển thị name stage (ví dụ S00: Input, S04: Join+), phần trăm slot-time nó chiếm, và số records vào/ra.
  • Cạnh = luồng dữ liệu giữa hai stage. Khi stage A đổ kết quả cho stage B, dữ liệu được shuffle (ghi ra lớp trung gian rồi phân phối lại) qua mạng Jupiter. Cạnh trong graph chính là quan hệ inputStages trong query plan: B có A trong inputStages thì có mũi tên A → B. Con số ghi trên cạnh là số records chảy qua (khớp recordsWritten của stage nguồn ≈ recordsRead của stage đích).

Chiều đọc: từ dưới/đầu vào (input stages đọc bảng) đi lên đầu ra (output stage trả kết quả). Input stages là các node không có cạnh vào (leaf) — chúng chạy step READ trên một bảng gốc. Output là node không có cạnh ra, thường tên Output, gom kết quả cuối cùng.

Graph trên là một query join transactions với customers rồi GROUP BY chi nhánh theo tháng. Chỉ nhìn hình, bạn đọc được câu chuyện: hai bảng được đọc (S00, S01) → join lại (S02) → gộp nhóm hai pha (S03 partial, S04 final) → ghi ra (S05). Node đỏ đậm (S00, S02) là nơi tốn slot nhất; node xanh (S05) gần như miễn phí. Đó là toàn bộ ý tưởng.

Màu và độ đậm node: đọc nhiệt độ query

Console tô màu/độ đậm node theo mức độ tốn tài nguyên, thường là slot-time (slotMs) hoặc thời gian của stage — stage càng "nặng" node càng đậm/đỏ. Đây là bản đồ nhiệt (heatmap) để mắt bạn nhảy ngay tới chỗ đáng lo mà không cần đọc số.

Sắc độ nodeÝ nghĩa thường gặpPhản xạ khi thấy
Đỏ / đậm nhấtStage chiếm phần lớn slotMs của cả jobĐây là nghi phạm số 1 — soi kỹ ở graph rồi mở thông số
Cam / trung bìnhStage tốn vừa phảiXem xét nếu tối ưu được stage đỏ mà chưa đủ
Xanh / nhạtStage nhẹ, gần như miễn phíBỏ qua, đừng phí công tối ưu

Quan trọng: màu là tương đối trong một job, không phải tuyệt đối giữa các query. Một node đỏ trong query nhẹ có thể vẫn rẻ hơn node xanh của query khổng lồ. Màu chỉ giúp bạn xếp thứ tự ưu tiên bên trong một lần chạy. Muốn con số thật để so sánh liên-job, bạn quay lại dạng bảng ở bq-12 hoặc truy vấn INFORMATION_SCHEMA.JOBS.

Phóng to một node: đọc gì trên mỗi stage

Click (hoặc hover) vào một node, console bung ra thẻ thông số của stage đó. Đây là "danh thiếp" của node — mô phỏng cách hiển thị (tên stage + %slot + records + steps):

┌─────────────────────────────────────────────────────────┐
│  S02: Join+                              ▓▓▓▓▓▓▓░░  38%   │  ← tên stage + thanh %slot-time của job
├─────────────────────────────────────────────────────────┤
│  Records read      49.2M      Records written   48.0M    │  ← recordsRead / recordsWritten
│  Parallel inputs   1,024      Completed         1,024    │  ← parallelInputs / completedParallelInputs
│  Slot time         41,800 ms                             │  ← slotMs (đóng góp vào %slot ở trên)
│  Shuffle out       6.1 GB     Spilled           0 B      │  ← shuffleOutputBytes / ...BytesSpilled
├─────────────────────────────────────────────────────────┤
│  Steps                                                   │
│   • READ        FROM __stage00_output  (transactions)    │
│   • READ        FROM __stage01_output  (customers)       │
│   • JOIN        HASH JOIN ON customer_id                 │
│   • WRITE       TO __stage02_output                      │
└─────────────────────────────────────────────────────────┘

Cùng thông tin ấy, khi vẽ trong graph, mỗi node được chú thích gọn — dưới đây là bản "phóng to" node S02 dạng Mermaid:

Ý nghĩa nhanh của các field trên thẻ (chi tiết ngưỡng cảnh báo để dành cho bq-14):

FieldÝ nghĩaĐọc gì từ nó
recordsRead / recordsWrittenSố bản ghi vào / ra stageSo sánh in vs out biết stage lọc/nở dữ liệu bao nhiêu
parallelInputs / completedParallelInputsSố đơn vị việc song song / đã xongChênh nhau = còn đang chạy hoặc bị chờ
slotMsTổng slot-time stage tiêu thụNguồn của màu node; số càng lớn node càng đỏ
shuffleOutputBytesBytes ghi ra lớp shuffle để chuyển stage sauLớn = nhiều dữ liệu chảy qua mạng
shuffleOutputBytesSpilledPhần shuffle phải tràn xuống đĩa> 0 là cờ đỏ — thiếu bộ nhớ/slot, cần chú ý

Đọc số records trên cạnh: nơi dữ liệu nở ra hay co lại

Con số trên cạnh là thứ kể chuyện rõ nhất về "chuyện gì đang xảy ra với dữ liệu". Đọc dọc theo luồng, bạn thấy dữ liệu nở ra (join tạo tích, nhân bản) hay co lại (filter, aggregate gộp nhóm):

Cách đọc luồng records này cho bạn ba tín hiệu quan trọng:

  1. Số co lại đột ngột ở cạnh vào một node aggregate (48.0M → 12.4K) là bình thường và tốt: GROUP BY đang gộp nhóm. Nếu số không co ở nơi bạn mong nó co, có thể khóa nhóm sai hoặc thiếu pre-aggregation.
  2. Số nở ra sau một join (ví dụ 48M → 300M) cảnh báo join fan-out — quan hệ nhiều-nhiều hoặc thiếu điều kiện join, tạo ra tích khổng lồ. Đây là nguyên nhân kinh điển khiến query nổ slot.
  3. Số vào input stage lớn hơn nhiều số ra nghĩa là stage đọc bảng đang quét thừa rồi mới FILTER. Cờ này gợi ý thiếu phân vùng hoặc clustering để cắt bytes ngay từ đầu.

Fan-in tại join/aggregate và bước repartition

Hai hình dạng bạn phải nhận ra ngay trên graph:

Fan-in — nhiều cạnh cùng đổ vào một node. Xảy ra ở join (hai nhánh bảng gặp nhau) hoặc aggregate final (nhiều stage partial gộp về một stage cuối). Fan-in là điểm dữ liệu hội tụ, thường là node đậm màu vì phải xử lý tất cả đầu vào. Trong graph tổng ở đầu bài, S02 (join) và S04 (final aggregate) đều là điểm fan-in.

Repartition — một bước phân phối lại dữ liệu giữa các stage để chuẩn bị cho phép toán sau. Ví dụ trước một GROUP BY branch, BigQuery cần đưa mọi bản ghi cùng branch về cùng một worker, nên chèn một stage Repartition (băm theo khóa rồi shuffle lại). Trên graph, repartition hiện ra như một node trung gian mảnh, với shuffleOutputBytes lớn nhưng records vào ≈ records ra (không lọc gì, chỉ xếp lại chỗ).

Nhận ra repartition quan trọng vì nó là chi phí ẩn: nó không tính toán logic nghiệp vụ nào, chỉ di chuyển dữ liệu qua Jupiter. Nếu bảng đã được clustered đúng khóa join/nhóm, BigQuery có thể giảm shuffle này. Nhìn thấy một node repartition khổng lồ ngay dưới một join là gợi ý điển hình để cân nhắc clustering.

Quy trình 6 bước: nhìn graph này là hiểu query làm gì

Gộp lại thành một checklist để bạn áp dụng mỗi khi mở tab Execution graph:

  1. Tìm các leaf (input stages) — node không có cạnh vào. Đọc step READ của chúng để biết query chạm những bảng gốc nào và đọc bao nhiêu records.
  2. Đi ngược lên output — theo mũi tên tới node không có cạnh ra (Output). Đây là "mạch truyện" của query.
  3. Quét màu — node nào đỏ/đậm nhất? Đó là nơi tốn slot nhất, ưu tiên số 1.
  4. Đọc số trên cạnh — dữ liệu nở ra ở đâu (coi chừng join fan-out), co lại ở đâu (aggregate hoạt động).
  5. Đánh dấu fan-in & repartition — mỗi join/aggregate-final là một điểm hội tụ; mỗi repartition là shuffle tốn kém có thể tối ưu bằng clustering.
  6. Zoom node đỏ — mở thẻ thông số của node nặng nhất, kiểm nhanh shuffleOutputBytesSpilled > 0 và chênh parallelInputs chưa hoàn tất.

Xong 6 bước, bạn đã có giả thuyết về việc query làm gì và điểm nghẽn ở đâu — mà chưa cần đọc một bảng số nào. Bước tiếp theo là định lượng giả thuyết đó: mở từng node và đọc các pha thời gian (waitMs, readMs, computeMs, writeMs) cùng dấu hiệu skew, được trình bày ở bq-14 — Các pha & thông số của stage; rồi biến chẩn đoán thành hành động tối ưu ở bq-15 — Chẩn đoán & tinh chỉnh query.

Execution graph khác gì dạng bảng (bq-12)

Execution graph (bài này)Execution details dạng bảng (bq-12)
Hình thứcDAG trực quan, node + cạnhBảng, mỗi stage một hàng
Mạnh ởThấy hình dạng: phụ thuộc, luồng, fan-in, nút nóngĐọc con số chính xác từng field
Đọc nhanhRất nhanh — liếc là thấy nút thắtChậm hơn — phải tự ghép inputStages
Màu/heatmapCó — theo slot-timeKhông (chỉ số thô)
Records trên cạnhCó, nhìn thấy luồngCó nhưng dạng cột rời recordsRead/Written
Khi nào dùngĐịnh hướng, tìm nghi phạmĐo đạc, so sánh, trích số để báo cáo

Cả hai tab đọc cùng một query plan (cùng mảng job_stages); chúng chỉ là hai cách trình bày. Thói quen tốt: bắt đầu bằng graph để định hướng, rồi nhảy sang bảng (hoặc thẻ node) khi cần con số.

Use case thực tế

Đội Data của NCB nhận cảnh báo một report cuối ngày về doanh số theo chi nhánh chạy chậm bất thường: bình thường ~40 giây, nay lên gần 5 phút. Analyst mở tab Execution graph của job.

Chỉ liếc graph, họ thấy ngay một node đỏ rực ở giữa với nhãn Join+, và điều bất thường là cạnh ra của nó ghi 1.4B records trong khi hai cạnh vào chỉ 48M (transactions) và 1.2M (customers). Dữ liệu nở gần 30 lần sau join — dấu hiệu kinh điển của join fan-out. Zoom node đỏ, thẻ thông số cho thấy shuffleOutputBytesSpilled đã lớn hơn 0 (shuffle tràn đĩa) và slotMs chiếm ~72% cả job.

Nguyên nhân: một thay đổi ở bảng customers khiến customer_id bị nhân bản (có bản ghi trùng do lỗi cập nhật), biến join 1-nhiều thành nhiều-nhiều. Fix bằng cách khử trùng customers trước khi join (thêm QUALIFY ROW_NUMBER() ... = 1). Sau sửa, cạnh ra của node join về lại 48M, node từ đỏ chuyển cam, thời gian chạy về ~38 giây. Toàn bộ chẩn đoán ban đầu chỉ mất một cái liếc vào con số trên cạnh — đúng thứ dạng bảng khó thấy ngay.

Ghi nhớ

  • Execution graph = DAG: node là stage, cạnh là shuffle/luồng dữ liệu; đọc từ input stages (leaf, READ bảng) đi lên output.
  • Màu/độ đậm node thể hiện mức tốn tài nguyên (thường slotMs), tương đối trong một job — dùng để xếp ưu tiên, không so sánh liên-job.
  • Số trên cạnh là số records: nở ra sau join = coi chừng fan-out; co lại trước aggregate = gộp nhóm bình thường.
  • Fan-in = nhiều cạnh đổ vào một node (join hoặc aggregate-final); repartition = node trung gian băm-shuffle lại dữ liệu, chi phí ẩn có thể giảm bằng clustering.
  • shuffleOutputBytesSpilled > 0 trên thẻ node là cờ đỏ thiếu bộ nhớ/slot.
  • Quy trình: tìm leaf → lên output → quét màu → đọc cạnh → đánh dấu fan-in/repartition → zoom node đỏ.
  • Graph để định hướng, bảng bq-12 để đo đạc — cùng một query plan, hai cách trình bày.
  • Sau khi có giả thuyết, định lượng bằng các pha thời gian ở bq-14 rồi tối ưu ở bq-15.

Nguồn tham khảo

Bài viết liên quan

So sánh các định dạng dữ liệu (CSV, JSON, XML, Avro, Parquet, ORC) và lý do lưu theo cột nhanh hơn cho phân tích. Bài đi sâu vào row vs columnar storage, nén (Snappy/gzip/zstd), schema evolution, OLTP vs OLAP, object storage và partitioning để tối ưu chi phí lẫn tốc độ truy vấn.

13 thg 7, 2026 10

Data Engineering là ngành xây dựng và vận hành hệ thống biến dữ liệu thô thành dữ liệu sạch, tin cậy, sẵn sàng cho phân tích và AI. Bài giới thiệu vai trò Data Engineer trong vòng đời dữ liệu (nguồn → ingestion → storage → transformation → serving), phân biệt với Analyst/Scientist/ML Engineer, bức tranh hệ sinh thái công cụ và bài toán đưa dữ liệu core banking sang kho phân tích.

13 thg 7, 2026 9

Stream processing là gì, khác biệt batch vs stream (bounded/unbounded), micro-batch (Spark) vs true streaming, Apache Flink là gì và định vị so với Spark Structured Streaming và Kafka Streams. Kiến trúc runtime JobManager/TaskManager, các tầng API, triết lý streaming-first và bối cảnh phát hiện gian lận ngân hàng.

13 thg 7, 2026 8

Vì sao một máy không đủ và cần xử lý phân tán: từ MapReduce, Hadoop/HDFS đến Apache Spark in-memory. Kiến trúc driver–executor–cluster manager, các mức trừu tượng RDD/DataFrame/Dataset, cơ chế lazy evaluation với DAG, và vì sao shuffle (wide dependency) là phần tốn kém nhất. Kèm PySpark, Spark SQL và các kỹ thuật tối ưu (partition, broadcast join, cache, chống skew) cùng khi nào KHÔNG nên dùng Spark.

13 thg 7, 2026 7

Cảm nhận của bạn

Bình luận

Bạn cần để viết bình luận.

Chưa có bình luận. Hãy là người đầu tiên chia sẻ!