Concurrency Control 101

Tae Keerati Jearjindarat
3 min readNov 1, 2020

ในยุคนี้ที่เราต้องออกแบบและพัฒนา Application ให้คนใช้หลาย ๆ คนในเวลาเดียวกัน ในบางครั้งเราไม่สามารถเลี่ยงได้เลยที่จะต้องเจอเรื่อง Concurrency ภายในระบบของเราซึ่งจะเกิดขึ้นง่ายมากหากมีการแชร์การเข้าถึงข้อมูลให้เข้าถึงได้หลายคนในเวลาเดียวกัน ไม่ว่าจะเก็บใน memory หรือ storage ก็ตาม และยิ่งระบบที่ให้ความสำคัญกับความถูกต้องของข้อมูลมาก ๆ ก็ยิ่งจำเป็นต้องมี Concern เรื่องนี้เพราะเมื่อเกิด Concurrency ยิ่งมีโอกาสทำให้ระบบของเราเก็บข้อมูลผิดพลาด (Lost updated) และบางครั้งอาจทำให้เกิดการเก็บข้อมูลผิดทั้ง ๆ ที่ Operation นั้นยังไม่สำเร็จ (Dirty Read) และสุดท้ายเมื่อเก็บข้อมูลผิด ข้อมูลที่ถูกนำไปใช้ก็ผิดพลาดอีก ทำให้ระบบเกิดบัคที่เป็นกรณีที่ทำสอบยากพอสมควร เนื่องจากมันไม่ใช่กรณีที่จะเขียน Unit Test ได้ง่าย ๆ

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

ยุคนี้ก็มีทั้ง SQL และ NoSQL หากเราใช้ Technology ตระกูล Relational Database ปกติก็คือ SQL เราก็ควรออกแบบ Application และโดยอิงกฏของ Database Transaction ตามชื่อย่อ ACID ซึ่งถ้าคุณใช้ NoSQL ผมจะแนะนำให้ไปดูเรื่อง BASE แทนเพราะใช้วิธีคิดคนละแบบกันโดยจะขอข้ามไปก่อน และขออธิบายเรื่อง ACID แบบไว ๆ ดังนี้นะครับ

Atomicity เราควรเก็บ Transaction ก็ต่อเมื่อ committ แล้วและจะไม่เก็บข้อมูล รวมถึงจะไม่จะกระทบข้อมูลอื่น ๆ ด้วยหาก Operations นั้นผิดพลาด เช่นหากโอนเงินสำเร็จก็ควรจะบันทึกข้อมูลถูกต้องทั้งบัญชีผู้โอนและผู้รับ แต่ถ้าการโอนผิดพลาด เงินทั้งสองฝ่ายจะต้องเหมือนเดิม

Consistency ทุก Transaction จะต้องมีข้อมูลสอดคล้องกัน โดย state ของข้อมูลจะต้องถูกต้องหากถูกเปลี่ยนจากสถานะหนึ่งไปเป็นอีกสถานะหนึ่ง เช่น เมื่อมีการสั่งสินค้าและยกเลิกสินค้า เช่น เมื่อเราออกแบบแอปเรียกรถ ผู้ออกแบบจะต้องคิดว่าผลจากการยกเลิกการเรียกรถจะทำให้บันทึกผลเป็นอย่างไร ทั้งการยกเลิกปกติและกรณีที่หากมีการยกเลิกพร้อมกันกับเมื่อฝั่งคนขับรถได้รับคำสั่งแล้ว เราต้องคิดว่าว่าสุดท้ายแล้วสถานะของข้อมูลจะต้องเป็น ยกเลิก order หรือ ถูก confirm

Isolation พูดถึงว่า หากมี 2 Transactions ทั้ง 2 จะต้องไม่แทรกแทรงซึ่งกันและกันในตอนสุดท้ายที่ทั้ง 2 Operations ทำสำเร็จ โดยหากมีอันใดอันหนึ่งไม่สำเร็จก็จะต้องไม่กระทบกับอีก Transaction เช่น เมื่อมีการโอนเงิน จาก นาย A โอนเงิน 50 และ นาย B โอนเงิน 20 ไปหา C ที่มีเงิน 100 พร้อมกัน ผลลัพธ์จะต้องถูกต้องเป็นเงิน 170 จะต้องไม่มีจำนวนเงินขาดไป ถ้าไม่มีข้อผิดพลาดเช่นนาย B โอนไม่สำเร็จ

Durability พูดถึงว่าผลลัพธ์ที่สำเร็จจะต้องถูกจัดเก็บและเข้าถึงได้โดยไม่ถูกลบหายไป

จาก 4 ข้อที่กล่าวไปใน ACID เราก็มีเรื่องที่ต้องจัดการมากมายในการเขียนโปรแกรม แต่ก็มีเทคนิคที่เราเห็นกันมานานคือ

Pessimistic Locking

เป็น Lock ที่เราน่าจะได้เรียนกันทุกคน คือเราจะ Lock ข้อมูลใน Database เพื่อให้การบันทึกข้อมูลถูกต้อง โดยจะต้อง Block การทำงานของคนอื่นๆ ไปด้วยเพื่อไม่ให้มีคนมาทำงานพร้อมกับเรา ทำให้ไม่มี Concurrency เกิดขึ้นซึ่งมันทำให้เกิดปัญหาคือหากมีคนทำงานอีก 10 คน หรือนับเป็นหลาย ๆ เครื่อง Server ที่เรา Deploy ไปเพื่อให้มันรองรับ Load จาก User ได้เยอะ ๆ ก็จะทำให้ช้าอยู่ดีเพราะสุดท้ายมาติดคอขวดที่ข้อมูลมันถูก Lock ทำให้อ่านหรือเขียนไม่ได้ แล้วระบบก็ช้าอยู่ดี ทำให้นำวิธีนี้ไปออกแบบ Applications ที่มีคนใช้จำนวนมาก จะทำให้เกิดปัญหาได้ ซึ่งเราทุกคนเจอกันบ่อย ๆ ว่าทำไมระบบรอช้าจัง และสุดท้ายก็จะทำให้ผู้ใช้กด Refresh ซึ่งทำให้เกิด Concurrency เพิ่มไปเรื่อย ๆ อีก ทำให้ยิ่งสร้างปัญหาแทนที่จะแก้ปัญหา และเป็นปัญหาที่กระทบต่อ User Experience ด้วยยิ่งทำให้ระบบเราไม่น่าใช้เข้าไปใหญ่ ผมจึงมองว่าวิธีนี้ไม่เหมาะกับการนำไปใช้ในกรณีที่เจอ Requirements ที่ต้องทำให้รองรับคนจำนวนมากครับ

ซึ่งการ Lock แบบนี้ผมเข้าใจว่าทำได้ในตระกูล Relational Database แนว ๆ SQL เท่านั้น ส่วน NoSQL คิดว่าทำไม่ได้เพราะมีวิธีคิดและการออกแบบใช้คนละวิธีกันนะครับ

ตัวอย่างจากภาพคือ Alice กับ Bob อยากโอนเงินให้ Charlie ทำให้ระบบต้องดึงข้อมูลของ Charlie ว่ามีเงินเท่าใด แต่ Alice ดึงข้อมูลก่อนทำให้ Bob ต้องรอจนกว่า Alice ทำสำเร็จ Bob ถึงจะโอนเงินได้สุดท้ายจากภาพคือ Charlie จะมีเงินเพิ่มเป็นจำนวน 70

Optimistic Locking

เป็นวิธีหนึ่งที่ใช้แก้ปัญหา Concurrency โดยจะไม่มีการ Block คนอื่นใดๆ แต่จะใช้วิธีการ Retry หรือทำซ้ำๆ แทน โดยจะ Retry เมื่อเกิด Failed Operation ซึ่งจะ Failed ก็ต่อเมื่อเกิดการอัพเดทข้อมูลผิดพลาด โดยจะใช้เทคนิคการอัพเดทแบบมีเงื่อนไขเพิ่มเติมแทนที่จะอัพเดทเพียงแค่การ Identity ไปหาใคร แต่ Identify ไปหา Record นั้น ๆ ด้วย State บางอย่างด้วยเงื่อนไขที่เรากำหนด โดยที่ State นั้นจะต้องเป็นข้อมูลที่สามารถระบุ Version ของ Record นั้น ๆ ได้

ผมมองว่ากรณีนี้สามารถใช้ใน Real World ได้จริง ๆ หากเราต้องการรองรับ Requirements ที่ต้องทำให้ Application ใช้งานได้หลายคนพร้อม ๆ กัน และ ส่วนตัวไม่ได้มองเป็นการ Lock ที่ข้อมูลแต่เป็นการ Lock ที่ระดับ Operations ที่ต้องทำซ้ำ ๆ จนสำเร็จ(หรือทำซ้ำตามจำนวนครั้งที่กำหนด) ทำให้เทคนิคนี้ใช้ได้ทั้ง SQL และ NoSQL ซึ่งมันทำให้ระบบ Flexible และ Scalable มาก ๆ เราสามารถขยายเครื่อง Server ให้รับ Load ได้เยอะ ๆ โดยที่ระบบไม่ได้ติดปัญหาคอขวดที่ Database อีกต่อไป

ตัวอย่างจากภาพคือ Alice เพิ่มเงินให้ Charlie สำเร็จโดยที่จะใส่เงื่อนไขว่าให้อัพเดทข้อมูลเงินของ Charlie โดยใช้ Amount เป็นตัวกำหนดเวอร์ชั่น ซึ่งทำสำเร็จเพราะไม่มีใครแก้ไขเวอร์ชั่น แต่กลับกันที่ Bob กลายเป็น Failed เพราะ Operations ทำหลังจากที่ Alice อัพเดทข้อมูลสำเร็จไปแล้วและทำการแก้ไข Version จาก 100 เป็น 150 ทำให้ในช่วงเวลานั้น Charlie จะมีเงิน 150 ซึ่งในทางโค้ดเราจะใช้จำนวน Rows ที่อัพเดทสำเร็จ หากเป็น 0 จะแสดงว่า Failed ทำให้ Bob ที่ใส่เงื่อนไขเพิ่มเงินให้คนที่มีเงิน 100 เป็น Failed แต่เมื่อ Retry Bob ได้ ทำการดึงข้อมูลมาใหม่ และเปลี่ยนเงื่อนไขเป็นต้องอัพเดทเงินให้กับคนที่มีเงิน 150 ทำให้ Operations นั้นสำเร็จ

สรุป

ตัวผมเองได้เข้าใจแล้วว่า Pessmistic Locking ไม่เหมาะกับ Requirments ที่มีคนใช้จำนวนมากในเวลาเดียวกันจริง ๆ และ ส่วนตัวคิดว่าเรื่องนี้เป็นเรื่องที่ยากมาก และที่เขียนบล็อคนี้เป็นคอนเซปคร่าว ๆ โดยอิงจากโค้ดที่เคยเขียนและประสบการณ์ที่เจอมาจริง ๆ และมองว่าสามารถนำไปใช้ได้หลาย Use cases ครับ

ผมเข้าใจว่ายังมีเทคนิคการ Lock อีกหลายแบบซึ่งก็ใช้ได้คนละ Use cases เพราะถ้าใช้แต่ Optimistic ก็อาจจะทำให้ระบบอัพเดทข้อมูลผิดได้ในกรณีที่ไม่ควรจะ Retry หรือใช้แต่แบบ Pessmistic ก็อาจทำให้ระบช้าไปเลย ทั้งนี้เลือกใช้ตามความเหมาะสมกับ Requirements นะครับ

ขอบคุณทาง depa และ คณะเทคโนโลยีสารสนเทศ มจธ.
ที่มอบทุนการศึกษาเพชรพระจอมเกล้าเพื่อพัฒนาเทคโนโลยีและนวัตกรรมดิจิทัล และทำให้ได้ความรู้เพิ่มสำหรับใช้ทำงานจริงครับ

เรื่องต่อไปจะเป็นเรื่องอะไร ติดตามกันต่อไป ขอบคุณที่อ่านจนจบครับ 🙏

--

--

Tae Keerati Jearjindarat

Hi, I'm Tae, Associate Engineering Manager at LINEMAN Wongnai. Thanks for following <3