Distributed Systems Concepts - System Design, Simply Explained

Share
Distributed Systems Concepts - System Design, Simply Explained

ในช่วงแรก Product เราสามารถออกแบบระบบให้อยู่ใน Server และ Database เดียวกันได้ แต่เมื่อระบบโตขึ้น มีผู้ใช้งานเพิ่มขึ้น ข้อมูลเยอะขึ้น ระบบเริ่มทำงานช้าลดเรื่อยๆ สิ่งที่หนีไม่ได้คือการ Decomposition Service ไปเป็น Distributed System เช่น Member Service, Booking Service, Payment Service, Check-in Service และ Notification Service (วิธีการในการแยก Service เขียนได้อีก 100+ บทความ รอติดตามได้นะ 😁) เพื่อช่วยให้ระบบสามารถ Scale และดูแลแต่ละส่วนได้ง่ายขึ้น(มั้ง)

ปัญหาที่ตามมาคือ ปัญหาของโลก Distributed System เช่น Network ล่มบางส่วน, ข้อมูลแต่ละ Node ไม่ตรงกัน, ลูกค้ากด Retry แล้วเกิดข้อมูลซ้ำ, การทำ Transaction ข้าม Service และการเรียงลำดับข้อมูล logs หรือเหตุการณ์ต่างๆ ที่เกิดจากหลาย Services สิ่งเหล่านี้เป็นเรื่องท้าทายกว่าโลก Monolith มาก

บทความนี้จะอธิบายพื้นฐานของ Distributed Systems Concepts โดยเล่าต่อจากตัวอย่าง Gym Application เพื่อให้เห็นว่าระบบที่เริ่มจาก Server เครื่องเดียว ค่อยๆ โตไปเป็นหลาย Service หลาย Database และหลายเครื่อง Server มี Concepts อะไรบ้างที่เราในฐานะ Software Engineer ควรรู้จักบ้าง(จะได้เถียง AI ได้)

บทความอื่นๆ ที่เกี่ยวข้องกัน

Functional requirements

  • ลูกค้าสมัครสมาชิกและเลือกแพ็กเกจได้
  • ลูกค้าชำระเงินค่าสมาชิกได้
  • ลูกค้าจองคลาสและเทรนเนอร์ได้
  • ลูกค้าสแกน QR Code เพื่อเข้ายิมได้
  • Admin อัปโหลดรูปคลาส รูปเทรนเนอร์ และเอกสารสัญญาได้
  • ระบบสามารถดูประวัติการเปลี่ยนแปลงของสมาชิกได้

Monolith

Monolith คือรูปแบบระบบที่รวมความสามารถหลักไว้ใน Application เดียว เช่น Authentication, Member, Booking, Payment, Admin และ Reporting อยู่ใน Codebase เดียว Deploy เป็นชุดเดียว และมักจะใช้ Database ก้อนเดียวร่วมกัน

Monolith ไม่ได้แย่เสมอไป สำหรับการพัฒนาระบบในช่วงแรก ๆ Monolith อาจเป็นทางเลือกที่ดีเมื่อทีมยังเล็ก ความซับซ้อนยังไม่สูง และการเปลี่ยนแปลง Business Logic ทำได้เร็ว หรือจุดที่คนส่วนมากใช้ตัดสินใจคือเรื่อง Cost และ Timeline

ลองดู Use Case ในระบบ Gym Application ช่วง Release แรก ที่ทีมได้ทำระบบเป็น Monolith โดยประกอบไปด้วย

  • Mobile/Web App เรียก API Server
  • API Server จัดการสมัครสมาชิก จองคลาส ชำระเงิน และ Check-in
  • API Server อ่านเขียนข้อมูลทั้งหมดลง SQL Database เดียว
  • Admin ใช้ระบบเดียวกันในการจัดการข้อมูลคลาสและสมาชิก

รูปแบบนี้ทำให้ทีมพัฒนาได้เร็ว เพราะ Flow เช่น จองคลาสแล้วตัดจำนวนที่นั่ง สามารถทำใน Transaction เดียวได้ง่าย ไม่ต้องคุยข้าม Service และไม่ต้องจัดการ Event หรือ Message Queue ตั้งแต่แรก

ข้อดี

  • Monolith เริ่มต้นง่าย เพราะ Code, Deployment และ Database อยู่รวมกัน ทำให้ Debug และ Trace Flow ของระบบได้ตรงไปตรงมา
  • Transaction ภายในระบบทำได้ง่ายกว่า เพราะหลาย Operation สามารถทำใน Database Transaction เดียวกันได้
  • เหมาะกับทีมเล็กหรือ Product ช่วงแรกที่ยังต้องเปลี่ยน Feature บ่อย และยังไม่รู้ชัดว่า Boundary ของ Service ควรถูกแบ่งตรงไหน

Trade-off

  • เมื่อ Codebase ใหญ่ขึ้น การเปลี่ยนส่วนหนึ่งอาจกระทบส่วนอื่นได้ง่าย เพราะทุกอย่างอยู่ใน Application เดียวกัน
  • การ Scale ทำได้ยาก เช่น ถ้า Booking ใช้งานหนัก แต่ Payment ใช้งานน้อย เราก็ยังต้อง Scale ทั้ง Application ไปพร้อมกัน
  • ถ้า Monolith ล่มหรือ Deploy ผิดพลาด จะกระทบทั้งระบบพร้อมกัน

Modular Monolith

Modular Monolith คือรูปแบบ Monolith ที่ยัง Deploy เป็น Application เดียว แต่แบ่งโครงสร้างภายในออกเป็น Module ที่มีขอบเขตความรับผิดชอบชัดเจน เช่น Member, Booking, Payment และ Check-in

แต่ละ Module มี Business Logic และข้อมูลของตัวเองอย่างชัดเจน และสื่อสารกันผ่าน Interface ที่กำหนดไว้ แทนการเรียกใช้ Code หรือ Database ของ Module อื่นโดยตรง

ข้อดี

  • ระบบยังคงความเรียบง่ายของ Monolith เช่น Deploy ง่ายและทำ Transaction ภายใน Application ได้สะดวก แต่ช่วยลดการผูกกันของ Code และทำให้ Boundary ของแต่ละส่วนชัดขึ้น

Trade-off

  • ถึงแม้จะแบ่งเป็นหลาย Module แต่ทั้งหมดก็ยังอยู่ใน Application และ Deployment เดียวกัน ถ้าระบบส่วนหนึ่งมีปัญหาหรือจำเป็นต้อง Scale ก็อาจยังส่งผลต่อทั้ง Application ได้

Distributed System

Distributed System คือระบบที่ประกอบด้วยหลาย Components ทำงานอยู่บนหลายเครื่อง และสื่อสารกันผ่าน Network เพื่อทำงานร่วมกันเป็นระบบเดียว

Distributed System ช่วยให้ระบบ Scale ได้ดีขึ้นและแยกความรับผิดชอบของแต่ละส่วนชัดขึ้น แต่ก็เพิ่มปัญหาที่ Monolith ไม่ค่อยเจอ เช่น Network Timeout, Partial Failure, Duplicate Request, Data Consistency, Clock Skew และการ Debug ที่ยากขึ้น

อีกหนึ่งเรื่องที่ยากคือ การตัดสินใจว่าระบบของเราควรแบ่งออกเป็นกี่ Services หรือกี่ Components รวมถึงควรแบ่งตาม Technical Layer หรือ Business Capability ซึ่งเป็นเรื่องที่ต้องอาศัยทั้งศาสตร์และศิลป์ในการออกแบบค่อนข้างมาก

ลองดู Use Case ในระบบ Gym Application เมื่อระบบโตขึ้น เราอาจแยกเป็นหลาย Service

  • Member Service ดูแลข้อมูลสมาชิกและแพ็กเกจ
  • Booking Service ดูแลการจองคลาสและจำนวนที่นั่ง
  • Payment Service ดูแลการชำระเงิน
  • Check-in Service ดูแลการสแกน QR Code เข้ายิม
  • Notification Service ส่ง Email, Push Notification และ SMS

เมื่อสมาชิกจองคลาส ระบบอาจต้องเรียก Booking Service เพื่อล็อกที่นั่ง เรียก Payment Service เพื่อชำระเงิน และเรียก Notification Service เพื่อส่งผลการจอง การทำงานหนึ่งครั้งจึงไม่ได้อยู่ใน Process เดียวอีกต่อไป แต่ต้องสื่อสารกันผ่าน Network

ข้อดี

  • Distributed System ช่วยให้แต่ละ Service Scale แยกกันได้ เช่น Booking Service อาจต้อง Scale สูงในช่วงเปิดจองคลาส ส่วน Notification Service อาจใช้ Worker แยกไปประมวลผลภายหลัง
  • แต่ละทีมสามารถดูแล Service ของตัวเองได้ชัดขึ้น และ Deploy บางส่วนได้โดยไม่ต้อง Deploy ทั้งระบบเสมอไป
  • ระบบสามารถทนต่อ Failure ได้ดีขึ้น ถ้าออกแบบให้ Service บางตัวล่มแล้วส่วนอื่นยังทำงานต่อได้

Trade-off

  • Network ไม่ได้เชื่อถือได้เสมอ Request อาจ Timeout, Response อาจหาย หรือ Service ปลายทางอาจทำงานสำเร็จแล้วแต่ Caller ไม่ได้รับคำตอบ
  • ข้อมูลที่กระจายอยู่หลาย Service อาจไม่ตรงกันทันที ทำให้ต้องออกแบบ Consistency, Retry, Idempotency และ Observability ให้ดี
  • การ Debug ยากขึ้น เพราะ Request หนึ่งอาจผ่านหลาย Service หลาย Log และหลาย Database

CAP Theorem

CAP Theorem คือแนวคิดที่บอกว่าใน Distributed System เราไม่สามารถรับประกัน Consistency, Availability และ Partition Tolerance ได้ครบทั้ง 3 อย่างพร้อมกันในช่วงที่เกิด Network Partition

Consistency หมายถึงทุก Node เห็นข้อมูลล่าสุดเหมือนกัน
Availability หมายถึงทุก Request ได้รับ Response แม้บางส่วนของระบบมีปัญหา
Partition Tolerance หมายถึงระบบยังรับมือได้เมื่อ Network ระหว่าง Node บางส่วนขาดการเชื่อมต่อ

ในระบบจริง Network Partition มีโอกาสเกิดขึ้นได้เสมอ เช่น Data Center เชื่อมต่อกันไม่ได้ หรือ Service บางเครื่องติดต่อ Database บางตัวไม่ได้ ดังนั้น Partition Tolerance เป็นสิ่งที่หลีกเลี่ยงไม่ได้ คำถามจริงๆ ในมุมมองของ Business คือเมื่อเกิด Network Partition ระบบจะเลือก Consistency หรือ Availability มากกว่ากัน

ลองดู Use Case ในระบบ Gym Application ถ้าสาขากรุงเทพและเชียงใหม่มีระบบ Check-in แยกกัน แล้ว Network ระหว่างสาขากับศูนย์กลางมีปัญหา

  • ถ้าเลือก Consistency: ยอมให้ระบบใช้งานไม่ได้ชั่วคราว จนกว่าจะยืนยันข้อมูลสมาชิกที่ถูกต้องและล่าสุดได้ เพื่อไม่ให้คนที่สมาชิกหมดอายุเข้าใช้ยิม
  • ถ้าเลือก Availability: ยอมให้ Check-in ต่อได้โดยอิงข้อมูล Cache ล่าสุด แม้ข้อมูลอาจเก่า จึงมีความเสี่ยงที่สมาชิกซึ่งหมดอายุแล้วจะยัง Check-in ผ่านได้

ข้อดี

  • CAP Theorem ช่วยให้ทีมออกแบบระบบด้วย Trade-off ที่ชัดเจน ไม่คาดหวังว่าระบบกระจายหลายเครื่องจะถูกต้องล่าสุดและตอบได้ตลอดเวลาในทุกสถานการณ์
  • ช่วยให้เลือกแนวทางตามความสำคัญของข้อมูล เช่น Payment และจำนวนที่นั่งควรเน้น Consistency ส่วนจำนวน Like หรือ Analytics อาจเน้น Availability ได้มากกว่า

Trade-off

  • CAP เป็นแนวคิด High Level มากๆ ไม่ได้ตอบว่าในระบบจริงต้องออกแบบอย่างไร เพราะแต่ละ Endpoint หรือข้อมูลแต่ละประเภทอาจต้องเลือก Trade-off ต่างกัน
  • ถ้าเลือก Consistency มากเกินไป ระบบอาจปฏิเสธ Request บ่อยในช่วง Network มีปัญหา แต่ถ้าเลือก Availability มากเกินไป ผู้ใช้อาจเห็นหรือทำงานบนข้อมูลที่ไม่ล่าสุด

Strong Consistency

Strong Consistency คือการรับประกันว่าหลังจากเขียนข้อมูลสำเร็จ การอ่านครั้งถัดไปจากทุก Node ที่เกี่ยวข้องต้องเห็นข้อมูลล่าสุด

แนวคิดนี้สำคัญกับระบบที่ต้องใช้ข้อมูลล่าสุดในการตัดสินใจ เช่น ยอดเงิน, สถานะการชำระเงิน, จำนวนที่นั่งคงเหลือ หรือสิทธิ์การเข้าใช้งาน เพราะถ้าอ่านข้อมูลเก่า อาจทำให้เกิดปัญหา เช่น จองที่นั่งเกินจำนวน, ตัดสินสถานะการชำระเงินผิด หรืออนุญาตให้ผู้ที่หมดสิทธิ์เข้าใช้งานได้

ลองดู Use Case ในระบบ Gym Application คลาส Pilates มีที่นั่งสุดท้ายเหลือ 1 ที่ แต่มีลูกค้า 2 คนกดจองพร้อมกัน ถ้าระบบใช้ข้อมูลเก่าหรือ Replica ที่ยังไม่อัปเดต ทั้งสองคนจะจองที่นั่งเดียวกันสำเร็จ

ในกรณีนี้ Booking Service ควรใช้ Strong Consistency กับข้อมูลจำนวนที่นั่ง เช่น เขียนผ่าน Leader ตัวเดียว ใช้ Transaction และตรวจสอบ Seat Availability ใน Database เดียวกันก่อน Commit เพื่อให้จองได้เพียงคนเดียว

ข้อดี

  • Strong Consistency ช่วยลดความเสี่ยงของข้อมูลขัดแย้งกัน เช่น จองที่นั่งเกินจำนวน, หักเงินผิด หรือให้สิทธิ์เข้าใช้งานผิดสถานะ
  • เหมาะกับ Workflow ที่ผู้ใช้คาดหวังว่าผลลัพธ์ต้องถูกต้องทันทีหลังทำรายการ เช่น Payment, Booking และ Contract Update

Trade-off

  • การรับประกันข้อมูลล่าสุดต้องมีการประสานงานระหว่าง Node หรือบังคับให้เขียนผ่านจุดควบคุมเดียว ทำให้ Latency สูงขึ้น
  • ในช่วง Network Partition ระบบอาจต้องปฏิเสธ Request หรือรอจนกว่าจะยืนยันสถานะล่าสุดได้ ทำให้ Availability ลดลง

Eventual Consistency

Eventual Consistency คือการรับประกันว่า หลังจากมีการเปลี่ยนแปลงข้อมูล ข้อมูลใน Replica หรือ Service ต่างๆ อาจยังไม่ตรงกันทันที แต่จะค่อยๆ อัปเดตจนมีข้อมูลตรงกันในภายหลัง

แนวคิดนี้เหมาะกับข้อมูลที่ยอมให้ช้าได้เล็กน้อย เช่น ยอดรวม Analytics, คะแนนความนิยมของคลาส, Feed, Notification Status หรือข้อมูล Summary ที่สร้างจาก Event

ลองดู Use Case ในระบบ Gym Application หลังจากสมาชิกจองคลาสสำเร็จ Booking Service จะบันทึกข้อมูลการจองทันที แล้วส่ง Event ไปให้ Notification Service และ Reporting Service ทำงานต่อ ผู้ใช้จึงเห็นหน้า Booking สำเร็จทันที แต่ Dashboard ของ Admin จะยังไม่แสดงจำนวนการจองล่าสุด หรือ Email ยืนยันอาจถูกส่งตามมาภายหลังอีกไม่กี่วินาที

นี่คือตัวอย่างของ Eventual Consistency เพราะข้อมูลในแต่ละส่วนของระบบไม่จำเป็นต้องอัปเดตพร้อมกันทันที แต่จะค่อยๆ ตามกันจนตรงกันในภายหลัง

ข้อดี

  • Eventual Consistency ช่วยลดการประสานงานแบบ Synchronous ระหว่าง Service ทำให้ระบบตอบผู้ใช้ได้เร็วขึ้นและทนต่อ Failure ได้ดีขึ้น
  • เหมาะกับงานที่ทำภายหลังได้ เช่น ส่ง Notification, อัปเดต Report, สร้าง Search Index หรือคำนวณ Recommendation

Trade-off

  • ผู้ใช้อาจเห็นข้อมูลไม่ตรงกันชั่วคราว เช่น หน้า Booking แสดงสำเร็จแล้ว แต่หน้า History ยังไม่ขึ้นทันที
  • Application ต้องออกแบบ UX และ Logic ให้รองรับสถานะระหว่างทาง เช่น pending, processing, retrying หรือ failed compensation

Consensus

Consensus คือกระบวนการที่ทำให้หลาย Node ใน Distributed System ตกลงกันได้ว่า ข้อมูลหรือสถานะไหนคือค่าที่ถูกต้อง แม้บาง Node จะล่ม หรือ Network ระหว่าง Node จะมีปัญหา

แนวคิดนี้ถูกใช้ในหลายเรื่อง เช่น Leader Election, Distributed Lock และ Replicated Log เพราะระบบต้องมีคำตอบร่วมกันว่าใครเป็น Leader, ใครถือ Lock หรือข้อมูลรายการไหนถูกยืนยันแล้ว

อัลกอริทึมอย่าง Raft และ Paxos มักใช้เสียงข้างมากของ Node เพื่อช่วยตัดสินว่า การเปลี่ยนแปลงหนึ่งสามารถถือว่าได้รับการยอมรับแล้ว ตัวอย่างเช่น ถ้ามี 5 Node และต้องเลือก Leader ระบบต้องให้ Node ส่วนใหญ่เห็นตรงกันว่า Node A เป็น Leader ก่อน จึงถือว่า Node A เป็น Leader ของ Cluster

ข้อดี

  • Consensus ช่วยป้องกัน Split Brain ซึ่งเป็นสถานการณ์ที่หลาย Node เข้าใจว่าตัวเองมีสิทธิ์ตัดสินใจพร้อมกัน
  • เหมาะกับข้อมูลหรือการตัดสินใจที่ต้องมีคำตอบเดียว เช่น ใครเป็น Leader, Log Entry ไหนถูก Commit หรือ Lock ตัวไหนถูกถืออยู่

Trade-off

  • Consensus มีต้นทุนสูง เพราะต้องสื่อสารหลายรอบและมักต้องรอเสียงข้างมาก ทำให้ Latency สูงกว่าการเขียนลง Node เดียว
  • ถ้า Node ส่วนใหญ่ไม่พร้อมใช้งาน ระบบที่พึ่ง Consensus จะหยุดรับการเปลี่ยนแปลงใหม่เพื่อรักษาความถูกต้อง

Leader Election

Leader Election คือกระบวนการที่ Node หลายตัวในระบบเลือก Node หนึ่งขึ้นมาเป็น Leader เพื่อรับผิดชอบงานที่ควรมีผู้ควบคุมหลักเพียงตัวเดียว

Leader จะทำหน้าที่เป็น Node หลักสำหรับการเขียนข้อมูล, จัดลำดับงาน, Run Scheduler หรือควบคุมงานบางอย่างที่ควรมี Node เดียวเป็นผู้ตัดสินใจ ส่วน Node อื่นอาจเป็น Follower ที่รอทำงานตาม Leader หรือพร้อมขึ้นมาแทนเมื่อ Leader ล่ม

ลองดู Use Case ในระบบ Gym Application ระบบอาจมี Worker หลายตัวที่คอยส่ง Reminder ให้สมาชิกก่อนคลาสเริ่ม 1 ชั่วโมง ถ้าทุก Worker ทำงานเดียวกันพร้อมกัน สมาชิกจะได้รับ Notification ซ้ำหลายครั้ง

ข้อดี

  • Leader Election ช่วยให้งานที่ต้องมีผู้ประสานงานหลักทำได้โดยไม่ต้องผูกกับ Server เครื่องเดียวตลอดเวลา
  • เมื่อ Leader ล่ม ระบบสามารถเลือก Node อื่นขึ้นมาทำหน้าที่แทนได้ ช่วยลด Downtime

Trade-off

  • ช่วงที่ Leader ล่มและกำลังเลือก Leader ใหม่ ระบบอาจหยุดรับงานบางประเภทชั่วคราว
  • ถ้าการเลือก Leader ออกแบบไม่ดี อาจเกิด Split Brain ทำให้มี Leader มากกว่าหนึ่งตัวและสร้างข้อมูลขัดแย้ง

Idempotency

Idempotency คือคุณสมบัติของ Operation ที่ทำซ้ำหลายครั้งแล้วผลลัพธ์สุดท้ายเหมือนทำครั้งเดียว

แนวคิดนี้สำคัญมากใน Distributed System เพราะเราไม่สามารถรู้ได้เสมอว่า Request ที่ส่งไปสำเร็จหรือไม่ เช่น Request อาจไปไม่ถึง Server, Server ทำงานสำเร็จแล้วแต่ Response ส่งกลับมาไม่ถึง Client หรือ Client Timeout แล้ว Retry Request เดิมอีกครั้ง

ลองดู Use Case ในระบบ Gym Application ลูกค้ากดชำระเงินค่าสมาชิก แล้ว Mobile App ได้ Timeout เพราะ Network ไม่ดี

ความจริงสามารถเกิดได้หลายแบบ

  • Payment Service ยังไม่ได้รับ Request
  • Payment Service ได้รับ Request และตัดเงินสำเร็จแล้ว แต่ Response กลับไม่ถึง Mobile App
  • Payment Service กำลังประมวลผลอยู่ แต่ Client Retry ไปอีกครั้ง

ถ้า Operation ไม่เป็น Idempotent การ Retry จะทำให้ลูกค้าถูกตัดเงินซ้ำ หรือสร้าง Payment Record ซ้ำหลายรายการ

ข้อดี

  • Idempotency ทำให้ระบบ Retry ได้อย่างปลอดภัยมากขึ้น โดยเฉพาะ Request สำคัญที่ Caller ต้องส่งซ้ำเมื่อ Timeout หรือเจอ Network Error
  • ช่วยลด Duplicate Side Effect เช่น ตัดเงินซ้ำ จองซ้ำ ส่ง Notification ซ้ำ หรือสร้าง Resource ซ้ำ

Trade-off

  • Application ต้องออกแบบ Operation ให้ตรวจสอบผลลัพธ์เดิมได้ เช่น ใช้ Unique Constraint, Request State หรือ Idempotency Key
  • บาง Operation ทำให้เป็น Idempotent ยาก โดยเฉพาะงานที่มี Side Effect ภายนอก เช่น เรียก Payment Gateway, ส่ง SMS หรือส่ง Email

Idempotency Key

Idempotency Key คือรหัสเฉพาะที่ Client ส่งมากับ Request เพื่อให้ Server ใช้ตรวจสอบว่า Request นี้เคยถูกประมวลผลไปแล้วหรือยัง

ถ้า Server ได้รับ Key เดิมซ้ำ ระบบควรคืนผลลัพธ์เดิมกลับไปโดยไม่ทำ Side Effect ซ้ำ เช่น ไม่ตัดเงินซ้ำ ไม่สร้าง Booking ซ้ำ และไม่ส่ง Notification ซ้ำ

ลองดู Use Case ในระบบ Gym Application ตอนลูกค้ากดจ่ายเงิน Mobile App สร้าง idempotency_key หนึ่งค่า เช่น payment_01HX... แล้วส่งไปกับ Request ชำระเงินทุกครั้งที่ Retry

Payment Service ทำงานประมาณนี้

  • ตรวจว่า idempotency_key นี้เคยถูกใช้หรือยัง
  • ถ้าไม่เคย ใช้ Key นี้สร้าง Payment Attempt และเริ่มตัดเงิน
  • ถ้าสำเร็จ เก็บผลลัพธ์ไว้กับ Key นั้น
  • ถ้า Client Retry ด้วย Key เดิม คืนผลลัพธ์เดิมโดยไม่ตัดเงินซ้ำ

ข้อดี

  • Idempotency Key ทำให้ Request ที่ปกติไม่ปลอดภัยต่อการ Retry เช่น POST /payments หรือ POST /bookings สามารถ Retry ได้ปลอดภัยขึ้น
  • ช่วยให้ Client รับมือกับ Timeout ได้ดีขึ้น เพราะส่ง Request ซ้ำด้วย Key เดิมได้โดยไม่ต้องเดาว่าครั้งแรกสำเร็จหรือไม่

Trade-off

  • Server ต้องเก็บสถานะของ Key และผลลัพธ์เดิมไว้ช่วงเวลาหนึ่ง ทำให้มี Storage และ Cleanup Logic เพิ่มขึ้น
  • ต้องกำหนด Scope ของ Key ให้ชัด เช่น Key เดียวใช้ได้กับ User ไหน Endpoint ไหน และ Payload เดิมเท่านั้นหรือไม่ ไม่เช่นนั้นอาจเกิด Bug หรือช่องโหว่ด้านความปลอดภัย

Two-Phase Commit (2PC)

Two-Phase Commit หรือ 2PC คือ Protocol ที่ใช้ทำให้ Transaction ที่เกี่ยวข้องกับหลาย Database หรือหลาย Service ตัดสินใจไปในทิศทางเดียวกัน คือ Commit ทั้งหมด หรือ Abort ทั้งหมด มันคือแนวทางในการแก้ปัญหา ACID ในโลกของ Distributed Transaction

2PC มี 2 ช่วงหลัก

  • Prepare Phase คือ Coordinator ถามทุก Participant ว่าพร้อมที่จะ Commit หรือไม่
  • Commit Phase คือถ้าทุก Participant ตอบว่าพร้อม Coordinator จะสั่งให้ทุก Participant Commit แต่ถ้ามีตัวใดตัวหนึ่งไม่พร้อม จะสั่ง Abort ทั้งหมด

ลองดู Use Case ในระบบ Gym Application ถ้าการซื้อแพ็กเกจต้องเขียนข้อมูลลง Member Database, Payment Database และ Contract Database และต้องการให้ทุกส่วนสำเร็จหรือยกเลิกพร้อมกัน เราอาจใช้ 2PC

Coordinator จะถามแต่ละระบบก่อนว่าพร้อม Commit หรือไม่ ถ้าทุกระบบตอบว่าพร้อม จึงสั่ง Commit ทั้งหมด แต่ถ้ามีระบบใดระบบหนึ่งไม่พร้อม ก็จะสั่ง Abort ทั้งหมด

ข้อดี

  • 2PC ให้ Atomicity ข้ามหลายระบบได้ในเชิงแนวคิด ทำให้ลดกรณีที่ Payment สำเร็จแต่ Contract ไม่ถูกสร้าง หรือ Contract ถูกสร้างแต่ Payment ไม่สำเร็จ
  • เหมาะกับระบบที่ควบคุม Participant ได้ชัดเจน และต้องการความถูกต้องแบบ Transaction สูงมาก

Trade-off

  • Participant อาจต้องถือ Lock ระหว่างรอ Coordinator ตัดสินใจ ทำให้ระบบช้าและลด Throughput
  • ถ้า Coordinator ล่มระหว่างทาง Participant บางตัวอาจค้างอยู่ในสถานะรอ Commit หรือ Abort ทำให้ 2PC เป็นแนวทางที่เปราะบางสำหรับ Distributed System ขนาดใหญ่

Saga Pattern

Saga Pattern คือแนวทางจัดการ Workflow ที่ข้ามหลาย Service โดยแบ่งงานใหญ่ออกเป็นชุดของ Local Transaction และมี Compensating Action สำหรับย้อนผลของแต่ละขั้นตอนเมื่อขั้นตอนหลังล้มเหลว

Saga ไม่ได้ทำให้ทุกอย่าง Atomic แบบ 2PC แต่พาระบบไปสู่ Eventual Consistency ผ่านการชดเชยผลลัพธ์ที่ทำไปแล้ว

ลองดู Use Case ในระบบ Gym Application การสมัครแพ็กเกจ Premium อาจมีขั้นตอนแบบนี้

  • สร้าง Contract ใน Member Service
  • ตัดเงินใน Payment Service
  • เปิดสิทธิ์เข้าใช้งานใน Access Service
  • ส่ง Email ยืนยันใน Notification Service

ถ้าตัดเงินสำเร็จแต่เปิดสิทธิ์ไม่สำเร็จ Saga อาจต้องทำ Compensating Action เช่น ปิด Contract, Refund เงิน หรือแจ้งทีม Support ให้ตรวจสอบตาม Policy ของระบบ

ข้อดี

  • Saga Pattern เหมาะกับ Database per service เพราะแต่ละ Service ทำ Local Transaction ของตัวเอง ไม่ต้องถือ Lock ข้ามระบบนานๆ
  • ระบบทนต่อ Failure ได้ดีขึ้น เพราะขั้นตอนที่ล้มเหลวสามารถ Retry หรือ Compensation ได้ตาม Business Rule

Trade-off

  • การออกแบบ Compensating Action ไม่ง่ายเสมอไป บางสิ่งย้อนกลับจริงไม่ได้ เช่น ส่ง Email ออกไปแล้ว หรือ Payment Gateway บางประเภท Refund ไม่ได้ทันที
  • ระบบต้องรองรับ Intermediate State เช่น payment_succeeded_but_access_pending และต้องมี Monitoring เพื่อจับ Saga ที่ค้างหรือ Compensation ล้มเหลว

Consistent Hashing Ring

Consistent Hashing Ring คือเทคนิคที่ใช้กระจาย Key ไปยัง Node หลายตัว โดยพยายามให้การเพิ่มหรือลด Node กระทบกับ Key เดิมให้น้อยที่สุด

จุดเด่นคือ เมื่อมีการเพิ่มหรือลด Node ระบบไม่จำเป็นต้องย้าย Key ทั้งหมด แต่จะย้ายเฉพาะ Key บางส่วนที่ได้รับผลกระทบ จึงเหมาะกับระบบอย่าง Distributed Cache หรือ Sharded Storage ที่ต้องเพิ่มลด Node บ่อย

ลองดู Use Case ในระบบ Gym Application ถ้าเราใช้ Redis Cluster เก็บ Cache ของ member_profile, class_schedule และ trainer_detail ระบบต้องกระจาย Key จำนวนมากไปยังหลาย Node

ถ้าใช้วิธีง่าย ๆ อย่าง member_id % number_of_nodes เมื่อจำนวน Node เปลี่ยน เช่น จาก 4 เป็น 5 ตำแหน่งของ Key จำนวนมากก็จะเปลี่ยนตาม ทำให้ต้องย้าย Cache จำนวนมากและอาจเกิด Cache Miss เพิ่มขึ้น

Consistent Hashing ช่วยลดปัญหานี้ โดยทำให้มีเพียง Key บางส่วนที่ต้องย้ายเมื่อจำนวน Node เปลี่ยน และมักใช้ Virtual Node เพื่อช่วยกระจาย Load ให้สม่ำเสมอขึ้น

ข้อดี

  • Consistent Hashing ช่วยให้เพิ่มหรือลด Node ได้โดยกระทบข้อมูลเพียงบางส่วน ไม่ต้อง Rebalance Key ทั้งหมด
  • เหมาะกับระบบ Cache หรือ Storage ที่ต้องกระจาย Key จำนวนมากและต้องการลดผลกระทบตอน Scale Cluster

Trade-off

  • การกระจายข้อมูลอาจไม่สม่ำเสมอถ้ามี Node น้อยหรือไม่มี Virtual Node ทำให้บาง Node รับ Load มากกว่าตัวอื่น
  • ระบบต้องจัดการเรื่อง Replication, Failure Detection และ Rebalancing เพิ่มเติม เพราะ Consistent Hashing เป็นเพียงวิธีเลือก Node ไม่ใช่ระบบ Storage ที่สมบูรณ์ในตัวเอง

Clock Skew

Clock Skew คือความคลาดเคลื่อนของเวลาระหว่างเครื่องหลายเครื่องในระบบ แม้จะใช้ Time Sync เช่น NTP เวลาในแต่ละเครื่องก็ยังอาจต่างกันเป็น Milliseconds หรือมากกว่านั้นได้

ปัญหานี้สำคัญเพราะ Distributed System คือมีหลาย Node เขียน Log, สร้าง Event หรืออัปเดตข้อมูลพร้อมกัน ถ้าใช้ Timestamp จากเครื่องแต่ละตัวมาตัดสินลำดับเหตุการณ์โดยตรง อาจได้ข้อสรุปผิด

ลองดู Use Case ในระบบ Gym Application ลูกค้าคนหนึ่งอัปเกรดแพ็กเกจเป็น Premium ผ่าน Member Service และเกือบพร้อมกันนั้นสแกน QR Code เข้ายิมผ่าน Check-in Service

ถ้า Clock ของ Check-in Service เร็วกว่าของ Member Service 500 milliseconds Log อาจแสดงเหมือน Check-in เกิดก่อนการอัปเกรด ทั้งที่ความจริงสิทธิ์ Premium ถูกเปิดก่อนแล้ว

ข้อดี

  • การเข้าใจ Clock Skew ช่วยให้ทีมไม่ใช้ Wall-clock Timestamp เป็นหลักฐานเดียวในการตัดสินลำดับเหตุการณ์ที่สำคัญ
  • ช่วยให้ระบบออกแบบ Audit Log, Event Ordering, Expiration และ Distributed Lock ด้วยความระมัดระวังมากขึ้น

Trade-off

  • การเลี่ยงปัญหา Clock Skew ทำให้ระบบต้องเพิ่มกลไกอื่น เช่น Logical Clock, Version Number, Database Sequence หรือ Vector Clock
  • บาง Feature ที่ดูง่าย เช่น expire token, sort event หรือ last write wins อาจต้องออกแบบละเอียดขึ้นเมื่อข้อมูลมาจากหลายเครื่อง

Vector Clock

Vector Clock คือกลไกที่ใช้ติดตามลำดับเชิงเหตุและผลของ Event ใน Distributed System โดยไม่ต้องพึ่งเวลาจริงจากนาฬิกาของแต่ละเครื่อง

แต่ละ Node จะมี Counter ของตัวเอง และส่งค่าของ Counter ไปพร้อมกับ Message เมื่อ Node อื่นรับ Message ก็จะรวมค่า Clock ด้วยการเลือกค่าสูงสุดของแต่ละ Node แล้วเพิ่ม Counter ของตัวเอง

สิ่งที่ Vector Clock บอกได้คือ Event ไหนเกิดก่อนอีก Event แบบมีความสัมพันธ์เชิงเหตุผล และ Event ไหนเกิดพร้อมกันแบบไม่มีใครเป็นสาเหตุของใคร ซึ่งช่วยมากในระบบ Eventually Consistent ที่ต้อง Resolve Conflict

ลองดู Use Case ในระบบ Gym Application ถ้า Admin 2 คนแก้ไขข้อมูลโปรไฟล์เทรนเนอร์คนเดียวกันจากคนละสาขาในช่วง Network มีปัญหา

  • Admin A แก้เบอร์โทร
  • Admin B แก้รูปโปรไฟล์
  • สองการแก้ไขเกิดบนคนละ Node และ Sync กันภายหลัง

ถ้าใช้ Timestamp อย่างเดียว ระบบอาจเลือก Update ที่เวลาล่าสุดแล้วทิ้งอีก Update ทั้งที่สองการแก้ไขไม่ได้ขัดแย้งกันจริง Vector Clock ช่วยให้ระบบรู้ว่า Update สองชุดเกิด Concurrent กัน และสามารถ Merge หรือให้คนตรวจสอบ Conflict ได้

ข้อดี

  • Vector Clock ช่วยตรวจจับความสัมพันธ์ของ Event ได้ดีกว่า Timestamp เพราะไม่พึ่ง Clock ของเครื่องที่อาจคลาดเคลื่อน
  • เหมาะกับระบบที่มีการเขียนข้อมูลจากหลาย Node และต้อง Resolve Conflict เช่น Distributed Database, Offline-first App หรือ Eventually Consistent Store

Trade-off

  • Vector Clock เพิ่มข้อมูล Metadata ที่ต้องส่งและเก็บไปกับแต่ละ Version ของข้อมูล
  • เมื่อจำนวน Node มากขึ้น Vector Clock อาจใหญ่ขึ้นและจัดการยากขึ้น จึงต้องมีวิธีตัดทอนหรือจำกัดขอบเขตการใช้งาน

สรุป

Distributed System ช่วยให้ระบบ Scale ได้ แยกความรับผิดชอบของแต่ละส่วนได้ และทนต่อ Failure ได้ดีขึ้น แต่ต้องแลกกับความซับซ้อนที่สูงกว่าระบบแบบ Monolith มาก

Monolith เหมาะกับจุดเริ่มต้น ถ้าจะให้ดีกว่าคือ Modular Monolith เหมาะกับจุดเริ่มต้นที่ต้องการความเร็วและความเรียบง่าย ส่วน Distributed System เหมาะเมื่อระบบโตจนต้องแยก Scale, แยกทีม หรือแยก Failure Boundary

แนวคิดอย่าง CAP Theorem, Strong Consistency, Eventual Consistency, Consensus, Leader Election, Idempotency, Saga, Consistent Hashing, Clock Skew และ Vector Clock คือเครื่องมือที่ช่วยให้เราออกแบบ Distributed System ได้รอบคอบขึ้น

Refs:

Amazon.com: System Design Interview – An insider’s guide eBook : Xu, Alex: Kindle Store
Amazon.com: System Design Interview – An insider’s guide eBook : Xu, Alex: Kindle Store
50 System Design Concepts Explained Simply [2026 Edition]
Fifty of the Most Important System Design Concepts, Each Explained Simply and Clearly With What It Does, Why It Exists, and When to Use It

Read more

Communication Concepts - System Design, Simply Explained

Communication Concepts - System Design, Simply Explained

บทความนี้จะอธิบายพื้นฐานของ System Design โดยต่อยอดจากตัวอย่าง Gym Application เพื่อให้เห็นภาพว่า เมื่อระบบเริ่มเติบโตและถูกแยกออกเป็นหลาย Service แต่ละ Service จะสื่อสารและทำงานร่วมกันอย่างไร งานแบบไหนควรสื่อสารแบบ Synchronous งานแบบไหนควรประมวลผลแบบ Asynchronous รวมถึงอธิบาย Concept พื้นฐานต่าง ๆ ที่ควรรู้ เช่น API

By Boonsong Srithong
Race condition

Race condition

ผมกำลังอ่านหนังสือ The Unicorn Project ซึ่งเป็นนิยายเกี่ยวกับ developer สาวคนหนึ่งที่ใช้ชีวิตในองค์กรท่ามกลางกระแส digital disruption อ่านแล้วสนุกมาก เป็นเรื่องที่ตื่นเต้น และหลาย ๆ dynamic ที่เกิดขึ้นก็คล้ายกับที่ผมพบเจอในบริษัทซอฟต์แวร์ เลยทำให้

By Chokchai Phatharamalai
Core Infrastructure Concepts - System Design, Simply Explained

Core Infrastructure Concepts - System Design, Simply Explained

บทความนี้จะอธิบายพื้นฐานของ Core Infrastructure Concepts ในมุมมองของ System Design โดยเล่าต่อจากตัวอย่าง Gym Application ในบทความ Data & Storage Concepts เพื่อให้เห็นว่า Request หนึ่งจาก Client ต้องผ่านอะไรบ้างก่อนจะไปถึง Backend, Database หรือ Storage จากบทความ Data & Storage Concepts ตอนนี้ Gym

By Boonsong Srithong
Database & Storage Concepts - System Design, Simply Explained

Database & Storage Concepts - System Design, Simply Explained

บทความนี้จะอธิบาย Database & Storage Concepts พื้นฐานในมุมมองของ System Design บอกข้อดีและ Trade-off ที่ควรรู้ พร้อมยกตัวอย่างผ่าน Use Case ของระบบ Gym Application บทความอื่นๆ ที่เกี่ยวข้องกัน * Core Infrastructure Concepts - System Design, Simply Explained * Distributed Systems Concepts - System Design, Simply

By Boonsong Srithong