min.insync.replicas là tham số quyết định lúc nào Kafka thà từ chối ghi còn hơn nhận rồi mất. Bài này đo cả ba giá trị ở cả ba mức hỏng.
Nó làm gì
Khi producer gửi với acks=all, broker chỉ xác nhận sau khi mọi bản sao trong ISR đã ghi. Vấn đề: ISR có thể co lại. Còn một bản sao trong ISR thì "mọi bản sao" là một, và acks=all lặng lẽ biến thành acks=1.
min.insync.replicas chặn chuyện đó: ISR nhỏ hơn con số này thì broker từ chối ghi với lỗi NOT_ENOUGH_REPLICAS.
Nó là cấu hình của topic, không phải của producer — nên nó là chỗ duy nhất người quản trị áp được luật lên mọi ứng dụng đang ghi vào topic đó.
Đo cả ba giá trị
Ba topic giống hệt nhau, replication-factor=3, chỉ khác min.insync.replicas. Gửi 200 tin với acks=all:
| 3 broker sống | Giết 1 broker | Giết 2 broker | |
|---|---|---|---|
min.insync=1 |
200 gửi được | 200 gửi được | 0 — hết giờ |
min.insync=2 |
200 gửi được | 200 gửi được | 0 — hết giờ |
min.insync=3 |
200 gửi được | 0 — NOT_ENOUGH_REPLICAS |
0 — hết giờ |
Ô đáng chú ý là ô giữa của hàng cuối.
min.insync.replicas=3 với replication-factor=3 khiến mọi lần bảo trì một broker đều làm dừng ghi. Không có dư địa nào. Vá hệ điều hành, nâng phiên bản Kafka, thay ổ đĩa — mỗi việc đó đều thành sự cố toàn phần.
Đây là sai lầm dễ mắc vì nó nghe hợp lý: "muốn an toàn nhất thì đòi cả ba bản sao". Thực tế nó đổi một rủi ro hiếm (mất dữ liệu khi hai broker chết cùng lúc) lấy một chắc chắn thường xuyên (dừng ghi mỗi lần bảo trì).
Luật thực dụng: min.insync.replicas phải nhỏ hơn replication-factor. Chênh lệch đó chính là số broker bạn được phép mất.
Giết hai broker: chuyện khác hẳn
Hàng cuối cùng của bảng — cột "giết 2 broker" — trông như min.insync đang làm việc của nó. Không phải.
Cụm của tôi có ba node, mỗi node mang cả hai vai broker và controller. Đây là cấu hình mặc định và phổ biến nhất cho cụm nhỏ. Giết hai node là mất luôn quorum controller — KRaft cần đa số, tức 2 trên 3.
min.insync = 1 -> 0 tin, TimeoutException
đọc -> 0 tin
describe -> vẫn in "Leader: 1, Isr: 1,2"
Ngay cả min.insync=1 cũng không ghi được — không phải vì thiếu bản sao, mà vì không còn ai bầu leader mới. Đây là kiểu hỏng khác hẳn, và không cấu hình topic nào chạm tới được.
Chi tiết đáng nhớ nhất: describe vẫn trả lời như thể mọi thứ bình thường. Nó in ra Leader: 1, Isr: 1,2 — thông tin từ bản siêu dữ liệu đã lưu sẵn, không hỏi lại controller. Cụm đã chết mà công cụ chẩn đoán vẫn báo khoẻ.
Nếu bạn từng gặp cảnh "Kafka không ghi được nhưng describe nói ổn", đây là một trong những nguyên nhân. Cách kiểm đúng là hỏi thẳng quorum:
kafka-metadata-quorum.sh --bootstrap-server kb1:9092 describe --status
Trên cụm ba node gộp vai, mất hai node là mất tất cả — bất kể replication-factor hay min.insync.replicas bằng bao nhiêu. Cụm năm node chịu được hai node chết. Đó là lý do cụm sản xuất lớn tách controller ra riêng, thường là năm controller nhỏ, để việc bảo trì broker không bao giờ chạm tới quorum.
Bộ ba dùng thật
replication-factor = 3
min.insync.replicas = 2
acks = all
Chịu được một broker chết mà không mất tin và không dừng ghi. Hai broker chết thì dừng ghi — và đó là lựa chọn có chủ ý, không phải lỗi: thà dừng còn hơn nhận tin rồi mất.
Đặt trên topic:
kafka-configs.sh --bootstrap-server kb1:9092 --alter \
--entity-type topics --entity-name ten-topic \
--add-config min.insync.replicas=2
Đổi được lúc đang chạy, có hiệu lực ngay, không cần khởi động lại gì.
Ba chỗ nó không cứu được
1. Producer dùng acks=1 hoặc acks=0. Broker chỉ kiểm min.insync.replicas khi producer đòi acks=all. Đặt min.insync.replicas=2 mà ứng dụng gửi acks=1 thì con số đó hoàn toàn vô nghĩa — phần 4 đã đo: với hai broker chết, acks=1 báo gửi thành công 100 tin trong khi acks=all từ chối.
Không có cách nào ép từ phía broker. Cách duy nhất là kiểm ở phía ứng dụng, hoặc kiểm định kỳ bằng cách đọc cấu hình producer từ số liệu JMX.
2. Topic tạo tự động. auto.create.topics.enable bật mặc định. Topic sinh ra kiểu đó nhận mặc định của broker — thường là replication-factor=1 và min.insync.replicas=1. Một lỗi gõ sai tên topic trong producer là đủ tạo ra một topic không có bản sao nào, và nó chạy êm cho tới khi broker đó chết.
Trên cụm sản xuất, tắt nó đi:
auto.create.topics.enable=false
3. Mất quorum controller. Như đo ở trên. Đây là bài toán tô-pô cụm, không phải bài toán cấu hình topic.
Kiểm nhanh toàn cụm
Liệt kê mọi topic không đạt chuẩn:
for t in $(kafka-topics.sh --bootstrap-server kb1:9092 --list); do
rf=$(kafka-topics.sh --bootstrap-server kb1:9092 --describe --topic "$t" \
| head -1 | grep -oE "ReplicationFactor: [0-9]+" | cut -d' ' -f2)
mi=$(kafka-configs.sh --bootstrap-server kb1:9092 --describe \
--entity-type topics --entity-name "$t" \
| grep -oE "min.insync.replicas=[0-9]+" | cut -d= -f2)
[ "$rf" -lt 3 ] || [ "${mi:-1}" -lt 2 ] && echo "$t rf=$rf min.insync=${mi:-mặc định}"
done
Chạy lệnh này trên một cụm đã sống vài năm thường cho ra một danh sách bất ngờ dài — phần lớn là topic tạo tự động mà không ai nhớ.
Thử ba mươi giây
Kiểm chính xác chỗ topic của bạn sẽ gãy:
kafka-topics.sh --bootstrap-server kb1:9092 --describe --topic ten-topic | head -1
kafka-configs.sh --bootstrap-server kb1:9092 --describe \
--entity-type topics --entity-name ten-topic | grep min.insync
Lấy ReplicationFactor trừ min.insync.replicas. Kết quả là số broker bạn được phép mất trước khi việc ghi dừng lại. Nếu nó bằng 0, mỗi lần bảo trì là một lần gián đoạn.
Phần sau đo lưu trữ: dữ liệu ở lại bao lâu, và điều gì thật sự xảy ra khi hết hạn.