Kafka Interview Prep — คำถามที่พบบ่อย
1. ทำไม Kafka ถึงเร็ว (high throughput)?
สามเหตุผลหลักที่ต้องพูดถึง: (1) Sequential disk write — เขียนข้อมูลต่อท้าย log เรียงลำดับ
เสมอ (ไม่ใช่ random write) ซึ่งเร็วกว่า random I/O มากแม้บน spinning disk (2) Zero-copy
— ใช้ sendfile() syscall ส่งข้อมูลจาก page cache ตรงไปยัง network socket โดยไม่ต้อง copy
ผ่าน application memory space (3) Batching + compression — รวม message หลายตัวเป็น
batch เดียวก่อนส่ง/compress ลด overhead ของแต่ละ network round-trip
จำ 3 คำ: "เรียง-ลัด-รวม" (sequential write, zero-copy shortcut, batch รวม) — Beyond: Zero-copy ที่แท้จริงทำงานร่วมกับ OS page cache อย่างแนบแน่น: Kafka ตั้งใจไม่เก็บ copy ของ data ไว้ใน JVM heap เอง (ต่างจากระบบอื่นที่มักทำ in-process cache) แต่พึ่งพา Linux page cache ล้วนๆ ให้ OS จัดการ caching การอ่านข้อมูลที่ "ใหม่พอ" (เพิ่ง produce ไปไม่นาน) แทบจะเร็ว เท่าอ่านจาก memory เพราะยังอยู่ใน page cache ไม่ต้องแตะ disk จริงเลย — นี่คือเหตุผลที่ broker ที่มี RAM เยอะพอจะเก็บ "recent data" ทั้งหมดไว้ใน page cache ให้ performance ดีกว่า broker ที่ RAM น้อยมาก แม้ configuration อื่นจะเหมือนกันทุกอย่าง
2. Kafka ต่างจาก RabbitMQ (traditional message queue) ยังไง?
| มุมมอง | Kafka | RabbitMQ |
|---|---|---|
| Message retention | เก็บไว้ตาม retention policy แม้ consume แล้ว | ลบทิ้งทันทีหลัง consume (ack) |
| ใครจำ offset | Consumer เป็นคนจำเอง | Broker จำให้ |
| Routing | partition key + consumer group | exchange/routing key ซับซ้อนกว่า (fanout, topic, direct) |
| เหมาะกับ | high-throughput event streaming, replay | complex routing, task queue, RPC pattern |
จำว่า "Kafka คือ log ที่ให้ replay ได้, RabbitMQ คือ queue ที่ของหายเมื่อหยิบไปแล้ว" — Beyond: ความต่างเชิงสถาปัตยกรรมที่ลึกกว่านั้นคือ RabbitMQ มี smart broker, dumb consumer (broker จัดการ routing ซับซ้อน, ack, retry ให้เกือบหมด) ส่วน Kafka ออกแบบเป็น dumb broker, smart consumer (broker แค่เก็บ log เรียงลำดับ ไม่รู้เรื่อง business logic ใดๆ consumer ต้อง จัดการ offset/retry/routing logic เองเกือบทั้งหมด) ปรัชญาการออกแบบที่ตรงข้ามกันนี้เป็นเหตุผลที่ Kafka scale ได้สูงกว่ามาก (broker ทำงานน้อยกว่าต่อ message) แต่ก็ต้องเขียนโค้ด consumer ที่ ซับซ้อนกว่าเพื่อชดเชย
3. Consumer Group Rebalance ทำงานยังไง และมีปัญหาอะไรที่พบบ่อย
Rebalance เกิดเมื่อสมาชิกใน consumer group เปลี่ยน (join/leave/crash) หรือ partition count
ของ topic เปลี่ยน — Group Coordinator (broker ที่ได้รับมอบหมาย) จะคำนวณ partition assignment
ใหม่แล้วแจกให้ทุก consumer ปัญหาที่พบบ่อย: "rebalance storm" ที่เกิดจาก consumer ที่ใช้
เวลา process message นานเกิน max.poll.interval.ms จน Kafka คิดว่า consumer ตายแล้ว
สั่ง rebalance ทั้งที่ consumer ยัง alive อยู่จริงๆ (แค่ทำงานช้า)
จำว่า "rebalance storm = ไล่ออกเพราะคิดว่าตาย ทั้งที่แค่ทำงานช้า" — Beyond: วิธีแก้
max.poll.interval.ms ที่ถูกต้องมี 2 แนวทางที่คนมักเลือกผิด: (1) เพิ่มค่า
max.poll.interval.ms ให้นานพอสำหรับ processing time จริง (ง่ายแต่ลดความไว ในการตรวจจับ
consumer ที่ตายจริง) หรือ (2) ลด max.poll.records ให้ consumer ดึง message น้อยลงต่อ
poll() รอบหนึ่ง (ทำให้แต่ละรอบ process เร็วขึ้น ไม่ชน timeout) ทีมส่วนใหญ่เลือกวิธีแรกเพราะ
ง่ายกว่า แต่วิธีที่สองมักเป็นทางแก้ที่ยั่งยืนกว่าเพราะไม่ลดความสามารถในการตรวจจับ consumer
ที่ตายจริงๆ เร็ว
4. อธิบาย Exactly-once Semantics ใน Kafka อย่างละเอียด
Exactly-once ของ Kafka ประกอบด้วย 2 กลไกทำงานร่วมกัน: Idempotent producer (ป้องกัน duplicate จาก producer retry ผ่าน sequence number) + Transactions (atomic write ข้าม partition/topic ผ่าน transaction coordinator และ marker) — แต่การันตีนี้ใช้ได้เฉพาะภายใน Kafka ecosystem เท่านั้น ไม่ครอบคลุม side effect ภายนอก (ดู Reliability & Ordering)
จำสูตร "Exactly-once = Idempotent (กันซ้ำตอนส่ง) + Transaction (กันครึ่งๆ กลางๆ ตอนเขียน หลายที่)" — Beyond: Kafka Streams (library สำหรับ stream processing บน Kafka) implement exactly-once semantics แบบเต็มรูปแบบที่เรียกว่า "exactly-once v2" (EOS v2, ตั้งแต่ Kafka 2.5+) ที่ลด overhead ลงมากจาก v1 เดิม โดยใช้ transaction เดียวครอบคลุมทั้ง consume-process -produce cycle ของแต่ละ stream task (ไม่ใช่แยก transaction ต่อ output record) ทำให้ "อ่าน จาก topic A, ประมวลผล, เขียนไป topic B" ทั้งหมดเป็น atomic unit เดียวจริงๆ — เป็นรายละเอียด ระดับ implementation ที่แสดงว่าเข้าใจ Kafka Streams ลึกกว่าแค่ "รู้ว่ามันมี exactly-once"
5. ถ้าต้องออกแบบระบบที่ต้องการ strict ordering ข้าม partition จะทำยังไง?
Kafka การันตี order แค่ภายใน partition เดียว ถ้าต้องการ global order จริงๆ มีทางเลือกหลัก: (1) ใช้ partition เดียว สำหรับ topic นั้น (แลกกับ throughput/parallelism ที่หายไปทั้งหมด) (2) re-design key ให้ทุก message ที่ต้องการ order สัมพันธ์กันใช้ key เดียวกัน (บังคับให้ ไปลง partition เดียวกันโดยธรรมชาติ ไม่ต้องเสีย parallelism ของ key อื่น) (3) ยอมรับ eventual ordering แล้วให้ consumer ทำ reordering เอง (เช่น buffer ตาม timestamp/sequence number ก่อนประมวลผล — ซับซ้อนและมี cost สูง)
จำว่า "Global order จริงๆ ต้องแลกด้วย parallelism เสมอ ไม่มีทางได้ทั้งคู่ฟรีๆ" — Beyond: ทางเลือกที่ "ฉลาดที่สุด" ในทางปฏิบัติแทบทุกครั้งคือข้อ (2) re-design key เพราะส่วนใหญ่ "global order" ที่โจทย์ต้องการจริงๆ ไม่ใช่ order ข้ามทุกอย่างในระบบ แต่เป็น order ภายใน entity เดียวกัน (เช่น "transaction ของ account เดียวกันต้องเรียงลำดับ" ไม่ใช่ "transaction ของทุก account ในระบบต้องเรียงลำดับเดียวกันหมด") — ถ้าตั้ง key เป็น account ID ทุก transaction ของ account เดียวกันจะลง partition เดียวกันโดยธรรมชาติ (order การันตีในระดับ ที่ต้องการจริง) ในขณะที่ account อื่นยังกระจายไปหลาย partition ทำงานขนานกันได้เต็มที่ — เป็น คำตอบที่แสดงว่าเข้าใจ "โจทย์จริงๆ ต้องการอะไร" ไม่ใช่แค่ตอบ "ใช้ partition เดียว" ซึ่งเป็น คำตอบระดับ surface เท่านั้น
Kafka's speed comes from sequential disk writes, zero-copy transfer via sendfile(), and
batching/compression — and critically, it deliberately relies on the OS page cache rather than
an in-process JVM cache, so broker RAM directly determines how much "recent data" serves at
memory speed. Compared to RabbitMQ, Kafka is "dumb broker, smart consumer" (retention-based
replay, consumer-tracked offsets) versus RabbitMQ's "smart broker, dumb consumer" (delete-on-ack,
broker-managed routing) — opposite design philosophies trading broker simplicity for consumer
complexity. Rebalance storms happen when slow processing exceeds max.poll.interval.ms; lowering
max.poll.records is often the more durable fix than just raising the interval. Exactly-once
combines idempotent producers with transactions, and Kafka Streams' EOS v2 wraps an entire
consume-process-produce cycle in one transaction for efficiency. True cross-partition ordering
always trades away parallelism — redesigning the partition key around the actual ordering
boundary (e.g., per-account, not global) is usually the smarter answer than a single partition.