Giao diện
"Đừng mang núi đến với thợ. Hãy đưa thợ đến chân núi, rồi chỉ gửi về những viên đá quý."
← Bài trước · Mục lục · Bài tiếp theo →
Bài này giải quyết vấn đề gì?
Năm 2004 Google công bố chỉ mục hơn 8 tỷ trang. Nghiên cứu Sawzall (2005) xử lý mẫu 450 GB nhật ký nén. Lần thu thập năm 1998 có 24 triệu trang với hơn 259 triệu liên kết. Không máy nào giữ nổi chừng ấy, và dữ liệu vốn đã nằm rải trên nhiều máy.
Map-Reduce là mô hình lập trình cho phép bạn chỉ viết hai hàm nhỏ, gồm Map và Reduce, trong khi hệ thống đảm nhiệm phần phức tạp: Chia dữ liệu, giao việc cho máy, chuyển dữ liệu theo khóa, phát hiện máy hỏng và chạy lại. Trong Khoa học dữ liệu, đây là nền móng cho mọi phép tổng hợp quy mô lớn: Đếm từ, xây chỉ mục, nhân , PageRank (Bài 03), và là tiền thân của Spark.
Nguồn đối chiếu: Slide Bài 02 của học phần, MMDS Chương 2 (mục 2.1.1–2.1.2, 2.2.1–2.2.6, 2.3.1, 2.5.1–2.5.2, bài tập 2.3.1), Dean & Ghemawat, MapReduce, OSDI 2004 và tài liệu Apache Hadoop 3.4.2.
Bố cục bài: (1) Giới thiệu → (2) Mô hình Map-Reduce → (3) Các ví dụ → (4) Chi phí và lợi ích song song hóa → (5) Hệ thống thực thi (đọc thêm) → (6) Thực hành Hadoop → (7) Bài tập MMDS.
Minh họa tương tác
uet: 1học: 1uet: 1dữ: 1liệu: 1học: 1
Mô hình tính toán trên tối đa 30 từ, minh họa ba pha và không mô phỏng thời gian truyền mạng.
Xem code minh họa
const pairs = words.map(word => [word, 1]);
const grouped = new Map();
for (const [key, value] of pairs) {
grouped.set(key, [...(grouped.get(key) || []), value]);
}
const counts = [...grouped].map(([key, values]) =>
[key, values.reduce((sum, value) => sum + value, 0)]
);1. Giới thiệu: Dữ liệu lớn trên nhiều máy
1.1. Bốn yêu cầu của một mô hình tính toán phân tán
| # | Yêu cầu | Ý nghĩa |
|---|---|---|
| 1 | Dữ liệu nằm trên nhiều máy | Chương trình phải xử lý các phần dữ liệu ở những nơi khác nhau |
| 2 | Song song hóa | Nhiều máy cùng làm các phần việc độc lập để rút ngắn thời gian |
| 3 | Đưa tính toán đến gần dữ liệu | Ưu tiên chạy tại máy đang lưu dữ liệu để giảm truyền mạng |
| 4 | Điều phối và phục hồi "trong suốt" | Lập trình viên mô tả phép tính, hệ thống lo chia việc, lập lịch, chống lỗi |
"Trong suốt" không có nghĩa máy không hỏng, mà là hệ thống phát hiện lỗi và chạy lại tác vụ khi có thể. Song song hóa nhằm giảm thời gian, không hứa tăng tốc tuyến tính.
1.2. Ví dụ khởi động: Cộng một dãy số trên ba máy
Ẩn dụ: Kiểm kê kho của chuỗi siêu thị
Ban giám đốc muốn biết tổng số chai nước trong cả chuỗi. Thay vì chở hàng của mọi chi nhánh về trụ sở để đếm, mỗi chi nhánh tự đếm kho của mình rồi chỉ gửi về một con số. Trụ sở cộng các con số lại. Cách làm này đúng vì phép cộng không quan tâm ai đếm trước, đếm theo nhóm nào.
Tính
Cách làm đúng nhờ hai tính chất của phép cộng:
- Giao hoán: Đổi thứ tự các số,
. - Kết hợp: Đổi cách nhóm,
.
Khái quát. Mọi toán tử
Số thực dấu phẩy động
Trên máy tính, phép cộng số float không kết hợp tuyệt đối do làm tròn: Đổi cách nhóm có thể cho kết quả khác ở các chữ số cuối. Các lập luận trong bài giả sử số học chính xác.
1.3. Lưu trữ phân tán HDFS
Hệ thống tệp Hadoop (HDFS) chia tệp thành khối và lưu nhiều bản sao của mỗi khối trên các máy khác nhau, thường trải trên nhiều rack (tủ máy) nối với nhau qua bộ chuyển mạch.
- Cục bộ hóa: bộ lập lịch ưu tiên máy có khối → cùng rack → rack khác.
- Chịu lỗi: mất một máy (hoặc một rack) vẫn còn bản sao ở nơi khác. NameNode chỉ đạo sao chép lại để bù bản sao thiếu.
Bản sao không phải dữ liệu mới
Khối A có 3 bản sao không có nghĩa dữ liệu của A được cộng 3 lần. Bản sao thuộc về lưu trữ (độ bền, vị trí đọc). Tính toán chỉ xử lý mỗi khối logic đúng một lần.
2. Mô hình tính toán Map-Reduce
2.1. Bài toán xuyên suốt: Đếm tần suất từ
Đặc tả. Cho
Đầu ra: Mỗi
| Ký hiệu | Ý nghĩa |
|---|---|
| Văn bản thứ | |
| Số từ trong văn bản | |
| Tập các từ xuất hiện trong toàn kho | |
| Số lần từ | |
| Tổng số lần xuất hiện của |
"Tần suất" ở đây là số lần xuất hiện, không chia cho tổng số từ, và không phải số văn bản chứa từ.
2.2. Ba bước: Map, nhóm theo khóa, Reduce
Ẩn dụ: Bưu điện phân loại thư
Mỗi nhân viên nhận thư (Map) đọc từng lá thư và dán nhãn mã bưu chính lên đó, họ không cần biết thư sẽ đi đâu tiếp. Trung tâm phân loại (hệ thống) gom mọi thư cùng mã bưu chính vào cùng một bao, gửi đến đúng bưu cục. Bưu cục địa phương (Reduce) mở bao của mình và xử lý từng mã một. Nhân viên chỉ cần biết "dán nhãn gì" và "xử lý bao thế nào". Mọi khâu vận chuyển đã có hệ thống lo.
text
Map(d_i):
với mỗi lần từ w xuất hiện trong d_i:
phát(w, 1)
Reduce(w, L_w):
c = 0
với mỗi giá trị v trong L_w:
c = c + v
phát(w, c)- Khóa = từ. Giá trị
= một lần xuất hiện. - Một từ xuất hiện
lần trong cùng văn bản → Map phát cặp , không gộp trước. - Hệ thống gom mọi giá trị của cùng từ
(từ mọi tác vụ Map) vào danh sách . - Từ không xuất hiện thì không có nhóm, Reduce không được gọi cho từ đó.
2.3. Dry-run 1: Đếm từ trên hai văn bản
| Pha | Đầu vào | Đầu ra |
|---|---|---|
| Map trên | "mèo chó mèo" | (mèo,1), (chó,1), (mèo,1) |
| Map trên | "chó chim" | (chó,1), (chim,1) |
| Nhóm theo khóa | 5 cặp trung gian | mèo: [1,1] · chó: [1,1] · chim: [1] |
| Reduce mèo | (mèo, [1,1]) | (mèo, 2) |
| Reduce chó | (chó, [1,1]) | (chó, 2) |
| Reduce chim | (chim, [1]) | (chim, 1) |
| Kiểm tra | 5 lần xuất hiện đầu vào |
Phân biệt: "Mèo" xuất hiện 2 lần nhưng chỉ trong 1 văn bản. "chó" xuất hiện 2 lần trong 2 văn bản.
2.4. Hình thức hóa
Hàm Map biến một cặp đầu vào thành một dãy cặp trung gian:
Hàm Reduce nhận một khóa và danh sách giá trị, trả một dãy cặp đầu ra:
Nhóm theo khóa. Gọi
Hàm phân phối. Với
| Ký hiệu | Ý nghĩa |
|---|---|
| Miền khóa, giá trị đầu vào (ví dụ: Mã văn bản, nội dung) | |
| Miền khóa, giá trị trung gian (ví dụ: Từ, số 1) | |
| Miền khóa, giá trị đầu ra, có thể khác | |
| Các dãy hữu hạn phần tử của | |
| Dãy toàn bộ cặp trung gian (mô tả logic, không bắt buộc lưu một chỗ) | |
| Danh sách giá trị đi cùng khóa | |
| Số tác vụ Map, số tác vụ Reduce | |
| Hàm phân phối khóa → tác vụ Reduce |
Dãy, không phải tập
Ký hiệu
Các quy tắc cần giữ:
- Cùng khóa → cùng tác vụ Reduce. Khác khóa có thể cùng tác vụ, nhưng vẫn là hai nhóm riêng, Reduce được gọi riêng cho từng khóa.
- Reduce không được phụ thuộc thứ tự giá trị trong
, vì hệ thống không cam kết thứ tự. - Phân biệt hàm với tác vụ: Một tác vụ Map gọi hàm Map nhiều lần (mỗi văn bản một lần). Một tác vụ Reduce thực hiện nhiều "reducer" (mỗi khóa một lần).
2.5. Các pha của một công việc
Bốn pha này là pha logic: Trong cài đặt thật, việc chuyển dữ liệu có thể bắt đầu khi một số Map khác còn chạy. Reduce cho mỗi khóa chỉ chốt kết quả khi đã nhận đủ dữ liệu của khóa đó.
Giả mã hình thức của một công việc (theo slide):
text
khởi tạo O_0, ..., O_{r−1} là các dãy rỗng
chia X thành B_1, ..., B_m theo vị trí bản ghi
song song với j = 1, ..., m:
I_j ← gom_giữ_lặp(Map(k,v) với mỗi (k,v) trong B_j)
I ← gom_giữ_lặp(I_1, ..., I_m)
song song với t = 0, ..., r−1:
với mỗi khóa phân biệt k xuất hiện trong I và p(k) = t:
L_k ← danh sách các v đi cùng k trong I
ghi mọi cặp Reduce(k, L_k) vào O_t
trả về Y = gom_giữ_lặp(O_0, ..., O_{r−1})Trường hợp biên: Tập
2.6. Hàm Combine: Gộp cục bộ trước khi gửi
Ẩn dụ: Đóng gói trước khi gửi bưu điện
Thay vì gửi 100 phong bì, mỗi phong bì ghi "mèo: 1", nhân viên gộp thành một phong bì ghi "mèo: 100". Bưu cục cuối vẫn cộng ra đúng tổng, nhưng khối lượng vận chuyển trên đường truyền giảm đi đáng kể.
Combine chạy ở phía Map, gộp các giá trị cùng khóa trong dữ liệu cục bộ của một tác vụ Map, trước khi truyền qua mạng.
Điều kiện đủ để Combine giữ nguyên kết quả (với các phép gộp trong bài):
- Phép gộp đóng trên kiểu giá trị trung gian (đầu vào và đầu ra Combine cùng kiểu).
- Phép gộp kết hợp và giao hoán.
- Giữ nguyên khóa.
- Tương thích với Reduce cuối: Thay một nhóm bằng trạng thái đã gộp không làm đổi kết quả Reduce.
Khi đó kết quả không đổi dù hệ thống bỏ qua Combine, gộp một phần hay gộp nhiều lần. Combine là tối ưu tùy chọn, không phải một pha bắt buộc.
Dry-run 2: Đếm từ có Combine (giao
| Tác vụ | Map phát | Sau Combine | Số cặp gửi đi |
|---|---|---|---|
| Map 1 ( | (mèo,1), (chó,1), (mèo,1) | (mèo,2), (chó,1) | 2 |
| Map 2 ( | (chó,1), (chim,1) | (chó,1), (chim,1) | 2 |
| Tổng | 5 cặp | 4 cặp | |
| Reduce | — | chó nhận [1, 1] → (chó, 2) | mèo: |
Hai cặp "chó" vẫn phải gặp nhau tại Reduce, vì chúng nằm ở hai tác vụ Map khác nhau và chưa được cộng chung.
Trung bình KHÔNG dùng được làm Combine trực tiếp
Trung bình không có khi các nhóm có kích thước khác nhau. Ví dụ dãy
3. Các ví dụ Map-Reduce
Ba ví dụ cho thấy ba kiểu "giá trị" khác nhau: Tích phần tử, dấu hiệu hiện diện, và cặp trạng thái.
3.1. Nhân ma trận – véc tơ
Đặc tả.
Dữ liệu logic: Gồm
Ẩn dụ: Tính lương theo phòng ban
Mỗi bản ghi chấm công
Ứng dụng: Điểm tương đồng trang web – truy vấn. Hàng của
Các hàm:
text
Map(B): # B là một khối ma trận, v đã có trong bộ nhớ
với mỗi bản ghi (i, j, a_ij) trong B:
yield(i, a_ij * v[j])
Combine(i, L): yield(i, sum(L))
Reduce(i, L): yield(i, sum(L))Khóa là hàng
Dry-run 3:
| Khối | Các tích được tính | Map yield | Combine yield |
|---|---|---|---|
| (1,1), (1,4), (2,0), (2,2) | (1,5), (2,2) | ||
| (1,0), (1,4), (2,6), (2,0) | (1,4), (2,6) | ||
| (3,2), (3,0), (4,1), (4,2) | (3,2), (4,3) | ||
| (3,3), (3,4), (4,0), (4,8) | (3,7), (4,8) |
| Khóa (hàng) | Reduce | Kiểm tra trực tiếp | |
|---|---|---|---|
| 1 | [5, 4] | (1, 9) | |
| 2 | [2, 6] | (2, 8) | |
| 3 | [2, 7] | (3, 9) | |
| 4 | [3, 8] | (4, 11) |
Kết quả:
Tính đúng. (1) Mỗi tọa độ thuộc đúng một khối logic, Map yield mỗi tích đúng một lần. (2) Khóa
Tại sao khóa là hàng
3.2. Đếm số từ phân biệt (MMDS bài 2.3.1(d), chuyển sang từ)
Đặc tả. Gọi
Ứng dụng:
Ẩn dụ: Điểm danh lớp học
Để đếm số học sinh khác nhau đã đến thư viện trong tuần, bạn không cộng số lượt mỗi ngày (một bạn đến 5 ngày sẽ bị đếm 5 lần). Bước 1: Lập danh sách tên, mỗi tên ghi một lần. Bước 2: Đếm số dòng của danh sách.
Vì sao khó? Cộng số từ phân biệt của từng văn bản sẽ đếm trùng: Văn bản
Phương án hai công việc:
| Hàm | Công việc 1: Loại trùng | Công việc 2: Đếm |
|---|---|---|
| Map | Mỗi lần từ | Nhận |
| Combine | Nhận | Nhận |
| Reduce | Nhận | Nhận |
Dry-run 4:
| Bước | Dữ liệu |
|---|---|
| Job 1 – Map | |
| Job 1 – Combine | |
| Job 1 – Reduce | mèo [1] → (mèo,1) · chó [1,1] → (chó,1) · chim [1] → (chim,1) |
| Job 2 – Map | (g,1), (g,1), (g,1) |
| Job 2 – Reduce | g [1,1,1] → (g, 3) |
Kết luận: Có
Tính đúng. Đầu ra Job 1 tương ứng một–một với các từ phân biệt. Cụ thể, mỗi từ có mặt sinh ít nhất một cặp Map, rồi Reduce thay cả nhóm bằng đúng một cặp. Từ vắng mặt không sinh cặp nào. Vì vậy, Job 2 đếm số cặp đó sẽ cho đúng
3.3. Trung bình cộng (MMDS bài 2.3.1(b))
Đặc tả. Dãy
Hai phần tử bằng nhau vẫn là hai đóng góp. Trung bình của dãy rỗng không xác định.
Ứng dụng: Tính số từ trung bình mỗi trang. Với "mèo chó mèo" (3 từ) và "chó chim" (2 từ), ta có
Ẩn dụ: Điểm trung bình toàn khối
Lớp A có 40 học sinh, trung bình 8. Lớp B có 10 học sinh, trung bình 6. Trung bình toàn khối không phải
Các hàm (với
| Hàm | Đầu vào → cặp phát ra | Kiểu giá trị |
|---|---|---|
| Map | cặp (tổng, số lượng) | |
| Combine | cặp (tổng, số lượng) | |
| Reduce | một số |
Dry-run 5: Map 1 nhận
| Bước | Dữ liệu | Trạng thái |
|---|---|---|
| Map 1 | phát | — |
| Combine 1 | cộng từng thành phần | |
| Map 2 | phát | — |
| Combine 2 | chỉ một cặp | |
| Reduce | phát |
Tính đúng (quy nạp). Bất biến: Mỗi trạng thái
4. Chi phí và lợi ích của song song hóa
Ba đại lượng chính, khác đơn vị, không cộng với nhau:
| Ký hiệu | Đo cái gì | Đơn vị |
|---|---|---|
| Tổng công việc (số phép toán) | phép toán | |
| Thời gian hoàn thành trên | giây | |
| Lượng dữ liệu qua đường truyền đang xét | byte | |
| Tổng kích thước đầu vào mọi tác vụ (quy ước MMDS) | byte, MB |
Trong phần này
4.1. Tổng công việc không giảm khi chia máy
Cộng
Với
4.2. Thời gian của một pha = máy chậm nhất
Ẩn dụ: Đoàn leo núi
Cả đoàn chỉ "đến đỉnh" khi người cuối cùng lên tới nơi. Thời gian của pha là thời gian của máy về đích muộn nhất, không phải tổng thời gian mọi máy.
Gọi
Ví dụ slide: Máy 1 chạy A, B (mỗi tác vụ 2 s), máy 2 chạy C, D (mỗi tác vụ 3 s), máy 3 chạy E (3 s).
4.3. Thời gian cộng nhiều số trên nhiều máy
Với
Dry-run 6: Chọn số máy cho
| Map: | Reduce: | Tổng việc | ||
|---|---|---|---|---|
| 1 | 15 | |||
| 2 | 15 | |||
| 4 | 15 | |||
| 8 | 15 | |||
| 16 | 15 |
Thêm máy giảm phần Map nhưng tăng phần gộp tuần tự. Từ công thức,
4.4. Chi phí đầu vào và chi phí khởi tạo tác vụ (MMDS 2.5.1)
Quy ước MMDS: chi phí một tác vụ là kích thước đầu vào của nó. Chi phí thuật toán là tổng trên mọi tác vụ.
| Ký hiệu | Ý nghĩa |
|---|---|
| Tổng kích thước dữ liệu gốc các tác vụ Map đọc (tính cả đọc cục bộ) | |
| Tổng kích thước dữ liệu trung gian các tác vụ Reduce nhận |
Ví dụ:
Quy ước khác
Một số slide (Stanford CS246) đếm tổng đọc/ghi thành
4.5. Thời gian truyền: Mô hình độ trễ – băng thông
| Ký hiệu | Ý nghĩa |
|---|---|
| Độ trễ khởi đầu (giây), trước khi dữ liệu bắt đầu tới | |
| Lượng dữ liệu cần truyền (MB) | |
| Băng thông (MB/s), quy ước 1 MB = |
Ví dụ:
Băng thông dùng chung. Nếu 4 máy mỗi máy gửi 100 MB qua cùng một đường nối 100 MB/s sang rack khác thì
Đây là cận dưới: 100 MB/s là băng thông của cả đường nối, không phải của mỗi máy. Đưa tính toán đến gần dữ liệu giúp giảm số byte phải đi qua đường nối này.
4.6. Combine giảm lượng truyền nhưng tăng việc ở Map
Giả thiết mỗi cặp 16 byte, mọi cặp qua đường truyền đúng một lần:
| Phương án | Số cặp truyền | |
|---|---|---|
| Không Combine | 5 | |
| Có Combine | 4 |
Giảm 20% lượng truyền, nhưng Map phải cộng thêm. Muốn biết Combine có giúp nhanh hơn hay không, phải tính thời gian toàn công việc.
4.7. Thời gian toàn công việc và mức tăng tốc
Với các pha nối tiếp, không chồng lấp, không lỗi:
Ví dụ slide (
4.8. Dry-run 7: Câu hỏi kiểm tra của slide
Dữ kiện:
| Bước | Tính | Kết quả |
|---|---|---|
| 5 s | ||
| 3 s | ||
| 11 s | ||
| 2 | ||
| Có Combine: | 6 s | |
| Có Combine: | 1 s | |
| 10 s (tiết kiệm 1 s) | ||
| 2,2 |
Lợi ích ròng của Combine: Bớt 2 s truyền, thêm 1 s Map. Sai lầm hay gặp: Cộng tải ba máy (
5. Hệ thống thực thi Map-Reduce (đọc thêm)
Phần này giải thích cơ chế tạo ra các chi phí ở mục 4. Slide đánh dấu là đọc thêm, không tiên quyết.
| Cơ chế | Điểm cần nhớ |
|---|---|
| InputSplit | Đơn vị chia việc cho Map, khối HDFS là đơn vị lưu trữ, hai thứ có thể khác nhau |
| Cục bộ hóa | Ưu tiên máy có bản sao → cùng rack → rack khác, là ưu tiên, không bảo đảm |
| Phân phối | Mỗi Map chia đầu ra thành |
| Shuffle | Mỗi Reduce lấy phần của mình trực tiếp từ các máy Map, không qua Master |
| Sắp và nhóm | Hệ thống sắp, ghép, Reduce đọc lần lượt từng nhóm, không cần giữ cả nhóm trong RAM |
| Master | Theo dõi trạng thái chờ / đang chạy / hoàn tất, mất liên lạc quá hạn coi là lỗi |
Phục hồi lỗi phụ thuộc nơi lưu dữ liệu:
| Dữ liệu | Nơi lưu | Khi mất máy lưu |
|---|---|---|
| Đầu vào | HDFS có bản sao | Đọc bản sao khác |
| Đầu ra Map trung gian | Đĩa cục bộ | Chạy lại Map để tạo lại (kể cả Map đã "xong") |
| Đầu ra Reduce đã hoàn tất | HDFS | Giữ nguyên, không cần chạy lại |
- Máy chạy Map hỏng trước khi Reduce lấy xong dữ liệu → Map đó phải chạy lại trên máy khác, Master báo vị trí mới cho Reduce.
- Máy chạy Reduce hỏng giữa chừng → chỉ chạy lại Reduce đó, lấy lại các phần trung gian. Map không phải chạy lại nếu đầu ra của chúng còn.
- Master hỏng (trong mô hình MMDS) → cả công việc phải khởi động lại.
- Một tác vụ có thể có nhiều lần thực thi (chạy lại sau lỗi, hoặc bản dự phòng khi chạy quá chậm). Hệ thống chỉ chấp nhận một kết quả cho mỗi tác vụ logic, không cộng hai bản.
6. Thực hành Hadoop với Docker Compose
Cụm 6 container Docker trên một máy: NameNode, 2 DataNode (HDFS, replication 2), ResourceManager, 2 NodeManager (YARN). Container Docker là tiến trình cô lập trên host. Container YARN là đơn vị tài nguyên NodeManager cấp cho ApplicationMaster, Map hoặc Reduce.
Khởi động và kiểm tra:
bash
cd hadoop-compose
docker compose pull
docker compose up -d
docker compose ps
docker compose exec namenode hdfs dfsadmin -report # cần 2 DataNode "Live"
docker compose exec namenode yarn node -list # cần 2 NodeManager "RUNNING"Web UI: HDFS tại http://127.0.0.1:19870, YARN tại http://127.0.0.1:18088.
mapper.py (Hadoop Streaming đưa từng dòng qua stdin):
python
import sys
# Mỗi từ phân cách bởi khoảng trắng đóng góp một lần xuất hiện.
for line in sys.stdin:
for word in line.split():
print(f"{word}\t1")reducer.py (Hadoop đưa các dòng cùng khóa liền nhau. Dùng được làm cả combiner):
python
import sys
from itertools import groupby
# Hadoop đưa các dòng cùng khóa liền nhau; đọc nhóm lần lượt.
pairs = (line.rstrip("\n").split("\t", 1) for line in sys.stdin)
for word, group in groupby(pairs, key=lambda pair: pair[0]):
total = sum(int(value) for _, value in group)
print(f"{word}\t{total}")Nộp công việc:
bash
docker compose exec -T namenode bash /work/run-job.shScript dùng các tham số Streaming: -files /work/mapper.py,/work/reducer.py, -mapper 'python3 mapper.py', -combiner 'python3 reducer.py', -reducer 'python3 reducer.py', 2 Reduce, minsplit 128 MiB (mỗi tệp nhỏ thành một Map).
Kết quả và bộ đếm của lần chạy kiểm thử:
text
chim 1
chó 2
mèo 2| Bộ đếm | Giá trị | Ý nghĩa |
|---|---|---|
| Map input records | 2 | hai dòng văn bản |
| Map output records | 5 | 5 cặp (từ, 1) |
| Combine output records | 4 | hai cặp "mèo" của |
| Reduce output records | 3 | ba từ phân biệt |
Với 2 Reduce, output có hai tệp part-*. Lần chạy mẫu có một tệp chứa cả ba khóa và một tệp rỗng, nguyên nhân là hàm băm mặc định không bảo đảm cân bằng tải. Số lần Combine chạy không cố định giữa các lần nộp job. Bộ đếm đo số cặp, không đo thời gian.
Câu hỏi kiểm tra của slide: (1) Xóa Combiner, kết quả không đổi vì phép cộng kết hợp và giao hoán. (2) Replication 2 không làm tổng thành 10: Bản sao là lưu trữ, mỗi từ vẫn chỉ được một Map xử lý một lần. (3) Bằng chứng job đúng: Trạng thái SUCCEEDED trên YARN, bộ đếm hợp lệ, và part-* cho đúng chim 1, chó 2, mèo 2, trong khi docker compose ps chỉ cho biết container đang chạy.
7. Bài tập MMDS 2.3.1 có lời giải
7.1. Số nguyên lớn nhất (Bài 2.3.1a)
| Hàm | Đặc tả |
|---|---|
| Map | Trên khối |
| Combine | |
| Reduce |
Tính đúng: Bất biến khi quét, biến đang giữ bằng max của tiền tố đã đọc. Mỗi phần tử thuộc đúng một khối, nên max toàn tệp bằng max của các max khối. Phép toán
7.2. Trung bình cộng (Bài 2.3.1b)
Map trên
7.3. Số giá trị phân biệt (Bài 2.3.1d)
Hai vòng như mục 3.2: Job 1 phát
8. Code minh họa: Mô phỏng Map-Reduce bằng Python
Chương trình dưới đây là một máy Map-Reduce thu nhỏ chạy trong một tiến trình: Chia đầu vào, Map, Combine, phân phối theo băm, nhóm, Reduce, đúng như giả mã hình thức ở mục 2.5. Chương trình chạy lại toàn bộ năm dry-run của bài giảng và đối chiếu kiểm tra bằng NumPy.
python
"""
Bài 02 - Mô phỏng mô hình Map-Reduce
Chạy: python bai02.py
"""
from collections import defaultdict
from fractions import Fraction
from zlib import crc32
import numpy as np
# ---------------------------------------------------------------
# 1. Máy Map-Reduce thu nhỏ (một tiến trình, mô phỏng m Map, r Reduce)
# ---------------------------------------------------------------
def phan_phoi(khoa, r):
"""Hàm phân phối p(k) = h(k) mod r; crc32 cho kết quả ổn định giữa các lần chạy."""
return crc32(repr(khoa).encode("utf-8")) % r
def chay_job(cac_phan, ham_map, ham_reduce, ham_combine=None, r=2, ghi_vet=False):
"""
cac_phan : danh sách các phần đầu vào B_1..B_m (mỗi phần là danh sách bản ghi)
ham_map : bản ghi -> danh sách cặp (k, v)
ham_combine : (k, L) -> danh sách cặp (k, v'), cùng kiểu giá trị trung gian
ham_reduce : (k, L) -> danh sách cặp đầu ra
"""
# Pha 2: mỗi tác vụ Map xử lý một phần, có thể Combine cục bộ
bo_dem = {"map_out": 0, "combine_out": 0}
vung_reduce = [defaultdict(list) for _ in range(r)] # r "ổ đĩa" của Reduce
for j, phan in enumerate(cac_phan, start=1):
cap = [c for ban_ghi in phan for c in ham_map(ban_ghi)] # giữ lặp: dãy, không phải tập
bo_dem["map_out"] += len(cap)
if ham_combine is not None:
nhom_cuc_bo = defaultdict(list)
for k, v in cap:
nhom_cuc_bo[k].append(v)
cap = [c for k, L in nhom_cuc_bo.items() for c in ham_combine(k, L)]
bo_dem["combine_out"] += len(cap)
if ghi_vet:
print(f" Map {j}: gửi {cap}")
# Pha 3: phân phối theo p(k) và nhóm theo khóa
for k, v in cap:
vung_reduce[phan_phoi(k, r)][k].append(v)
# Pha 4: mỗi tác vụ Reduce gọi hàm Reduce cho từng khóa (nhóm riêng)
dau_ra = []
for t in range(r):
for k in sorted(vung_reduce[t], key=repr):
dau_ra.extend(ham_reduce(k, vung_reduce[t][k]))
if ghi_vet:
print(f" Bộ đếm: {bo_dem}")
return dau_ra
# ---------------------------------------------------------------
# 2. Đếm từ
# ---------------------------------------------------------------
def map_dem_tu(van_ban):
return [(w, 1) for w in van_ban.split()]
def cong_theo_khoa(k, L):
return [(k, sum(L))]
# ---------------------------------------------------------------
# 3. Nhân ma trận - véc tơ, Map nhận cả một khối
# ---------------------------------------------------------------
def tao_khoi(A, kich_thuoc=2):
"""Chia A thành các khối vuông; mỗi khối là danh sách bản ghi (i, j, a_ij), chỉ số từ 1."""
p, q = A.shape
khoi = []
for r0 in range(0, p, kich_thuoc):
for c0 in range(0, q, kich_thuoc):
khoi.append([[(i + 1, j + 1, int(A[i, j]))
for i in range(r0, min(r0 + kich_thuoc, p))
for j in range(c0, min(c0 + kich_thuoc, q))]])
return khoi
def lam_map_ma_tran(v):
def ham_map(B): # B là một khối; v có sẵn trong bộ nhớ
return [(i, a * v[j - 1]) for (i, j, a) in B]
return ham_map
# ---------------------------------------------------------------
# 4. Đếm từ phân biệt: hai công việc
# ---------------------------------------------------------------
def mot_cap_moi_khoa(k, L):
return [(k, 1)] # giữ sự hiện diện, không giữ số lần
def map_sang_khoa_chung(cap):
return [("g", 1)]
# ---------------------------------------------------------------
# 5. Trung bình cộng với trạng thái (tổng, số lượng)
# ---------------------------------------------------------------
def map_trung_binh(a):
return [("g", (Fraction(a), 1))]
def combine_trung_binh(k, L):
return [(k, (sum(s for s, _ in L), sum(c for _, c in L)))] # chưa chia!
def reduce_trung_binh(k, L):
S = sum(s for s, _ in L)
C = sum(c for _, c in L)
return [(k, S / C)]
# ---------------------------------------------------------------
# 6. Mô hình chi phí
# ---------------------------------------------------------------
def thoi_gian_cong(n, P, tau=1):
"""T_P = (n/P - 1) tau + (P - 1) tau, một Reduce gộp tuần tự."""
return (n // P - 1) * tau + (P - 1) * tau
if __name__ == "__main__":
kho = [["mèo chó mèo"], ["chó chim"]] # Map 1 nhận d1, Map 2 nhận d2
print("Dry-run 1: đếm từ không Combine")
print(" ", sorted(chay_job(kho, map_dem_tu, cong_theo_khoa, ghi_vet=True)))
print("Dry-run 2: đếm từ có Combine")
print(" ", sorted(chay_job(kho, map_dem_tu, cong_theo_khoa, cong_theo_khoa, ghi_vet=True)))
print("Dry-run 3: nhân ma trận - véc tơ")
A = np.array([[1, 2, 0, 1], [0, 1, 2, 0], [2, 0, 1, 1], [1, 1, 0, 2]])
v = np.array([1, 2, 3, 4])
y = dict(chay_job(tao_khoi(A), lam_map_ma_tran(v), cong_theo_khoa, cong_theo_khoa, r=2))
y_mr = np.array([y[i] for i in range(1, A.shape[0] + 1)])
print(" Map-Reduce:", y_mr, "| NumPy A @ v:", A @ v, "| khớp:", np.array_equal(y_mr, A @ v))
print("Dry-run 4: đếm từ phân biệt")
job1 = chay_job(kho, map_dem_tu, mot_cap_moi_khoa, mot_cap_moi_khoa)
job2 = chay_job([job1], map_sang_khoa_chung, cong_theo_khoa, cong_theo_khoa, r=1)
D = job2[0][1] if job2 else 0 # điều phối trả 0 khi không có từ
print(" Job 1:", sorted(job1), "| D =", D)
print("Dry-run 5: trung bình cộng")
kq = chay_job([[2, 4], [9]], map_trung_binh, reduce_trung_binh, combine_trung_binh, r=1)
print(" mu =", kq[0][1])
print("Dry-run 6: thời gian cộng 16 số")
for P in (1, 2, 4, 8, 16):
print(f" P = {P:2d}: T_P = {thoi_gian_cong(16, P)} tau")
print("Dry-run 7: câu hỏi kiểm tra")
T_P = 1 + max(2, 3, 5) + 120 / 40 + 2
T_Pc = 1 + (max(2, 3, 5) + 1) + 40 / 40 + 2
print(f" T_P = {T_P} s, S_P = {22 / T_P}; có Combine: T'_P = {T_Pc} s, S'_P = {22 / T_Pc}")Kết quả mong đợi:
text
Dry-run 1: đếm từ không Combine
Map 1: gửi [('mèo', 1), ('chó', 1), ('mèo', 1)]
Map 2: gửi [('chó', 1), ('chim', 1)]
Bộ đếm: {'map_out': 5, 'combine_out': 5}
[('chim', 1), ('chó', 2), ('mèo', 2)]
Dry-run 2: đếm từ có Combine
Map 1: gửi [('mèo', 2), ('chó', 1)]
Map 2: gửi [('chó', 1), ('chim', 1)]
Bộ đếm: {'map_out': 5, 'combine_out': 4}
[('chim', 1), ('chó', 2), ('mèo', 2)]
Dry-run 3: nhân ma trận - véc tơ
Map-Reduce: [ 9 8 9 11] | NumPy A @ v: [ 9 8 9 11] | khớp: True
Dry-run 4: đếm từ phân biệt
Job 1: [('chim', 1), ('chó', 1), ('mèo', 1)] | D = 3
Dry-run 5: trung bình cộng
mu = 5
Dry-run 6: thời gian cộng 16 số
P = 1: T_P = 15 tau
P = 2: T_P = 8 tau
P = 4: T_P = 6 tau
P = 8: T_P = 8 tau
P = 16: T_P = 15 tau
Dry-run 7: câu hỏi kiểm tra
T_P = 11.0 s, S_P = 2.0; có Combine: T'_P = 10.0 s, S'_P = 2.29. Tổng kết
Bẫy thi & Ngộ nhận kinh điển
- Đầu ra Map là tập hợp: sai, là dãy giữ lặp. Dùng tập sẽ mất các cặp (mèo,1) trùng và đếm sai.
- Đếm "tần suất" bằng số văn bản chứa từ: sai, phải đếm số lần xuất hiện.
- Khóa –véc tơ là cột
: sai, phải là hàng vì là tổng theo hàng. - Cộng số từ phân biệt của từng văn bản: đếm trùng (
thay vì ). Phải loại trùng toàn cục trước. - Dùng Reduce của bài trung bình làm Combine: Reduce phát
(một số) nên không gộp tiếp được. Combine phải giữ cặp . - "Trung bình của trung bình": chỉ đúng khi các nhóm có cùng kích thước.
- Combine là bắt buộc / luôn chạy đúng một lần: sai. Hệ thống có thể không chạy hoặc chạy nhiều lần. Kết quả phải không đổi trong mọi trường hợp.
- Combine luôn làm chạy nhanh hơn: chưa chắc. Nó giảm
nhưng tăng việc ở Map. Khóa không lặp thì không giảm gì. = tổng tải các máy: sai, là max (máy chậm nhất). - Tăng số máy luôn giảm thời gian: sai. Phần gộp tuần tự, truyền mạng, điều phối, tải lệch có thể làm
tăng (xem bảng ). - Tổng công việc giảm khi song song: sai,
không đổi. - Cộng đầu ra cuối vào
: theo MMDS 2.5.1, , không tính đầu ra cuối. - 100 MB/s là băng thông mỗi máy: khi đường nối dùng chung, 4 máy gửi tổng 400 MB cần ít nhất 4 s.
- Replication làm tăng số đếm: sai. Bản sao là lưu trữ. Mỗi khối logic xử lý đúng một lần.
- Reduce đã xong phải chạy lại khi Map hỏng: không. Map đã xong phải chạy lại nếu đầu ra cục bộ mất và còn Reduce chưa lấy. Reduce đã ghi lên HDFS được giữ.
- Trả 0 cho max hoặc trung bình của tệp rỗng: sai. Max không xác định, trung bình chia cho 0. Chỉ "số giá trị phân biệt" mới có
. - Reduce phụ thuộc thứ tự giá trị trong
: hệ thống không cam kết thứ tự.
Bảng công thức ghi nhớ nhanh
| Khái niệm | Công thức / Từ khóa |
|---|---|
| Hàm Map | |
| Hàm Reduce | |
| Nhóm theo khóa | |
| Hàm phân phối | |
| Đếm từ | |
| –véc tơ | |
| Phân biệt | |
| Trung bình | trạng thái |
| Điều kiện Combine | đóng, kết hợp, giao hoán, giữ khóa, tương thích Reduce |
| Tổng công việc | |
| Thời gian một pha | |
| Cộng | |
| Chi phí MMDS | |
| Thời gian truyền | |
| Toàn công việc | $$ |
| T_P = T_{\mathrm{coord}} + T_{\text{Map}} + T_{\mathrm{comm}} + T_{\text{Reduce}} |