Google BigQuery - Bài học 4: Mạng lưới Jupiter & Borg – Cách BigQuery luân chuyển dữ liệu nội bộ với băng thông hàng Petabit/giây
Chào anh em! Qua 3 bài học đầu tiên, chúng ta đã cùng nhau giải phẫu được 3/4 "tứ trụ" làm nên sức mạnh của Google BigQuery:
-
Colossus: Hệ thống file phân tán lưu trữ hàng Petabyte dữ liệu.
-
Capacitor: Định dạng lưu trữ dạng cột giúp nén cực sâu và cắt tỉa khối dữ liệu thừa (Block Pruning).
-
Dremel: Động cơ tính toán phân tán (MPP) dùng hàng ngàn nhân CPU (Slots) để xử lý câu lệnh SQL song song.
Tuy nhiên, có một nghịch lý vật lý mà bất kỳ kỹ sư Backend hay System Architect nào khi nhìn vào kiến trúc tách biệt Compute (Dremel) và Storage (Colossus) cũng phải nhíu mày:
"Trong một máy chủ Database truyền thống, dữ liệu đọc từ ổ cứng NVMe SSD qua khe cắm PCIe Gen 4 ngay trên Mainboard có tốc độ khoảng 5 - 7 GB/s (tương đương ~50 Gbps). Vậy mà khi chạy query nặng còn nghẽn lên nghẽn xuống. Bây giờ ông Google lại tách CPU một nơi, ổ cứng một nẻo rồi kéo dữ liệu qua dây mạng nội bộ (Network I/O). Chẳng lẽ tốc độ đọc qua dây mạng của Google còn nhanh hơn cả đọc ổ cứng gắn trên Mainboard?!"
Câu trả lời ngắn gọn là: Đúng vậy!
Nếu bạn mang kiến trúc tách rời của BigQuery đi triển khai trên một hệ thống mạng LAN doanh nghiệp thông thường (1 Gbps hoặc 10 Gbps), hệ thống sẽ lập tức "chết lâm sàng" vì nghẽn cổ chai mạng. Để biến điều không tưởng này thành hiện thực, Google dựa vào hai vũ khí tối mật bên dưới tầng hạ tầng: Mạng lưới Jupiter (Jupiter Network Fabric) và Hệ điều hành cụm máy chủ Borg.
Hôm nay, chúng ta sẽ lặn xuống tầng sâu nhất của Data Center Google để xem dữ liệu thực sự "bay" như thế nào khi bạn bấm nút Run Query!
1. Nút thắt cổ chai của mạng Data Center truyền thống
Trong kiến trúc mạng truyền thống (Hierarchical / Tree Topology), các máy chủ được nối vào Switch ở đầu tủ rack (ToR - Top of Rack), các Switch này lại gom lên tầng Aggregation, rồi cuối cùng mới đẩy lên một vài con Core Switch trung tâm cực lớn.
Mô hình hình cây này có 2 điểm yếu chí mạng khi xử lý dữ liệu lớn (Big Data):
-
Nghẽn cổ chai ở tầng trên (Oversubscription): Ở dưới đáy có hàng ngàn máy chủ cùng bơm dữ liệu lên, nhưng càng lên đỉnh cây thì số lượng đường truyền càng hẹp lại. Khi 500 máy chủ của tầng Compute cùng lúc yêu cầu đọc 10 Terabyte dữ liệu từ 500 máy chủ tầng Storage nằm ở các dãy tủ rack khác nhau, con Core Switch ở giữa sẽ bị "tắc đường" giống hệt như ngã tư Hàng Xanh giờ cao điểm!
-
Chi phí phần cứng đắt đỏ & Khó mở rộng: Những con Core Switch thương mại có băng thông lớn đắt tới hàng trăm ngàn đô la nhưng vẫn có giới hạn vật lý, không thể mở rộng tuyến tính (Scale-out) cho hàng trăm ngàn máy chủ.
2. Mạng lưới Jupiter: Siêu xa lộ 1+ Petabit/giây của Google
Không hài lòng với các thiết bị mạng thương mại trên thị trường, Google đã tự thiết kế phần cứng Switch, cáp quang và giao thức điều khiển mạng riêng mang tên Jupiter.
Thay vì dùng kiến trúc hình cây cổ điển, Jupiter sử dụng kiến trúc Clos Network Topology (Mạng đa tầng đan chéo - Spine & Leaf) kết hợp với công nghệ SDN (Software-Defined Networking) và chuyển mạch quang học (Optical Circuit Switching - OCS).
Plaintext
[ Spine Blocks ] [ Spine Blocks ] [ Spine Blocks ] <-- Hàng trăm đường truyền cáp quang song song
/ | \ / | \ / | \
/ | \ / | \ / | \ <-- Kiến trúc đan chéo (Clos Fabric):
/ | \ / | \ / | \ Mọi điểm đều nối với nhau, không có nút cổ chai!
[Rack 1 - Dremel] [Rack 2 - Colossus] [Rack 3 - Shuffle] <-- Hàng chục ngàn máy chủ trong Data Center
Sức mạnh khủng khiếp của Jupiter nằm ở đâu?
-
Băng thông Bisection vượt mốc 1 Petabit/giây (1.000.000 Gbps): "Bisection Bandwidth" là thông số đo lường trường hợp xấu nhất: Nếu bạn cắt đôi cả trung tâm dữ liệu gồm 100.000 máy chủ thành 2 nửa (một nửa chứa CPU Dremel, một nửa chứa ổ cứng Colossus) và bắt tất cả nói chuyện chéo với nhau cùng một lúc, Jupiter vẫn cung cấp tổng băng thông lên tới hơn 1 Petabit mỗi giây!
-
Để bạn dễ hình dung con số 1 Petabit/giây lớn cỡ nào: Nó đủ để truyền tải hơn 100.000 bộ phim chuẩn Full HD trong đúng 1 giây, hoặc cho phép toàn bộ dân số trên Trái Đất thực hiện cuộc gọi thoại cùng một lúc mà không bị trễ nhịp nào.
-
Tốc độ mạng vượt mặt tốc độ ổ đĩa: Mỗi máy chủ trong cụm có thể truyền nhận dữ liệu ở tốc độ từ 40 Gbps đến 100+ Gbps. Điều này có nghĩa là khi một Leaf Node (Slot) của Dremel đọc các mảnh dữ liệu từ hàng trăm ổ đĩa khác nhau trên Colossus thông qua mạng Jupiter, tổng băng thông đổ về RAM của nó còn nhanh hơn nhiều lần so với việc đọc từ một ổ cứng vật lý gắn trực tiếp trên máy chủ đó!
-
Định tuyến thông minh bằng phần mềm (SDN): Thay vì để từng con Switch tự mò đường (như giao thức BGP/OSPF truyền thống), Jupiter có một "Bộ não trung tâm" nhìn thấy toàn cảnh lưu lượng của cả Data Center. Nếu một nhánh cáp quang đang đông đúc vì một job chạy Machine Learning, nó lập tức bẻ lái luồng dữ liệu BigQuery của bạn sang hàng ngàn nhánh cáp quang rảnh rỗi khác chỉ trong vài micro-giây.
3. Cơn ác mộng mang tên "Data Shuffle" và cách Jupiter + Dremel giải quyết
Nếu câu lệnh SQL của bạn chỉ đơn giản là SELECT SUM(amount) FROM orders WHERE status = 'COMPLETED', các Leaf Nodes chỉ cần đọc dữ liệu từ Colossus, tự cộng cục bộ rồi gửi đúng 1 con số tổng lên tầng trên. Rất nhẹ nhàng!
Nhưng điều gì xảy ra khi anh em chạy một câu lệnh GROUP BY trên một cột có hàng chục triệu giá trị khác nhau (High Cardinality - ví dụ GROUP BY user_id) hoặc JOIN hai bảng khổng lồ (ví dụ bảng orders 1 tỷ dòng JOIN với bảng order_items 5 tỷ dòng theo order_id)?
Lúc này, bài toán khó nhất của mọi hệ thống phân tán xuất hiện: Data Shuffle (Tráo đổi dữ liệu chéo giữa các Node).
Tại sao JOIN và GROUP BY lớn lại cần Shuffle?
Giả sử đơn hàng có order_id = 999 nằm ở Node A (khi đọc bảng orders), nhưng danh sách sản phẩm của đơn hàng order_id = 999 đó lại vô tình nằm ở Node B và Node C (khi đọc bảng order_items).
Để ghép (JOIN) chúng lại với nhau, các Node bắt buộc phải băm (Hash) cột order_id và bắn dữ liệu chéo qua mạng cho nhau sao cho mọi bản ghi có cùng mã order_id = 999 phải tụ họp về chung một Node xử lý!
Plaintext
[Tầng đọc dữ liệu - Stage 1] [Tầng trộn - In-Memory Shuffle] [Tầng JOIN/GROUP BY - Stage 2]
Leaf Node 1 (Đọc mảnh A) ----\ /----> Worker Node X (Xử lý Hash=1)
\===(Bắn hàng tỷ dòng chéo qua mạng Jupiter)===> [ RAM ] ===
Leaf Node 2 (Đọc mảnh B) ----/ \----> Worker Node Y (Xử lý Hash=2)
Điểm yếu của Hadoop / Spark truyền thống vs Sự vượt trội của BigQuery:
-
Trên Hadoop MapReduce hoặc Spark đời cũ: Khi thực hiện Shuffle hàng Terabyte dữ liệu, các Node tính toán phải ghi dữ liệu trung gian xuống ổ cứng cục bộ (Spill to Disk), sau đó kéo qua mạng LAN chậm chạp sang Node khác. Quá trình đọc/ghi đĩa tạm và chờ mạng này chiếm tới 70% - 80% tổng thời gian chạy query.
-
Cơ chế Disaggregated In-Memory Shuffle của BigQuery: Google tách riêng một tầng Shuffle dựa hoàn toàn trên bộ nhớ RAM (In-Memory Shuffle) nằm ngay trên lưng mạng lưới Jupiter!
-
Ngay khi các Leaf Nodes ở Stage 1 vừa đọc và lọc xong dữ liệu từ Colossus, chúng bắn thẳng kết quả qua mạng Jupiter (tốc độ hàng trăm GB/s) vào các RAM Buffers của tầng Shuffle mà không cần chờ đợi.
-
Các Nodes ở Stage 2 lập tức hút dữ liệu đã được phân loại theo Hash từ tầng RAM Shuffle này về để thực hiện
JOINhoặcGROUP BY. -
Nhờ việc tách rời Compute và Shuffle trên nền mạng Jupiter, ngay cả khi một số Node tính toán chạy chậm hơn các Node khác (hiện tượng Stragglers), toàn bộ hệ thống vẫn không bị đứng hình chờ nhau!
-
4. Borg – "Nhạc trưởng" điều phối hàng triệu nhân CPU phía sau hậu trường
Chúng ta đã có kho lưu trữ Colossus, động cơ Dremel, định dạng Capacitor và mạng Jupiter. Nhưng ai là người đứng ra cấp phát máy chủ, bật tắt hàng ngàn container, và đảm bảo khi một máy chủ bị cháy RAM thì câu lệnh SQL của bạn không bị báo lỗi Query Failed?
Đó chính là Borg – Hệ thống quản lý cụm máy chủ (Cluster Manager) nội bộ huyền thoại của Google (và chính là "cha đẻ" của Kubernetes - K8s mà anh em DevOps/Backend đang dùng ngày nay!).
Khi bạn làm việc với BigQuery, Borg âm thầm thực hiện 3 nhiệm vụ sống còn sau:
4.1. Cấp phát hàng ngàn Slots trong vài mili-giây (Multi-tenant Scheduling)
Hàng chục ngàn máy chủ vật lý trong Data Center của Google đều do Borg quản lý. Khi bạn bấm chạy một câu lệnh SQL nặng, bộ điều phối của BigQuery sẽ yêu cầu Borg cấp phát tài nguyên.
Chỉ trong tích tắc, Borg tìm ra 2.000 Slots (mỗi Slot là một container cô lập gồm một phần CPU và RAM) đang rảnh rỗi trên khắp cụm máy chủ để giao việc cho câu truy vấn của bạn. Ngay khi câu lệnh chạy xong ở giây thứ 3, Borg lập tức thu hồi 2.000 Slots đó để cấp cho người dùng khác. Đó chính là bí mật của từ khóa Serverless!
4.2. Chống lỗi tự động giữa chừng (Mid-Query Fault Tolerance)
Hãy thử làm một bài toán xác suất: Một máy chủ vật lý rất hiếm khi hỏng. Nhưng nếu câu truy vấn của bạn huy động cùng lúc 3.000 máy chủ để quét 50 TB dữ liệu, thì xác suất có ít nhất 1 máy chủ bị treo, rớt mạng hoặc ổ cứng phản hồi chậm ngay trong lúc câu lệnh đang chạy là cực kỳ cao!
Trong các hệ thống MPP truyền thống, chỉ cần 1 Node trong cụm bị chết giữa chừng, toàn bộ câu lệnh SQL sẽ bị hủy (Failed) và bạn phải chạy lại từ đầu.
Nhưng với sự kết hợp của Borg + Dremel + In-Memory Shuffle:
-
Nếu Borg phát hiện Slot A đang xử lý mảnh dữ liệu số 15 mà bị treo hoặc phản hồi quá chậm (Tail Latency), Dremel sẽ không chờ đợi.
-
Nó lập tức yêu cầu Borg tạo ra một Slot B trên một máy chủ khỏe mạnh khác để đọc lại mảnh dữ liệu số 15 từ Colossus (hoặc lấy lại từ bộ nhớ đệm Shuffle) và xử lý thay thế!
-
Thậm chí, với những mảnh dữ liệu cuối cùng của câu truy vấn, Dremel còn chủ động gửi cho 2 Slots cùng làm song song (kỹ thuật Speculative Execution / Backup Tasks) – thằng nào xong trước thì lấy kết quả của thằng đó. Nhờ vậy, bạn luôn nhận được kết quả nhanh và ổn định nhất mà không hề hay biết có một máy chủ vật lý vừa "hy sinh" ở dưới tầng hạ tầng!
5. Ứng dụng thực tế: Hiểu Jupiter & Borg giúp bạn tối ưu SQL như thế nào?
Nhiều anh em sẽ nghĩ: "Mạng Jupiter hay Borg là việc của Google lo, mình là người viết SQL thì quan tâm làm gì?"
Sai lầm nhé! Hiểu rõ cơ chế vận chuyển dữ liệu của Jupiter và cách chia việc của Borg sẽ giúp bạn bắt bệnh những câu lệnh SQL chạy chậm cực kỳ chuẩn xác:
-
Cảnh giác với "Kẻ hủy diệt" Data Skew (Lệch dữ liệu khi Shuffle):
Dù mạng Jupiter có nhanh đến đâu và Borg cấp cho bạn 2.000 Slots, nhưng nếu bạn thực hiện
JOINhoặcGROUP BYtrên một cột mà có tới 80% số dòng đều mang giá trịNULLhoặcuser_id = 0(khách vãng lai chưa đăng nhập) thì sao?Theo quy tắc băm (Hash) khi Shuffle, toàn bộ 80% dữ liệu mang giá trị
0đó sẽ bị dồn hết về cho đúng 1 Slot duy nhất xử lý! Kết quả là 1.999 Slots kia làm xong trong 1 giây rồi ngồi chơi, còn 1 Slot tội nghiệp phải gồng mình xử lý hàng trăm triệu dòng dẫn đến lỗiResources exceeded during query execution(Tràn RAM của Slot) hoặc câu lệnh chạy mất cả chục phút.Mẹo thực chiến: Luôn lọc bỏ các giá trị
NULLhoặc các giá trị mặc định gây lệch (WHERE user_id IS NOT NULL AND user_id != 0) trước khi thực hiện phépJOIN! -
Tối ưu lượng dữ liệu đưa vào tầng Shuffle (Early Filtering & Projection):
Bộ nhớ In-Memory Shuffle trên mạng Jupiter rất nhanh nhưng nó có hạn ngạch (Quota) cho mỗi truy vấn. Đừng bao giờ viết
SELECT *ở các bảng con (Subquery/CTE) rồi mới đem điJOINhoặcORDER BY. Hãy chỉ giữ lại đúng những cột thực sự cần thiết và lọcWHEREcàng sớm càng tốt để các gói tin bay qua mạng Jupiter nhẹ nhàng nhất có thể. -
Tại sao
ORDER BYkhông kèmLIMITtrên bảng tỷ dòng lại bị cấm/chậm?Để sắp xếp (
ORDER BY) toàn bộ 1 tỷ dòng, hàng ngàn Slots không thể tự làm độc lập được mà cuối cùng phải dồn toàn bộ kết quả về 1 Node duy nhất (Root/Mixer) để ghép thứ tự từ đầu đến cuối. Lúc này, sức mạnh song song của Borg bị vô hiệu hóa hoàn toàn tại nút cổ chai cuối cùng! Vì vậy, chỉ dùngORDER BYở bước cuối cùng sau khi đã gom nhóm (GROUP BY) hoặc đi kèm vớiLIMITnhỏ.
Tổng kết Bài học 4 (Và khép lại Bức tranh Kiến trúc cốt lõi)
Vậy là qua 4 bài học đầu tiên, anh em đã nắm trọn trong tay toàn bộ "nội công tâm pháp" về kiến trúc bên dưới của Google BigQuery – thứ mà rất nhiều người dùng BigQuery lâu năm cũng chỉ hiểu lờ mờ:
Plaintext
+-------------------------------------------------------------------------+
| BỨC TRANH TOÀN CẢNH KHI BẠN BẤM "RUN QUERY" TRÊN GOOGLE BIGQUERY |
+-------------------------------------------------------------------------+
1. [BORG] Cấp phát ngay lập tức hàng ngàn Slots (CPU + RAM) rảnh rỗi.
2. [DREMEL] Biến câu lệnh SQL thành Cây thực thi phân tán (Execution Tree).
3. [COLOSSUS] Mở các mảnh lưu trữ phân tán chứa bảng dữ liệu của bạn.
4. [CAPACITOR] Chỉ đọc đúng cột cần thiết, dùng Min/Max bỏ qua khối thừa,
tính toán trực tiếp trên dữ liệu nén (RLE / Dictionary).
5. [JUPITER] Bơm dữ liệu từ Colossus lên Dremel và thực hiện In-Memory Shuffle
(cho JOIN / GROUP BY) với băng thông hàng Petabit/giây!
+-------------------------------------------------------------------------+
Khi đã hiểu rõ bộ máy bên dưới vận hành ra sao, giờ là lúc chúng ta bước lên mặt đất để bắt tay vào thiết kế và quản trị thực tế! Trong một doanh nghiệp có hàng chục phòng ban và hàng trăm dự án Microservices, làm sao để tổ chức các bảng dữ liệu trên BigQuery cho khoa học, phân quyền chặt chẽ và không biến Data Warehouse thành một "bãi rác dữ liệu" (Data Swamp)?
Hẹn gặp lại anh em ở Bài học 5: Cấu trúc phân cấp tài nguyên – Quản lý Organization, Project, Dataset, Table và View chuẩn doanh nghiệp!
All Rights Reserved