SQLite ใช้บน production ได้จริง ถ้ารู้ไม่กี่เรื่อง เริ่มจากคำสั่งเดียวที่ลดเวลา query จาก 5 วินาทีเหลือ 0.05 วินาที
SQLite ไม่ใช่แค่ฐานข้อมูลของเล่นสำหรับฝึกเขียนโปรแกรม แต่ใช้เป็นฐานข้อมูลบน production ได้จริง เช็กลิสต์นี้มาจากประสบการณ์ของคนที่ใช้ SQLite เป็นฐานข้อมูลให้เว็บจริง ตั้งแต่คำสั่งที่ช่วยให้ query เร็วขึ้นไปจนถึงวิธีสำรองข้อมูล

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

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

ก่อนเริ่มโปรเจกต์ เธอเปิดโหมด WAL ไว้ตั้งแต่แรกตามที่บล็อกทั่วไปแนะนำ โหมดนี้ช่วยให้การอ่านกับการเขียนฐานข้อมูลไม่ต้องรอคิวกันมากเท่าเดิม
แต่ WAL แก้ได้แค่ส่วนหนึ่ง เพราะข้อจำกัดจริงๆ คือ SQLite รองรับการเขียนข้อมูลได้ทีละงานเท่านั้น งานอื่นที่ต้องเขียนข้อมูลในช่วงเดียวกันจึงต้องรอคิว
ปัญหาโผล่มาตอนต้องลบข้อมูลเก่าทิ้งเป็นก้อนใหญ่ เช่น แถวข้อมูลของงานที่เสร็จแล้วแต่ยังค้างอยู่ในฐานข้อมูล ลำดับเหตุการณ์ที่เธอเจอเป็นแบบนี้
- สั่งลบข้อมูลเก่าทีเดียวเป็นก้อนใหญ่
- คำสั่งลบใช้เวลาเกิน 5 วินาที เพราะมีแถวให้ลบเยอะ
- ระหว่างนั้นมีงานอื่นเข้ามาเขียนฐานข้อมูล แต่ต้องรอ เพราะ SQLite ให้เขียนทีละงาน
- พอรอเกิน 5 วินาทีตามเวลาที่ตั้งไว้ (busy_timeout) งานนั้นก็ล้มเหลว ทำให้ VM ซึ่งเป็นเครื่องที่รันเว็บอยู่ล่มตามไปด้วย
ทางแก้ของเธอคือซอยงานลบเป็นก้อนเล็กๆ ให้แต่ละก้อนเสร็จภายใน 5 วินาที งานอื่นจะได้ไม่ต้องรอคิวนานเกินไป
เธอบอกตรงๆ ว่าประสบการณ์นี้ทำให้เข้าใจมากขึ้นว่าทำไมบางงานถึงต้องใช้ฐานข้อมูลอย่าง Postgres ที่รองรับงานเขียนพร้อมกันได้หลายงาน อีกทางที่เธอคิดไว้คือปิดเว็บช่วงสั้นๆ เพื่อทำงานพวกนี้ แต่ยังไม่ได้ทำจริง
สำรองข้อมูลด้วย VACUUM INTO หรือ Litestream
เธอสำรองข้อมูลไว้สองแบบ
แบบแรกคือใช้ restic เครื่องมือสำหรับสำรองข้อมูลไปยังพื้นที่เก็บข้อมูลบนคลาวด์ เริ่มจากสั่ง SQLite ให้สร้างสำเนาฐานข้อมูลชุดใหม่ด้วยคำสั่ง VACUUM INTO
sqlite3 /data/calendar.db "VACUUM INTO '/tmp/calendar.sqlite'"
จากนั้นบีบอัดไฟล์แล้วส่งขึ้น S3 (ที่เก็บไฟล์บนคลาวด์ของ Amazon) ด้วย restic จุดที่เธอเจอบ่อยคือบางครั้งระบบหยุดงานสำรองกลางคันเพื่อเรียกคืนหน่วยความจำ แล้วทิ้งล็อกค้างไว้ ต้องสั่ง restic unlock ก่อนถึงจะทำงานต่อได้
แบบที่สองคือ Litestream เครื่องมือที่คอยส่งข้อมูลส่วนที่เปลี่ยนแปลงไปยังพื้นที่สำรองอย่างต่อเนื่อง แทนการสำรองข้อมูลเป็นรอบๆ เธอแค่เขียนไฟล์ตั้งค่าแล้วสั่งรัน
litestream replicate -config litestream.yml
เธอตั้งค่า retention ไว้ที่ 400h เพื่อเก็บข้อมูลย้อนหลังช่วงหนึ่ง แม้จะยังไม่แน่ใจว่าระบบเก็บข้อมูลย้อนหลังตามที่ตั้งไว้จริงไหม
แต่มีเรื่องหนึ่งที่เธอยอมรับตรงๆ คือยังไม่เคยลองกู้ข้อมูลกลับจากไฟล์สำรองทั้งสองแบบเลย เธอได้แต่คอยเช็กว่าการสำรองยังทำงานอยู่ โดยใช้ตัวแจ้งเตือนที่ส่งสัญญาณถ้าการสำรองหยุดไป (dead man's switch) และเหตุการณ์อย่างAI ลบฐานข้อมูล production ทั้งก้อนใน 9 วินาที ก็ย้ำว่าไฟล์สำรองจะมีค่าก็ต่อเมื่อกู้กลับได้จริง
แยกฐานข้อมูลเป็นหลายไฟล์ได้ ถ้าตารางไม่ต้องพึ่งกัน
อีกวิธีที่ใช้กับ SQLite ได้คือแยกตารางของโปรเจกต์เดียวออกเป็นไฟล์ฐานข้อมูลหลายไฟล์ วิธีนี้ใช้ได้เมื่อตารางเหล่านั้นไม่ต้องแก้ไขพร้อมกันเป็นชุดเดียว หรือไม่มี transaction ข้ามตาราง
เธอเคยใช้วิธีนี้กับโปรเจกต์ Mess with DNS โดยแยกตารางไปไว้ในไฟล์ฐานข้อมูลสามไฟล์ เพราะตารางพวกนั้นไม่จำเป็นต้องอยู่ในไฟล์เดียวกัน โปรเจกต์นี้รันบน SQLite มาสี่ปีตั้งแต่ปี 2022 หลังย้ายมาจาก Postgres และเธอมองว่าเป็นการตัดสินใจที่ดี
ถ้าจะดูว่าตารางไหนแยกออกไปได้บ้าง ต้องเห็นภาพก่อนว่าตารางในฐานข้อมูลเชื่อมโยงกันยังไง เดี๋ยวนี้มีเครื่องมืออย่าง sqltoerdiagram ที่วาง SQL แล้วได้แผนภาพฐานข้อมูล ช่วยให้เห็นภาพได้เร็วขึ้น
เมื่อไหร่ SQLite พอ เมื่อไหร่ควรขยับไปฐานข้อมูลตัวใหญ่
เส้นแบ่งค่อนข้างชัด ในเคสของเธอ ฐานข้อมูลมีขนาดเล็กและนิ่ง มีข้อมูลราวหนึ่งหมื่นแถว และคาดว่าจะมีขนาดเท่านี้ไปเรื่อยๆ ฐานข้อมูลเล็กจนเธอไม่ต้องปรับ query ของ ORM (ตัวช่วยเขียนคำสั่งฐานข้อมูลแทนเราในโค้ด) ให้เร็วขึ้นเลยก็ยังไหว
ข้อจำกัดของ SQLite คือเขียนข้อมูลได้ทีละงาน ถ้างานนั้นต้องเขียนฐานข้อมูลพร้อมกันเยอะๆ หรือข้อมูลโตขึ้นเรื่อยๆ โดยไม่มีทีท่าจะหยุด ก็เป็นสัญญาณว่าถึงเวลาขยับไปใช้ฐานข้อมูลขนาดใหญ่ที่ออกแบบมารองรับงานเขียนพร้อมกันได้หลายงาน
เธอทิ้งท้ายไว้ว่า ใช้ SQLite ทำเว็บมาตั้งแต่ปี 2022 แต่เพิ่งรู้ว่ามีคำสั่ง ANALYZE ในวันที่เขียนบันทึกนี้เอง
เท่ากับว่าเช็กลิสต์พวกนี้ไม่ใช่สิ่งที่ต้องรู้ครบตั้งแต่วันแรก เมื่อใช้งานจริง เราจะค่อยๆ เจอปัญหาทีละข้อ และตราบใดที่ฐานข้อมูลยังเล็กและนิ่งพอ เราก็มีเวลาไล่แก้ทีละข้อได้ทัน นั่นแหละคือเงื่อนไขจริงๆ ที่ทำให้เราใช้ SQLite กับงานจริงไหว
ที่มา: บทความ Learning a few things about running SQLite โดย Julia Evans
ชอบเรื่องแนวนี้ มีอีบุ๊คฟรีให้อ่านต่อ
NotebookLM ฉบับเข้าใจง่าย โยนเอกสารให้ AI อ่าน แล้วได้สรุป พอดแคสต์ และคลังความรู้ส่วนตัว
กดสมัครแล้วเราจะส่งเทคนิค AI และของแจกใหม่ๆ ให้ทางอีเมล เลิกรับได้ตลอด



