Hiểu Sâu Về Kafka Partitions – Chìa Khóa Vàng Để Tối Ưu Tốc Độ Xử Lý Tin Nhắn Song Song
📌 Tóm tắt nhanh
- Partition là đơn vị lưu trữ vật lý, giúp Kafka ghi/đọc song song trên nhiều Broker.
- Message Key quyết định tin nhắn vào Partition nào; chỉ đảm bảo thứ tự trong cùng một Partition.
- Số lượng Partition tối thiểu = max(Thông lượng ghi / p, Thông lượng đọc / c) – công thức từ Confluent.
- Không thể giảm Partition; hãy over‑partition từ đầu.
- Tránh Data Skew bằng cách dùng Key có độ phân tán cao (user_id, order_id…).
Bạn đã bao giờ tự hỏi: Tại sao Kafka có thể xử lý hàng triệu tin nhắn mỗi giây trong khi RabbitMQ hay các hàng đợi truyền thống lại bị nghẽn ở ngưỡng vài nghìn TPS?
Câu trả lời nằm ở một khái niệm tưởng chừng đơn giản nhưng là linh hồn của toàn bộ kiến trúc Kafka: Partition.
Nếu bạn vẫn đang nghĩ về một Topic Kafka như một hàng đợi (Queue) đơn lẻ – hãy dừng lại ngay. Đó là tư duy sai lầm nguy hiểm nhất khi thiết kế hệ thống phân tán với Kafka. Partition mới chính là đơn vị lưu trữ vật lý cơ sở, là “cỗ máy” duy nhất giúp Kafka có khả năng scale up hiệu năng đọc/ghi song song (concurrency) lên mức hàng triệu tin nhắn mỗi giây.
Bài viết này sẽ đưa bạn đi từ bản chất vật lý của Partition, qua cơ chế phân tán dữ liệu, đến công thức định lượng thực tế để tính toán số lượng Partition cho dự án của bạn – những kiến thức mà hầu hết tài liệu tiếng Việt hiện nay đang thiếu hụt.
Bản chất vật lý của Kafka Topic: Tập hợp các file Commit Log append-only
Để hiểu Partition, trước hết bạn phải hiểu Kafka lưu trữ dữ liệu như thế nào.
Khác với RabbitMQ – nơi tin nhắn thường được lưu trong RAM và xử lý theo cơ chế hàng đợi truyền thống – Kafka lưu trữ dữ liệu trực tiếp trên ổ đĩa cứng (disk) của các Broker. Nhưng đừng lo, Kafka không hề chậm vì điều này. Nó tận dụng cơ chế append-only log (ghi tuần tự) để biến I/O đĩa thành một trong những điểm mạnh nhất của mình.
Topic không phải là một hàng đợi – nó là một thư mục chứa nhiều partition
Về mặt vật lý, một Topic trong Kafka không phải là một thực thể duy nhất. Nó được phân rã thành nhiều Partition, và mỗi Partition tương ứng với một thư mục (folder) riêng biệt trên ổ đĩa của Broker.
Cấu trúc thư mục của một Partition trông như sau:
/var/lib/kafka/data/
└── my-topic-0/ ← Partition 0
│ ├── 00000000000000000000.log (file dữ liệu)
│ └── 00000000000000000000.index (file index)
├── my-topic-1/ ← Partition 1
│ ├── 00000000000000000000.log
│ └── 00000000000000000000.index
└── my-topic-2/ ← Partition 2
├── 00000000000000000000.log
└── 00000000000000000000.index
Mỗi Partition là một commit log append-only: tin nhắn mới luôn được ghi vào cuối file (append), không bao giờ sửa hay xóa tin nhắn cũ (trừ khi hết hạn theo retention policy). Chính cơ chế append-only này cho phép Kafka đạt tốc độ ghi đĩa gần bằng tốc độ mạng, vì nó không phải thực hiện các thao tác tìm kiếm ngẫu nhiên (random seek) như database.
Điều quan trọng cần nhớ: Các Partition của cùng một Topic có thể nằm trên các Broker khác nhau trong cụm Cluster. Điều này có nghĩa là dữ liệu của một Topic được phân tán vật lý trên nhiều máy chủ khác nhau – đó chính là nền tảng cho khả năng mở rộng của Kafka.
Tại sao Partition lại là chìa khóa giúp Kafka đạt thông lượng hàng triệu TPS?

Câu trả lời nằm ở cơ chế phân tán gánh nặng I/O và mô hình Leader – Follower.
1. Ghi song song trên nhiều Broker
Khi Producer gửi tin nhắn đến Kafka, nó không ghi vào một “hàng đợi” trung tâm. Thay vào đó, mỗi tin nhắn được ghi vào một Partition cụ thể. Và vì các Partition có thể nằm trên các Broker khác nhau, các thao tác ghi diễn ra hoàn toàn song song trên nhiều máy chủ.
Giả sử bạn có một Topic với 10 Partition được phân bố trên 5 Broker (mỗi Broker chứa 2 Partition). Khi Producer gửi 10 tin nhắn cùng lúc, Kafka có thể ghi chúng vào 10 Partition khác nhau trên 5 máy chủ khác nhau – thay vì phải ghi tuần tự vào một hàng đợi duy nhất.
2. Mô hình Leader – Follower và Replication Factor
Mỗi Partition trong Kafka có một Leader và nhiều Follower (bản sao).
| Thành phần | Vai trò |
|---|---|
| Leader | Chịu trách nhiệm xử lý toàn bộ các yêu cầu đọc và ghi cho Partition đó. Mọi Producer và Consumer đều giao tiếp trực tiếp với Leader. |
| Follower | Sao chép dữ liệu từ Leader một cách thụ động. Follower không phục vụ request nào cho đến khi Leader bị lỗi và một Follower được bầu làm Leader mới. |
Với Replication Factor = 3, mỗi Partition sẽ có 1 Leader và 2 Follower, phân bố trên 3 Broker khác nhau. Điều này vừa đảm bảo tính sẵn sàng cao (High Availability), vừa phân tán gánh nặng lưu trữ.
3. Đọc song song nhờ Consumer Groups
Về phía Consumer, một Partition chỉ có thể được gán cho tối đa một Consumer Instance trong cùng một Consumer Group. Điều này có nghĩa là số lượng Consumer song song trong một Group bị chặn cứng bởi số lượng Partition.
Consumer Group có 3 Consumer
│
▼
┌─────────────────────────────────┐
│ Topic "orders" (3 Partitions) │
├─────────┬─────────┬─────────────┤
│ P-0 │ P-1 │ P-2 │
│ ▲ │ ▲ │ ▲ │
│ │ │ │ │ │ │
│ C-1 │ C-2 │ C-3 │
└─────────┴─────────┴─────────────┘
Nếu Topic chỉ có 3 Partition, bạn có thể chạy tối đa 3 Consumer song song. Thêm Consumer thứ 4 sẽ ngồi không (idle) vì không còn Partition nào để gán.
🔑 Bài học quan trọng: Nếu bạn muốn tăng tốc độ xử lý bằng cách thêm Consumer, bạn phải tăng số lượng Partition tương ứng. Ngược lại, tăng Consumer mà không tăng Partition là vô nghĩa.
➡️ Tìm hiểu thêm về cách tối ưu số lượng Consumer và Partition qua bài viết: Kafka Consumer Groups: Bí quyết Scaling hệ thống xử lý tin nhắn.
Chiến lược gán Khóa (Message Key) và Nghịch lý giữa Tải song song vs Thứ tự tin nhắn
Đây là phần quan trọng nhất và cũng là nơi nhiều developer mắc sai lầm nhất.
Kafka chỉ đảm bảo thứ tự tin nhắn trong phạm vi một Partition. Nếu bạn cần đảm bảo thứ tự toàn cục cho tất cả tin nhắn (ví dụ: tất cả giao dịch của một hệ thống tài chính), bạn chỉ có thể dùng một Partition duy nhất – nhưng điều đó đồng nghĩa với việc hy sinh toàn bộ khả năng xử lý song song.
Giải pháp của Kafka là: cho phép bạn chọn Partition cho từng tin nhắn thông qua Message Key.
Khi Message Key bằng Null – Cơ chế lặp vòng Round-Robin hiện đại
Khi Producer gửi tin nhắn không có Key (key = null), Kafka sẽ tự động quyết định Partition nào nhận tin nhắn đó.
Cách thức hoạt động phụ thuộc vào phiên bản Kafka:
| Phiên bản Kafka | Cơ chế | Mô tả |
|---|---|---|
| < 2.4 | Round-Robin | Lần lượt gửi tin nhắn vào từng Partition theo vòng tròn |
| ≥ 2.4 | Sticky Partitioner | Gửi tất cả tin nhắn vào cùng một Partition cho đến khi batch đầy, sau đó chuyển sang Partition khác |
Sticky Partitioner (mặc định từ Kafka 2.4) cải thiện đáng kể hiệu năng cho các tin nhắn không có Key bằng cách:
- Tận dụng tối đa kích thước batch (nhiều tin nhắn hơn trên mỗi lần gửi)
- Giảm số lượng request mạng
- Nén dữ liệu hiệu quả hơn nhờ batch lớn
Khi có Message Key – Đảm bảo toàn vẹn thứ tự logic dòng sự kiện

Khi bạn gán Key cho tin nhắn, Kafka sẽ sử dụng thuật toán băm (hash) để xác định Partition:
partition = hash(key) % number_of_partitions
Thuật toán băm mặc định của Kafka là Murmur2 (phiên bản MurmurHash2). Đây là thuật toán băm không mã hóa (non-cryptographic) được tối ưu về tốc độ và phân phối đồng đều.
⚠️ Lưu ý về phiên bản: Một số tài liệu cũ đề cập đến MurmurHash3. Tuy nhiên, theo mã nguồn và tài liệu chính thức của Kafka, DefaultPartitioner sử dụng Murmur2 để băm key. Điều này đã được xác nhận trong mã nguồn của Kafka clients.
🔐 Với Kafka 3.x (KRaft): Từ phiên bản 3.x, cụm Kafka không còn phụ thuộc vào ZooKeeper mà sử dụng KRaft. Cơ chế băm partition và luồng xử lý tin nhắn hoàn toàn giữ nguyên, nên những hướng dẫn trong bài áp dụng được cho cả hai môi trường.
Ví dụ thực tế: Giả sử bạn có Topic order-events với 6 Partition. Bạn muốn tất cả sự kiện của cùng một order_id phải vào cùng một Partition để đảm bảo thứ tự xử lý.
order_id = "ORDER-1001" → hash("ORDER-1001") % 6 = 3 → Partition #3
order_id = "ORDER-1001" → hash("ORDER-1001") % 6 = 3 → Partition #3 (luôn giống nhau)
order_id = "ORDER-2002" → hash("ORDER-2002") % 6 = 0 → Partition #0
Nhưng có một cái bẫy: Nếu bạn thay đổi số lượng Partition sau khi đã có dữ liệu, công thức hash(key) % number_of_partitions sẽ thay đổi kết quả. Điều này có nghĩa là:
- Các tin nhắn cũ của
order_idđang nằm ở Partition #3 - Các tin nhắn mới của cùng
order_idcó thể rơi vào Partition khác (ví dụ #1) - Thứ tự tin nhắn bị phá vỡ
🚨 Nguyên tắc vàng: Nếu bạn cần bảo toàn thứ tự tin nhắn theo Key, hãy over‑partition ngay từ đầu – tức là tạo số lượng Partition dựa trên nhu cầu tăng trưởng trong tương lai (1-2 năm), thay vì nhu cầu hiện tại. Bạn có thể tăng số lượng Partition nhưng không thể giảm.
Trong thực tế, khi một tin nhắn lỗi (poison pill) bị loại bỏ khỏi luồng, việc xử lý không đúng cách có thể làm hỏng thứ tự. Tham khảo ngay bài viết Kafka Dead Letter Queue (DLQ): Chiến Lược Xử Lý Và Cô Lập Tin Nhắn Lỗi Trong Kiến Trúc Event‑Driven để bảo vệ toàn bộ dòng chảy dữ liệu của bạn.
Cẩm nang định lượng: Dự án của bạn thực tế cần bao nhiêu Partitions?

Đây là câu hỏi số một mà các System Architect thường đặt ra. Và câu trả lời không phải là “càng nhiều càng tốt”.
Công thức tính số lượng Partition tối thiểu
Theo tài liệu chính thức từ Confluent, công thức để xác định số lượng Partition tối thiểu là:
$$\text{Partitions} = \max\left(\frac{t}{p}, \frac{t}{c}\right)$$
Trong đó:
| Ký hiệu | Ý nghĩa | Cách xác định |
|---|---|---|
| t | Target Throughput (thông lượng mục tiêu) | Yêu cầu kinh doanh của hệ thống |
| p | Throughput ghi của một Partition | Đo thực tế với cấu hình Producer của bạn |
| c | Throughput đọc của một Partition | Đo thực tế với logic Consumer của bạn |
Ví dụ tính toán thực tế
Giả sử bạn đang xây dựng hệ thống xử lý đơn hàng thương mại điện tử với các thông số:
| Tham số | Giá trị | Giải thích |
|---|---|---|
| Target Throughput (t) | 100.000 msg/giây | Doanh nghiệp cần xử lý 100K tin nhắn/giây |
| Throughput ghi 1 Partition (p) | 10.000 msg/giây | Đo được từ Producer với batch.size=16KB, acks=1 |
| Throughput đọc 1 Partition (c) | 5.000 msg/giây | Phụ thuộc vào logic xử lý (gọi API, DB) |
Áp dụng công thức:
$$\text{Partitions} = \max\left(\frac{100.000}{10.000}, \frac{100.000}{5.000}\right) = \max(10, 20) = 20$$
Kết luận: Bạn cần tối thiểu 20 Partition cho Topic này.
💡 Lưu ý quan trọng: Confluent khuyến cáo nên over‑partition – tức là tạo số lượng Partition dựa trên nhu cầu tăng trưởng của 1-2 năm tới. Với ví dụ trên, nếu dự kiến tải tăng gấp đôi sau 1 năm, bạn nên tạo 40 Partition ngay từ đầu.
Giới hạn trên – Đừng lạm dụng quá nhiều Partition
Nhiều người nghĩ rằng càng nhiều Partition càng tốt. Sai lầm.
Việc tăng quá nhiều Partition gây ra các vấn đề nghiêm trọng:
| Vấn đề | Giải thích |
|---|---|
| File handle quá tải | Mỗi Partition tương ứng với ít nhất 2 file (log + index) trên đĩa. Hàng nghìn Partition đồng nghĩa với hàng nghìn file mở cùng lúc. |
| Thời gian phục hồi cụm (Leader Election) lâu | Khi một Broker gặp sự cố, Kafka phải bầu lại Leader cho tất cả Partition trên Broker đó. Càng nhiều Partition, thời gian phục hồi càng lâu. |
| Rebalance chậm | Khi thêm/bớt Consumer, Kafka phải thực hiện rebalance để phân phối lại Partition. Quá nhiều Partition làm chậm quá trình này. |
Giới hạn khuyến nghị:
- Tối đa 4.000 Partition trên mỗi Broker
- Tối đa 200.000 Partition trên toàn cụm Cluster
- Tổng số Topic dưới 7.000
💻 Code Examples
Ví dụ 1: Cấu hình khởi tạo Topic kèm chỉ định số lượng Partition bằng Node.js (kafkajs)
Mã nguồn dưới đây sử dụng Admin Client của thư viện kafkajs để tự động tạo Topic ecommerce-orders với 6 Partition và Replication Factor = 3.
const { Kafka } = require('kafkajs');
// Khởi tạo Kafka client
const kafka = new Kafka({
clientId: 'admin-client',
brokers: ['broker1:9092', 'broker2:9092', 'broker3:9092']
});
const admin = kafka.admin();
async function createTopic() {
await admin.connect();
try {
await admin.createTopics({
topics: [
{
topic: 'ecommerce-orders',
numPartitions: 6, // 👈 6 Partition
replicationFactor: 3, // 👈 3 bản sao (1 Leader + 2 Follower)
configEntries: [
{ name: 'retention.ms', value: '604800000' }, // 7 ngày
{ name: 'compression.type', value: 'snappy' }
]
}
]
});
console.log('✅ Topic ecommerce-orders đã được tạo thành công!');
} catch (error) {
console.error('❌ Lỗi tạo topic:', error);
} finally {
await admin.disconnect();
}
}
createTopic();
Giải thích:
numPartitions: 6– Topic này sẽ có 6 Partition độc lập, cho phép tối đa 6 Consumer trong cùng một Consumer Group xử lý song song.replicationFactor: 3– Mỗi Partition có 3 bản sao trên 3 Broker khác nhau, đảm bảo dữ liệu không bị mất nếu 1-2 Broker gặp sự cố.configEntries– Các cấu hình bổ sung: retention 7 ngày và nén dữ liệu bằng Snappy để tiết kiệm dung lượng.
Ví dụ 2: Viết Producer gán Message Key thông minh bảo toàn chuỗi sự kiện
Mã nguồn dưới đây gửi tin nhắn giao dịch với Key là customer_id để đảm bảo tất cả giao dịch của cùng một khách hàng luôn rơi vào cùng một Partition – từ đó bảo toàn thứ tự sự kiện.
const { Kafka, Partitioners } = require('kafkajs');
const kafka = new Kafka({
clientId: 'order-producer',
brokers: ['broker1:9092', 'broker2:9092', 'broker3:9092']
});
const producer = kafka.producer({
createPartitioner: Partitioners.DefaultPartitioner // Sử dụng partitioner mặc định
});
async function sendOrderEvents() {
await producer.connect();
// Danh sách các đơn hàng mẫu
const orders = [
{ customer_id: 'CUST_1001', order_id: 'ORD_001', amount: 150.00 },
{ customer_id: 'CUST_1001', order_id: 'ORD_002', amount: 89.50 }, // Cùng customer_id
{ customer_id: 'CUST_1002', order_id: 'ORD_003', amount: 200.00 },
{ customer_id: 'CUST_1001', order_id: 'ORD_004', amount: 45.00 }, // Cùng customer_id
{ customer_id: 'CUST_1003', order_id: 'ORD_005', amount: 320.00 }
];
for (const order of orders) {
// 👉 Key là customer_id - tất cả đơn hàng của cùng 1 customer sẽ vào chung 1 Partition
const key = order.customer_id;
await producer.send({
topic: 'ecommerce-orders',
messages: [
{
key: key, // 👈 Message Key
value: JSON.stringify(order), // Nội dung tin nhắn
headers: {
'source': 'web-checkout',
'version': '1.0'
}
}
]
});
console.log(`✅ Đã gửi đơn hàng ${order.order_id} với key=${key}`);
}
await producer.disconnect();
}
sendOrderEvents().catch(console.error);
Giải thích cơ chế phân bổ (minh họa):
| Customer ID | Kết quả hash(Key) % 6 |
Partition |
|---|---|---|
CUST_1001 |
hash("CUST_1001") % 6 → 0 |
Partition 0 |
CUST_1002 |
hash("CUST_1002") % 6 → 2 |
Partition 2 |
CUST_1003 |
hash("CUST_1003") % 6 → 4 |
Partition 4 |
(Các giá trị số 0, 2, 4 chỉ là ví dụ minh họa; kết quả thực tế phụ thuộc vào giá trị hash thực)
Tất cả đơn hàng của CUST_1001 (ORD_001, ORD_002, ORD_004) đều được gửi vào Partition 0. Consumer đọc Partition 0 sẽ nhận được các tin nhắn theo đúng thứ tự gửi, đảm bảo tính nhất quán cho luồng xử lý của khách hàng đó.
Trạng thái phân bổ dữ liệu sau khi chạy:
Topic: ecommerce-orders (6 partitions)
┌────────────┬──────────────────────────────────────────────┐
│ Partition │ Messages (theo thứ tự gửi) │
├────────────┼──────────────────────────────────────────────┤
│ P-0 │ ORD_001 → ORD_002 → ORD_004 (cùng CUST_1001)│
│ P-2 │ ORD_003 (CUST_1002) │
│ P-4 │ ORD_005 (CUST_1003) │
│ P-1, P-3, P-5 │ (trống) │
└────────────┴──────────────────────────────────────────────┘
⚠️ Lỗi thường gặp: Hiện tượng lệch dữ liệu (Data Skew)
Lỗi: Gặp hiện tượng lệch dữ liệu nghiêm trọng (Data Skew)
Một vài Partition bị đầy tràn ổ đĩa làm sập Broker, trong khi các Partition khác gần như rỗng tuếch.
Nguyên nhân
Chọn Message Key có độ phân tán quá thấp (low cardinality) hoặc bị trùng lặp tần suất quá cao.
Ví dụ điển hình của sai lầm:
// ❌ SAI: Key theo giới tính (chỉ có 2 giá trị: "MALE" và "FEMALE")
const key = user.gender; // "MALE" hoặc "FEMALE"
// hash("MALE") % 6 → luôn ra cùng 1 Partition
// hash("FEMALE") % 6 → luôn ra cùng 1 Partition khác
Với 6 Partition, chỉ có 2 Partition được sử dụng, 4 Partition còn lại hoàn toàn trống. Tất cả user nam đổ dồn vào đúng 1 Partition duy nhất.
Các ví dụ khác về Key có cardinality thấp:
country_code(VN, US, JP… ~ 200 giá trị)user_type(admin, member, guest… ~ 5 giá trị)device_type(mobile, web, tablet… ~ 3 giá trị)

Hướng khắc phục
Luôn luôn lựa chọn Message Key có tính phân tán cao (High Cardinality) như:
| Loại Key | Cardinality | Ví dụ |
|---|---|---|
user_id |
Hàng triệu → hàng tỷ | "USER_123456789" |
order_id |
Hàng triệu → hàng tỷ | "ORD_2024_001234" |
transaction_id |
Hàng triệu → hàng tỷ | "TXN_abc123def456" |
session_id |
Hàng triệu | "SESSION_xyz789" |
// ✅ ĐÚNG: Key có cardinality cao
const key = order.customer_id; // "CUST_1001", "CUST_1002", ...
Ngoài ra, bạn có thể sử dụng composite key (kết hợp nhiều trường) để tăng độ phân tán:
// Composite key: customer_id + order_date
const key = `${order.customer_id}_${order.date}`;
✅ Best Practices
1. Số lượng Partition bằng hoặc là bội số của số Consumer Instance dự kiến
Nếu bạn dự định chạy 6 Consumer Instance song song trong tương lai, hãy tạo Topic với 6, 12, hoặc 18 Partition. Điều này giúp tối ưu hóa khả năng chia việc và phân phối tải đồng đều.
2. Tuyệt đối không lạm dụng việc tăng số lượng Partition vô tội vạ
| Ngưỡng an toàn | Giới hạn tối đa |
|---|---|
| 100 × b × r (latency-sensitive) | 4.000 Partition/broker |
Trong đó: b = số Broker, r = Replication Factor.
Ví dụ: Cluster có 3 Broker, RF = 3 → ngưỡng an toàn ≈ 100 × 3 × 3 = 900 Partition/broker.
3. Over‑partition từ đầu nếu cần bảo toàn thứ tự theo Key
Vì không thể giảm số Partition sau khi tạo, hãy dự phòng dư ra 2-3 lần so với nhu cầu hiện tại để tránh phải tăng Partition sau này – việc tăng Partition sẽ phá vỡ thứ tự tin nhắn theo Key.
4. Luôn sử dụng Key có High Cardinality để tránh Data Skew
Không bao giờ dùng gender, country, user_type làm Key. Luôn dùng user_id, order_id, transaction_id hoặc composite key.
5. Sử dụng Sticky Partitioner cho tin nhắn không có Key
Từ Kafka 2.4 trở đi, Sticky Partitioner là mặc định và mang lại hiệu năng tốt hơn so với Round‑Robin truyền thống.
❓ FAQ
1. Tôi có thể giảm bớt số lượng Partition của một Topic sau khi đã lỡ tăng lên quá nhiều không? Tại sao?
Câu trả lời: KHÔNG.
Kafka không hỗ trợ giảm số lượng Partition của một Topic. Lý do là việc giảm Partition sẽ phá vỡ cơ chế ánh xạ key → partition, làm hỏng thứ tự tin nhắn và gây ra sự phức tạp trong việc di chuyển dữ liệu giữa các Partition.
Giải pháp: Nếu bạn đã tạo quá nhiều Partition, cách duy nhất là:
- Tạo một Topic mới với số lượng Partition mong muốn
- Migrate dữ liệu từ Topic cũ sang Topic mới bằng Kafka MirrorMaker hoặc custom consumer/producer
2. Làm sao Kafka đảm bảo dữ liệu không bị mất giữa các bản sao Partition (Replication Factor)?
Kafka sử dụng cơ chế In-Sync Replicas (ISR) để đảm bảo độ bền dữ liệu.
Quy trình:
- Producer gửi tin nhắn đến Leader của Partition
- Leader ghi tin nhắn vào log cục bộ
- Các Follower trong danh sách ISR sao chép tin nhắn từ Leader
- Producer chỉ nhận được acknowledgment (acks) sau khi:
acks=0: Không chờ xác nhận (nguy cơ mất dữ liệu)acks=1: Leader đã ghi xong (mặc định)acks=all: Tất cả ISR đã ghi xong
Với acks=all và min.insync.replicas=2, tin nhắn chỉ được coi là đã commit khi có ít nhất 2 replica (Leader + 1 Follower) đã ghi thành công. Điều này đảm bảo ngay cả khi Leader bị lỗi ngay sau khi ghi, vẫn còn ít nhất 1 Follower có dữ liệu.
3. Có thể tăng số lượng Partition sau khi Topic đã hoạt động không? Tác động là gì?
Có, bạn hoàn toàn có thể tăng số Partition bằng lệnh kafka-topics.sh --alter --partitions.
Tác động:
- ✅ Không mất dữ liệu – dữ liệu cũ vẫn nằm ở các Partition cũ.
- ⚠️ Phá vỡ thứ tự tin nhắn theo Key – vì công thức
hash(key) % new_partitionskhác với cũ, các tin nhắn mới của cùng một key có thể vào Partition khác, làm hỏng thứ tự logic. - ⚠️ Có thể gây rebalance – Consumer Group sẽ rebalance để phân phối lại các partition mới.
Khuyến nghị: Chỉ tăng Partition khi bạn thực sự cần thêm throughput và không yêu cầu bảo toàn thứ tự theo key cũ, hoặc bạn đã có kế hoạch xử lý dữ liệu cũ (ví dụ: migrate sang topic mới).
📝 Kết luận
Kafka Partition không chỉ là một khái niệm lý thuyết – nó là trái tim của toàn bộ kiến trúc phân tán của Kafka. Hiểu đúng về Partition giúp bạn:
- 🚀 Tối ưu thông lượng: Thiết kế số lượng Partition phù hợp với nhu cầu xử lý song song
- 🔒 Bảo toàn thứ tự: Sử dụng Message Key thông minh để đảm bảo tính nhất quán của dòng sự kiện
- ⚖️ Tránh Data Skew: Chọn Key có High Cardinality để phân phối tải đồng đều
- 🛡️ Tránh over‑partitioning: Không lạm dụng Partition để tránh các vấn đề về phục hồi và rebalance
Hãy nhớ công thức vàng: Partitions = max(t/p, t/c), và luôn over‑partition dựa trên nhu cầu tăng trưởng tương lai.