---
id: case-kafka
title: "Kafka: partition, offset và consumer lag"
shortTitle: "Kafka & consumer lag"
track: cases
trackTitle: "Tình huống vận hành"
order: 12
level: important
status: published
estimatedMinutes: 45
prerequisites:
  - case-rabbitmq
next: case-outbox
reviewedAgainst: 2026-10-12
sources:
  - title: "Apache Kafka Introduction"
    url: https://kafka.apache.org/documentation/#intro_concepts_and_terms
  - title: "Kafka Consumer Configs"
    url: https://kafka.apache.org/documentation/#consumerconfigs
  - title: "Kafka Design"
    url: https://kafka.apache.org/documentation/#design
---

# Kafka: partition, offset và consumer lag

Kafka là event streaming platform, không phải queue serverless. Event được giữ lại theo retention, và nhiều consumer group có thể đọc độc lập với nhau. Một message không biến mất chỉ vì một consumer đã đọc nó.

## Để làm gì

Bạn sẽ chọn partition key, đọc consumer lag, và xử lý duplicate khi consumer commit offset sau khi đã ghi database.

## Gồm những gì

- Topic, partition, offset, consumer group.
- Ordering chỉ được đảm bảo trong một partition.
- Lag và rebalance.

Bài không tự dựng cluster production, vì Kafka một node không cho bạn kiểm tra replication.

## Dùng khi nào

Event có nhiều bên cùng đọc, cần replay, cần throughput cao, hoặc cần lưu lịch sử thay đổi. RabbitMQ hợp với task queue và routing linh hoạt; Kafka hợp với event log cần giữ lâu. Hãy chọn theo nhu cầu, đừng chọn theo độ nổi tiếng.

## Kiến thức cốt lõi

Topic được chia thành nhiều partition. Mỗi record có một offset trong partition của nó. Kafka chỉ đảm bảo thứ tự trong cùng một partition. Muốn mọi event của một order giữ đúng thứ tự, hãy dùng `orderId` làm key. Nếu không có key, producer sẽ rải event ra các partition, và `created` có thể tới consumer sau `cancelled`.

Một consumer group chia các partition cho các consumer trong group. Tại một thời điểm, mỗi partition chỉ do một consumer trong group đọc. Topic có 6 partition mà bạn scale group lên 20 consumer thì 14 consumer sẽ ngồi không. Số partition là giới hạn trên cho mức song song của group.

Consumer lag là latest offset trừ đi committed offset. Lag tăng có thể do consumer chậm, do downstream chậm, hoặc do rebalance liên tục. Vì vậy đừng phản xạ bằng cách scale consumer: nếu database mới là nút nghẽn, thêm consumer chỉ làm tình hình tệ hơn.

Khi lag tăng, điều tra theo thứ tự:

1. Xem lag theo từng partition bằng `kafka-consumer-groups.sh --describe`. Lag dồn vào một partition thường là do key bị lệch hoặc một record độc; lag tăng đều trên mọi partition thì consumer hoặc downstream đang chậm.
2. Xem group có đang rebalance liên tục không. Nếu có, consumer không có thời gian xử lý, và scale thêm chỉ làm rebalance nhiều hơn.
3. Xem downstream: CPU của consumer thấp mà connection tới database đầy nghĩa là nút nghẽn nằm ở database, không nằm ở consumer.

Bạn chỉ scale consumer khi cả ba bước trên cho thấy chính consumer là nút nghẽn, và số consumer vẫn còn nhỏ hơn số partition.

Commit offset trước khi xử lý xong có thể làm mất công việc khi consumer crash. Commit sau khi xử lý thì có khả năng duplicate: database đã ghi, consumer crash trước khi commit, record được đọc lại. Giống như RabbitMQ, consumer cần idempotent.

"Exactly once" của Kafka có phạm vi giới hạn. Transaction của Kafka có thể atomic giữa việc consume và produce, nhưng chỉ trong Kafka. Ghi vào PostgreSQL rồi commit offset vẫn là hai hệ riêng, nên bạn vẫn cần idempotency hoặc transactional outbox/inbox.

Rebalance tạm dừng các partition rồi chia lại cho consumer khi group thay đổi. Nếu consumer xử lý một record lâu hơn `max.poll.interval.ms`, nó có thể bị loại khỏi group; record đó được giao cho consumer khác và chạy trùng.

## Lệnh và code mẫu

Đừng dựng cluster chỉ để học. Khi đã có một Kafka lab:

```bash
kafka-consumer-groups.sh \
  --bootstrap-server localhost:9092 \
  --describe \
  --group order-worker
```

Các cột cần đọc:

| Cột | Ý nghĩa |
| --- | --- |
| CURRENT-OFFSET | Offset group đã commit |
| LOG-END-OFFSET | Offset mới nhất của partition |
| LAG | Số record chưa xử lý theo offset |

Pseudo-code:

```js
for (const event of batch) {
  await db.transaction(async (tx) => {
    if (await tx.inbox.has(event.topic, event.partition, event.offset)) return;
    await applyEvent(tx, event);
    await tx.inbox.insert(event.topic, event.partition, event.offset);
  });
}
await consumer.commitOffsets(offsetsAfterBatch);
```

## Thực hành

1. Topic có 6 partition, group có 10 consumer. Có tối đa bao nhiêu consumer làm việc? Đáp án: 6.
2. Chọn key cho chuỗi event `order-created`, `paid`, `cancelled`. Đáp án: `orderId`.
3. Mô tả chuyện xảy ra khi consumer crash sau khi commit database nhưng trước khi commit offset: record được chạy lại, và bảng inbox chặn duplicate.
4. Lag tăng, CPU của consumer thấp, connection tới database đầy: hãy sửa downstream, đừng scale mù.

## Kết quả mong đợi

Bạn không hứa ordering trên toàn topic. Bạn đọc lag theo từng partition, và hiểu duplicate là chuyện bình thường của at-least-once.

## Checklist

- [ ] Key giữ các event cần thứ tự trong cùng một partition.
- [ ] Số partition đủ cho mức song song cần thiết, nhưng không tăng vô hạn.
- [ ] Consumer idempotent trước khi commit offset.
- [ ] Có alert theo lag và tuổi event, không chỉ theo CPU của broker.
- [ ] Xem downstream trước khi scale consumer.

## Lỗi thường gặp

**Dùng timestamp làm key.** Event của cùng một order bị rải ra nhiều partition và mất thứ tự nghiệp vụ. Hãy đặt key theo entity.

**Bật auto-commit offset trong khi handler vẫn đang chạy.** Nếu consumer crash, record bị coi là đã xử lý xong. Hãy tắt auto-commit, hoặc tự kiểm soát việc commit sau khi xử lý theo cách client của bạn hỗ trợ.

**Reset offset để replay thẳng trên production.** Các side effect như gửi email hay trừ tiền sẽ chạy lại. Hãy replay vào một group riêng hoặc bật chế độ dry-run, và đảm bảo có idempotency.

## Nguồn

1. [Kafka concepts](https://kafka.apache.org/documentation/#intro_concepts_and_terms) — topic, partition, consumer group.
2. [Consumer configs](https://kafka.apache.org/documentation/#consumerconfigs) — auto commit, poll interval và session.
3. [Kafka design](https://kafka.apache.org/documentation/#design) — ordering, delivery semantics và transactions.

## Tiếp theo

[Database và event: Transactional Outbox](./13-outbox.md)
