การตั้งค่าความทนทานเริ่มต้นของ SQLite แตกต่างกันในแต่ละแพลตฟอร์ม ก่อให้เกิดการถกเถียงเรื่องความปลอดภัยของฐานข้อมูล

ทีมชุมชน BigGo
การตั้งค่าความทนทานเริ่มต้นของ SQLite แตกต่างกันในแต่ละแพลตฟอร์ม ก่อให้เกิดการถกเถียงเรื่องความปลอดภัยของฐานข้อมูล

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

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

ความแปรปรวนของค่าเริ่มต้นเฉพาะแพลตฟอร์มสร้างความสับสน

การค้นพบที่โดดเด่นที่สุดเกี่ยวข้องกับ macOS ของ Apple ซึ่ง SQLite ที่ระบบจัดให้มีค่าเริ่มต้นเป็น synchronous=NORMAL (ค่า 1) ในขณะที่การติดตั้งใหม่จากตัวจัดการแพ็กเกจเช่น Homebrew มีค่าเริ่มต้นเป็น synchronous=FULL (ค่า 2) ความไม่สอดคล้องกันนี้ขยายไปนอกเหนือจากระบบนิเวศของ Apple โดยมีการแจกจ่าย Linux ที่แตกต่างกันและตัวจัดการแพ็กเกจที่ใช้ตัวเลือกของตนเอง

เมื่อ SQLite ทำงานในโหมด WAL (Write-Ahead Logging) ด้วย synchronous=NORMAL มันจะไม่ทำการ fsync ในแต่ละการคอมมิตธุรกรรม แต่จะซิงค์เฉพาะระหว่างการทำงาน checkpoint ซึ่งเกิดขึ้นไม่บ่อยนัก แม้ว่าวิธีการนี้จะป้องกันการเสียหายของฐานข้อมูล แต่ก็ยังสามารถทำให้ธุรกรรมที่คอมมิตแล้วสูญหายระหว่างไฟฟ้าดับโดยไม่คาดคิด

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

ค่าเริ่มต้นที่แตกต่างกันตามแพลตฟอร์ม

  • macOS (system SQLite): synchronous=NORMAL (1)
  • macOS (Homebrew): synchronous=FULL (2)
  • การแจกจ่าย Linux: โดยทั่วไปใช้ synchronous=FULL (2)
  • ค่าเริ่มต้นจากการคอมไพล์ของ SQLite อย่างเป็นทางการ: synchronous=FULL (2)
  • พฤติกรรมที่กำหนดเองของ Apple: ใช้ F_BARRIERFSYNC แทน F_FULLFSYNC

การแลกเปลี่ยนระหว่างความปลอดภัยและประสิทธิภาพ

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

ค่าเริ่มต้นควรปลอดภัย ปรับแต่งเพื่อประสิทธิภาพ ไม่ใช่ทางกลับกัน

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

fsync: การเรียกระบบที่บังคับให้ระบบปฏิบัติการเขียนข้อมูลที่บัฟเฟอร์ไปยังที่เก็บข้อมูลถาวร เพื่อให้แน่ใจว่ามีความทนทาน

การเปรียบเทียบโหมด Synchronous ของ SQLite

โหมด ค่า พฤติกรรม fsync ความเสี่ยงต่อความทนทาน ประสิทธิภาพ
OFF 0 ไม่มีการเรียก fsync ความเสี่ยงสูงต่อการเสียหาย เร็วที่สุด
NORMAL 1 fsync น้อยที่สุด (เฉพาะ WAL checkpoints) อาจสูญเสียธุรกรรม เร็ว
FULL 2 fsync ในทุกการ commit ความทนทานเต็มรูปแบบ ช้ากว่า
EXTRA 3 มีการ sync ไดเรกทอรีเพิ่มเติม ความทนทานสูงสุด ช้าที่สุด

การใช้งานเฉพาะของ Apple เพิ่มความซับซ้อนอีกชั้นหนึ่ง

การใช้งานของ Apple แนะนำความซับซ้อนเพิ่มเติมนอกเหนือจากการตั้งค่าเริ่มต้น บริษัทได้แก้ไขการสร้าง SQLite ของตนเพื่อใช้ F_BARRIERFSYNC แทน F_FULLFSYNC มาตรฐาน แม้เมื่อแอปพลิเคชันขอการซิงโครไนซ์แบบเต็มอย่างชัดเจน การเปลี่ยนแปลงนี้ส่งผลต่อการรับประกันความทนทานบนระบบ macOS และ iOS แม้ว่าจะให้ลักษณะประสิทธิภาพที่ดีกว่าก็ตาม

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

ผลกระทบต่อแอปพลิเคชันฐานข้อมูล

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

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

อ้างอิง: SQLite (with WAL) doesn't do fsync on each commit under default settings