Ba khái niệm này thường được vẽ bằng hình hộp và mũi tên. Bài này mở thư mục dữ liệu ra xem, và tìm được một cái bẫy về key mà hình vẽ không bao giờ cho thấy.
Topic là thư mục
Tạo một topic 4 partition rồi nhìn vào đĩa:
/tmp/kafka-logs/t4-0/ t4-1/ t4-2/ t4-3/
Mỗi partition một thư mục. Bên trong, khi chưa có tin nào:
00000000000000000000.log 0 byte
00000000000000000000.index 10.485.760 byte
00000000000000000000.timeindex 10.485.756 byte
leader-epoch-checkpoint 8 byte
partition.metadata 43 byte
Topic rỗng mà đã chiếm 20 MB tệp chỉ mục mỗi partition. Chúng được cấp phát trước và ánh xạ bộ nhớ; phần thân là lỗ thưa nên du báo 12 KB. Lát nữa sẽ thấy vì sao con số này không đáng lo.
Ghi 100.000 tin 200 byte vào, .log lên 5.207.886 byte cho một partition. Dữ liệu thật là 100.000 × 200 ÷ 4 = 5.000.000 byte, phần dôi ra khoảng 4% là phần đầu mỗi bản ghi và phần đầu mỗi lô.
Partition quyết định thứ tự — và chỉ trong phạm vi của nó
Kafka không có thứ tự toàn cục. Nó chỉ hứa: các tin cùng vào một partition thì ra theo đúng thứ tự vào.
Không có key thì producer rải đều. 100.000 tin vào 4 partition:
p0 24.804 p1 23.872 p2 28.080 p3 23.244
Không chia chằn chặn 25.000 vì producer dùng lối "gộp dính": nó dồn đầy một lô cho một partition rồi mới chuyển. Đủ đều để không thành vấn đề.
Cái bẫy: bốn key vào bốn partition
Muốn giữ thứ tự cho từng khách hàng thì gửi kèm key. Cùng key luôn vào cùng partition. Tôi thử 20.000 tin với 4 key K0 K1 K2 K3 vào topic 4 partition, chờ mỗi partition một key:
p0 5.000 p1 0 p2 0 p3 15.000
Hai partition rỗng hoàn toàn, một partition ôm ba phần tư.
Kafka chọn partition bằng murmur2(key) % số_partition. Bốn giá trị băm có thể chia dư ra cùng một số, và ở đây đúng là như vậy: K1, K2, K3 cùng rơi vào p3.
Phản xạ đầu tiên là thêm partition. Cùng 4 key đó vào topic 8 partition:
p0 5.000 p1 0 p2 0 p3 5.000 p4 0 p5 0 p6 0 p7 10.000
Năm partition rỗng, và một partition vẫn gấp đôi. Thêm partition không chữa được — nó chỉ đổi cách đụng.
Cái chữa được là số key phải lớn hơn số partition rất nhiều. Cùng phép đo, 1.000 key vào 4 partition:
p0 4.640 p1 4.880 p2 5.160 p3 5.320
Lệch cao nhất 7%. Đủ đều.
Bài học thực dụng: nếu key của bạn là region với 5 giá trị, hay tenant với 12 giá trị, đừng mong tải rải đều — dù bạn đặt bao nhiêu partition. Một consumer sẽ ngồi không trong khi consumer khác quá tải, và đồ thị độ trễ sẽ cho thấy điều đó chứ không ai báo lỗi cả.
Offset và segment
Offset là số thứ tự trong một partition, bắt đầu từ 0, tăng đơn điệu, không bao giờ dùng lại. Nó không phải id toàn cục: offset 4998 của partition 0 và offset 4998 của partition 3 là hai tin khác nhau.
Đọc thẳng từ một offset:
kafka-console-consumer.sh --bootstrap-server kf:9092 --topic tk \
--partition 0 --offset 4998 --max-messages 2 \
--property print.offset=true --property print.key=true
Offset:4998 K0 tin-19996
Offset:4999 K0 tin-20000
Trên đĩa, partition không phải một tệp khổng lồ mà là nhiều segment. Đặt segment.bytes=1MB rồi ghi 30.000 tin:
00000000000000000000.log 1.048.128 .index 504 byte
00000000000000004992.log 1.048.128 .index 504 byte
00000000000000009984.log 1.048.128 .index 504 byte
00000000000000014976.log 1.048.128 .index 504 byte
00000000000000019968.log 1.048.128 .index 504 byte
00000000000000024960.log 1.048.128 .index 504 byte
00000000000000029952.log 10.093 .index 10.485.760 byte <- đang mở
Tên tệp chính là offset đầu tiên trong khúc đó. Muốn đọc từ offset 20.100, Kafka tìm nhị phân trên danh sách tên để chọn tệp 19968, rồi tra .index để nhảy đúng vị trí byte. Không quét tuần tự từ đầu.
Và đây là câu trả lời cho 20 MB chỉ mục ở đầu bài: chỉ khúc đang mở mới giữ kích thước cấp phát trước. Khi khúc đóng lại, Kafka cắt chỉ mục xuống đúng cỡ thật — 504 byte. Sáu khúc đã đóng cộng lại chưa tới 4 KB.
Segment còn là đơn vị xoá. Kafka không xoá từng tin; hết hạn lưu trữ thì nó xoá cả tệp segment. Đó là lý do retention.ms không bao giờ chính xác đến từng tin, và là lý do segment.bytes quá lớn khiến dữ liệu cũ nằm lâu hơn bạn nghĩ.
Đặt bao nhiêu partition
Ba ràng buộc, theo thứ tự quan trọng:
- Số partition là trần của song song đọc. Một partition chỉ được một consumer trong nhóm đọc. 4 partition thì consumer thứ 5 ngồi không.
- Tăng được, giảm không được.
--alter --partitionschỉ đi lên. Và tăng partition làm hỏng phân bố key cũ:% nđổi thì tin cùng key sẽ sang partition khác, thứ tự trong quá khứ không còn ý nghĩa. - Mỗi partition tốn tệp và bộ nhớ. Vài nghìn partition trên một broker là bình thường; vài chục nghìn thì thời gian khởi động và thời gian bầu lại leader tăng rõ.
Khởi điểm thực dụng: đặt gấp 2–3 lần số consumer bạn nghĩ sẽ cần lúc cao điểm. Thà thừa một chút còn hơn phải tăng sau.
Thử ba mươi giây
Tự kiểm cái bẫy key trên máy bạn:
kafka-topics.sh --bootstrap-server kf:9092 --create --topic tk --partitions 4 --replication-factor 1
for i in $(seq 1 20000); do echo "K$((i % 4)):tin-$i"; done | \
kafka-console-producer.sh --bootstrap-server kf:9092 --topic tk \
--property parse.key=true --property key.separator=:
kafka-get-offsets.sh --bootstrap-server kf:9092 --topic tk
Nếu có partition nào bằng 0, bạn vừa nhìn thấy lý do tại sao "chia đều theo key" là một giả định chứ không phải một bảo đảm.
Phần sau đo acks — và cho thấy acks=0 không nhanh hơn acks=1 chút nào.