ฟีเจอร์ LISTEN/NOTIFY ของ PostgreSQL สร้างการล็อกฐานข้อมูลทั้งระบบภายใต้โหลดหนัก

ทีมชุมชน BigGo
ฟีเจอร์ LISTEN/NOTIFY ของ PostgreSQL สร้างการล็อกฐานข้อมูลทั้งระบบภายใต้โหลดหนัก

ปัญหาการขยายตัวที่สำคัญของฟีเจอร์ LISTEN/NOTIFY ของ PostgreSQL ได้จุดประกายการอพยพอย่างกว้างขวางในชุมชนนักพัฒนา หลังจากที่ Recall.ai แบ่งปันประสบการณ์เกี่ยวกับปัญหาประสิทธิภาพฐานข้อมูล บริษัทได้ค้นพบว่าฟีเจอร์ที่ดูเหมือนไม่เป็นอันตรายนี้สามารถทำให้ระบบฐานข้อมูลทั้งหมดล่มได้ภายใต้โหลดการเขียนพร้อมกันที่หนัก

ปัญหาการล็อกทั้งระบบที่ซ่อนอยู่

ฟีเจอร์ LISTEN/NOTIFY ของ PostgreSQL ซึ่งมักใช้สำหรับการแจ้งเตือนแบบเรียลไทม์และรูปแบบ pub-sub มีข้อจำกัดทางสถาปัตยกรรมที่น่าประหลาดใจ เมื่อคำสั่ง NOTIFY ทำงานภายในธุรกรรม มันจะได้รับการล็อกทั้งระบบในฐานข้อมูลทั้งหมดในระหว่างขั้นตอนการคอมมิต สิ่งนี้ทำให้การคอมมิตฐานข้อมูลทั้งหมดกลายเป็นการเข้าแถวเดียว ทำลายความสามารถในการจัดการกับผู้เขียนหลายคนพร้อมกันอย่างสิ้นเชิง

ปัญหานี้จะรุนแรงเป็นพิเศษภายใต้สถานการณ์ที่มีการใช้งานพร้อมกันสูง Recall.ai ประสบปัญหานี้โดยตรงเมื่อประมวลผลการบันทึกการประชุมหลายล้านชั่วโมงด้วยผู้เขียนพร้อมกันหลายหมื่นคน โหลดฐานข้อมูลของพวกเขาเพิ่มขึ้นอย่างมาก แต่การใช้งาน CPU และดิสก์กลับลดลง ซึ่งเป็นสัญญาณที่ชัดเจนว่าระบบติดอยู่กับการรอการล็อกมากกว่าการทำงานจริง

หมายเหตุ: การล็อกทั้งระบบหมายความว่ามีเพียงการดำเนินการเดียวเท่านั้นที่สามารถดำเนินการได้ในแต่ละครั้งทั่วทั้งฐานข้อมูล ซึ่งจะบล็อกธุรกรรมอื่นๆ ทั้งหมดจากการเสร็จสิ้น

ปัญหาการขยายขนาดของ PostgreSQL LISTEN/NOTIFY:

  • ปัญหา: การล็อกฐานข้อมูลแบบ Global ในระหว่างขั้นตอน NOTIFY commit
  • ผลกระทบ: ทำให้การ commit ฐานข้อมูลทั้งหมดต้องเรียงคิวกันภายใต้การเขียนข้อมูลพร้อมกัน
  • อาการ: ฐานข้อมูลมีโหลดสูง การใช้งาน CPU/disk ต่ำ อัตราการประมวลผล query ลดลงอย่างมาก
  • เกณฑ์การขยายขนาด: เริ่มมีปัญหาเมื่อมี writer พร้อมกันหลักหมื่นตัว
  • ประเภทการล็อก: AccessExclusiveLock บน object 0 ของ class 1262 ของ database 0
เมตริกประสิทธิภาพของระบบที่เน้นผลกระทบของ global locking ต่อการดำเนินงานของฐานข้อมูล
เมตริกประสิทธิภาพของระบบที่เน้นผลกระทบของ global locking ต่อการดำเนินงานของฐานข้อมูล

ปฏิกิริยาของชุมชนและทางเลือกอื่น

ชุมชนนักพัฒนาได้ตอบสนองด้วยการผสมผสานระหว่างการพูดว่า เราบอกคุณแล้ว และความกังวลที่แท้จริงเกี่ยวกับข้อจำกัดที่มีเอกสารไม่ดีนี้ นักพัฒนาที่มีประสบการณ์หลายคนชี้ให้เห็นว่า PostgreSQL เป็นเลิศในการดำเนินการข้อมูลเชิงสัมพันธ์ แต่ต่อสู้กับโหลดงาน pub-sub ที่มีการใช้งานพร้อมกันสูง

แนวทางทางเลือกหลายแนวทางได้เกิดขึ้นจากการอภิปราย นักพัฒนาบางคนแนะนำให้ใช้คิวข้อความเฉพาะเช่น NATS หรือ Redis สำหรับการดำเนินการ pub-sub ในขณะที่คนอื่นๆ แนะนำโซลูชันแบบ polling โดยใช้ฟีเจอร์ FOR UPDATE SKIP LOCKED ของ PostgreSQL แนวทางการตรวจสอบ WAL (Write-Ahead Log) ก็ได้รับความสนใจเช่นกันในฐานะวิธีการตรวจจับการเปลี่ยนแปลงฐานข้อมูลโดยไม่มีปัญหาการล็อก

หมายเหตุ: การตรวจสอบ WAL เกี่ยวข้องกับการดูล็อกธุรกรรมของ PostgreSQL เพื่อหาการเปลี่ยนแปลง ให้วิธีการตอบสนองต่อการอัปเดตฐานข้อมูลโดยไม่ใช้ LISTEN/NOTIFY

วิธีแก้ปัญหาทางเลือกที่มีการหารือ:

  • แนวทางการ Polling: การใช้ FOR UPDATE SKIP LOCKED ร่วมกับ exponential backoff
  • Message queues: NATS , Redis หรือ Kafka สำหรับการดำเนินงาน pub-sub
  • การตรวจสอب WAL: การฟังการเปลี่ยนแปลงใน PostgreSQL Write-Ahead Log
  • การติดตามในชั้น Application: การย้ายตรรกะการแจ้งเตือนออกจากฐานข้อมูล
  • รูปแบบ Transactional outbox: การประมวลผลการแจ้งเตือนแยกต่างหากหลังจาก commit

บทเรียนสำหรับสถาปัตยกรรมฐานข้อมูล

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

การอภิปรายของชุมชนเผยให้เห็นว่านักพัฒนาหลายคนได้พบปัญหาที่คล้ายกันกับฟีเจอร์อื่นๆ ของ PostgreSQL เช่น triggers และ row-level security ภายใต้โหลดสูง ฉันทามติดูเหมือนจะเป็นว่าแม้ฟีเจอร์เหล่านี้จะทำงานได้ดีสำหรับโหลดปานกลาง แต่พวกมันไม่ได้ขยายตัวแบบเชิงเส้นกับการใช้งานพร้อมกันที่เพิ่มขึ้น

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

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

การก้าวไปข้างหน้า

สำหรับนักพัฒนาที่ใช้ LISTEN/NOTIFY อยู่ในปัจจุบัน นี่ไม่ได้หมายถึงการตื่นตระหนกทันที ฟีเจอร์นี้ทำงานได้ดีสำหรับแอปพลิเคชันส่วนใหญ่ที่มีโหลดปานกลาง อย่างไรก็ตาม คุ้มค่าที่จะพิจารณาทางเลือกอื่นหากคุณกำลังวางแผนสำหรับการเติบโตที่สำคัญหรือประสบปัญหาประสิทธิภาพอยู่แล้ว

เหตุการณ์นี้ยังแสดงให้เห็นถึงคุณค่าของการตรวจสอบและการบันทึกที่เหมาะสม Recall.ai สามารถระบุประเภทการล็อกเฉพาะที่ทำให้เกิดปัญหาได้เฉพาะหลังจากเปิดใช้งานการบันทึกการล็อกโดยละเอียด ซึ่งช่วยให้พวกเขาติดตามปัญหากลับไปยังฟีเจอร์ LISTEN/NOTIFY

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

อ้างอิง: Postgres LISTEN/NOTIFY does not scale