2 คะแนน โดย GN⁺ 2023-09-19 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • หากนำ OpenDocument Presentation (ODP) ไปเก็บไว้ใน คอนเทนเนอร์ SQLite แทนไฟล์ ZIP archive จะสามารถออกแบบวิธีบันทึก เริ่มต้น และกู้คืนเอกสารให้ปลอดภัยและรวดเร็วขึ้นได้
  • ปัจจุบัน ODP มีโครงสร้างที่รวมไฟล์ XML และไฟล์รูปภาพไว้ใน ZIP archive และไฟล์งานนำเสนอตัวอย่าง 49 สไลด์ประกอบด้วยรายการทั้งหมด 78 รายการ เช่น content.xml, styles.xml, meta.xml, settings.xml และรูปภาพต่าง ๆ
  • ในโครงสร้างแบบ ZIP แม้มีการเปลี่ยนแปลงเล็กน้อยก็มักต้องเขียน archive ทั้งหมดใหม่ ทำให้ทำ incremental update ได้ยาก และนำไปสู่ความล่าช้าในการ File/Save รวมถึงปริมาณการเขียนบน SSD ที่เพิ่มขึ้น
  • หากเปลี่ยนเป็น SQLite จะสามารถเก็บไฟล์เป็นแถวในตารางได้ และยิ่งไปกว่านั้นยังสามารถแบ่งเนื้อหาและเวอร์ชันตามสไลด์ เพื่ออ่านเฉพาะสไลด์แรกหรือบันทึกเฉพาะสไลด์ที่เปลี่ยนแปลงได้
  • นี่ไม่ใช่ข้อเสนอให้วิจารณ์หรือเปลี่ยน OpenDocument เอง แต่เป็นตัวอย่างที่แสดงให้เห็นว่า SQLite ช่วยทำให้ฟอร์แมตไฟล์ของแอปพลิเคชันสร้างความสามารถอย่าง การบันทึกแบบ atomic, การเข้าถึง, การจัดการเวอร์ชัน และการกู้คืนได้ง่ายขึ้น

ขอบเขตและเป้าหมายของการทดลองทางความคิด

  • เป้าหมายคือ ODP (OpenDocument Presentation) ซึ่งเป็นฟอร์แมตเอกสารนำเสนอใน OpenDocument
  • จุดประสงค์ไม่ใช่การเปลี่ยน OpenDocument จริง ๆ แต่เป็นการพิจารณาวิธีใช้ SQLite เป็นคอนเทนเนอร์ในการออกแบบฟอร์แมตไฟล์ในอนาคต
  • ผลที่คาดหวังคือเอกสารมีขนาดเล็กลง, File/Save เร็วขึ้น, เริ่มต้นเร็วขึ้น, ใช้หน่วยความจำน้อยลง, จัดการเวอร์ชันเอกสารได้ และมอบประสบการณ์ผู้ใช้ที่ดีขึ้น

โครงสร้างไฟล์ ODP ปัจจุบัน

  • ไฟล์ ODP คือ ZIP archive ที่บรรจุไฟล์ XML และทรัพยากรรูปภาพ
  • ตัวอย่างเช่น ไฟล์งานนำเสนอ 49 สไลด์เกี่ยวกับ SQLite จาก SouthEast LinuxFest ปี 2014 มีรายการทั้งหมด 78 รายการในผลลัพธ์ zip -l
    • ไฟล์ XML สี่ไฟล์ ได้แก่ content.xml, styles.xml, meta.xml, settings.xml ใช้กำหนดเลย์เอาต์สไลด์ เนื้อหาข้อความ และสไตล์
    • ในไฟล์งานนำเสนอมี รูปภาพ 62 รูป ตั้งแต่ภาพเต็มหน้าจอไปจนถึงไอคอนขนาดเล็ก ซึ่งถูกเก็บเป็นไฟล์แยก
    • ไฟล์ mimetype มีบรรทัดเดียวคือ application/vnd.oasis.opendocument.presentation
  • ไฟล์ของโปรแกรมประมวลผลคำและสเปรดชีตใน OpenDocument ก็มีโครงสร้างคล้ายกัน แต่สิ่งที่วิเคราะห์คือ ODP

ข้อจำกัดของ ODP ที่ใช้ ZIP

  • ZIP archive คล้ายกับ ฐานข้อมูล key/value ที่ปรับให้เหมาะกับการเขียนครั้งเดียว อ่านหลายครั้ง และเหมาะกับโครงสร้างที่มี key จำนวนน้อยโดยแต่ละ key มีค่า BLOB ขนาดใหญ่
  • เนื่องจากอัปเดตรายการรายตัวได้ยาก เมื่อผู้ใช้เลือก File/Save โดยทั่วไปจึงต้องสร้าง ZIP archive ทั้งหมดขึ้นใหม่
    • แม้จะสามารถอัปเดตรายการรายตัวโดยไม่ทำให้เอกสารทั้งไฟล์เสียหายระหว่างไฟดับหรือแครชได้ แต่ก็ยากพอสมควรจนในทางปฏิบัติแทบไม่ถูกใช้
    • ในไฟล์งานนำเสนอขนาด 50MB แม้เปลี่ยนตัวอักษรเพียงตัวเดียวก็อาจต้องเขียนข้อมูลทั้ง 50MB ใหม่ทั้งหมด
  • เวลาเริ่มต้นอาจช้าลง
    • ODP เก็บเนื้อหาของทุกสไลด์ไว้ในไฟล์ XML ขนาดใหญ่ไฟล์เดียวชื่อ content.xml
    • LibreOffice ต้องอ่านและ parse ไฟล์นี้ทั้งหมดเพื่อแสดงสไลด์แรก
    • ดูเหมือนว่ารูปภาพทั้งหมดก็ถูกอ่านเข้าไปในหน่วยความจำด้วย ส่งผลให้เมื่อดับเบิลคลิกไฟล์ สิ่งที่ปรากฏไม่ใช่สไลด์แรก แต่เป็นแถบความคืบหน้า
  • ใช้หน่วยความจำมากขึ้น
    • โครงสร้าง ZIP ชักนำให้การใช้งานถูกออกแบบเป็นการอ่านเอกสารทั้งหมดเข้า memory ตอนเริ่มต้น แก้ไขใน memory แล้วเขียนเอกสารทั้งหมดลงดิสก์ตอนบันทึก
    • ไฟล์งานนำเสนอขนาด 50MB อาจใช้ RAM มากกว่า 200MB
    • หากเปิดไฟล์งานนำเสนอหลายไฟล์พร้อมกัน และใช้งานเบราว์เซอร์กับแอปเดสก์ท็อปไปด้วย อาจเกิดการ swapping ได้
  • การกู้คืนหลังแครชยุ่งยากขึ้น
    • ตระกูล OpenOffice จะสำรองเอกสารที่อยู่ใน memory เป็นระยะเพื่อรับมือกับการแครช
    • ระหว่างสำรอง แอปอาจค้างไปหลายวินาที และหลังเริ่มใหม่ต้องผ่านกล่องโต้ตอบกู้คืนแยกต่างหาก
  • การเข้าถึงเนื้อหาทำได้ยาก
    • แม้จะใช้เครื่องมือ ZIP ดึงรูปภาพออกมาได้ แต่การดึงหรือแก้ไขข้อความในสไลด์ด้วยเครื่องมือทั่วไปทำได้ยาก
    • content.xml ของไฟล์ตัวอย่างมีบรรทัดแรกเป็น XML declaration และบรรทัดที่สองมี XML 211,792 ตัวอักษร อยู่ในบรรทัดเดียว

การปรับปรุงแรก: แทนที่ ZIP ด้วย SQLite

  • ขั้นแรกคือโครงสร้างง่าย ๆ ที่เปลี่ยนรายการใน ZIP ให้เป็นแถวของตาราง SQLite
CREATE TABLE OpenDocTree(
  filename TEXT PRIMARY KEY,
  filesize BIGINT,
  content BLOB
);
  • ในขั้นนี้ไม่ได้เปลี่ยนโครงสร้างส่วนอื่นของฟอร์แมตไฟล์
    • ยังคงเป็นโครงสร้างแบบ “กองไฟล์” แต่แต่ละไฟล์กลายเป็นแถวในฐานข้อมูล SQLite แทนที่จะเป็น entry ใน ZIP
  • การเปรียบเทียบขนาดไฟล์ SQLite ที่นำเนื้อหาเดียวกับ self2014.odp ที่สร้างโดย NeoOffice มาบรรจุใหม่ด้วยยูทิลิตี SQLAR เป็นดังนี้
    • self2014.odp: 10,514,994 ไบต์
    • self2014.sqlar: 10,464,256 ไบต์
    • zip.odp ที่บีบอัดใหม่ด้วย zip บน command line: 10,416,644 ไบต์
  • ไฟล์ SQLite มีขนาดเล็กกว่า ODP ที่ NeoOffice สร้างประมาณ 0.5%
    • ไฟล์ ZIP ที่บีบอัดอย่างดีด้วย zip บน command line มีขนาดเล็กกว่า SQLite อีกประมาณ 0.5%
    • ฐานข้อมูล SQLite สามารถแข่งขันกับ ZIP archive ได้ในแง่ขนาด
  • SQLite มี การเขียนแบบ atomic จึงสามารถบันทึกการเปลี่ยนแปลงแบบเพิ่มส่วนได้โดยไม่เสี่ยงทำให้เอกสารเสียหายระหว่างแครชหรือไฟดับ
    • ข้อจำกัดที่ยังคงอยู่คือยังต้องเขียน content.xml ทั้งหมดใหม่
    • ถึงอย่างนั้น ไฟล์ที่เหลืออีก 77 ไฟล์ก็ปล่อยไว้เหมือนเดิมได้ ทำให้ File/Save เร็วขึ้นและลดปริมาณการเขียนบน SSD

การปรับปรุงที่สอง: แบ่งเนื้อหาเป็นชิ้นเล็ก ๆ

  • SQLite สามารถเก็บทั้งก้อนข้อมูลขนาดใหญ่และชิ้นข้อมูลเล็กจำนวนมากได้อย่างมีประสิทธิภาพ จึงสามารถมีตารางเนื้อหาแยกตามสไลด์ได้
CREATE TABLE slide(
  pageNumber INTEGER,
  slideContent TEXT
);
CREATE INDEX slide_pgnum ON slide(pageNumber);
  • เมื่อจะแสดงหน้าจอแรก แอปพลิเคชันต้องอ่านเฉพาะสไลด์แรกเท่านั้น
SELECT slideContent FROM slide WHERE pageNumber=1;
  • โครงสร้างนี้ทำให้ดึง parse และแสดงเฉพาะเนื้อหาของสไลด์แรกได้อย่างรวดเร็ว โดยไม่ต้องอ่าน content.xml ทั้งหมดตอนเริ่มต้น
  • ทางเลือกในการ implement ก็เพิ่มขึ้น
    • หลังแสดงสไลด์แรกแล้ว สามารถอ่านหน้าที่เหลือใน background thread ได้
    • สามารถเก็บเฉพาะสไลด์ปัจจุบันไว้ใน memory ได้
    • หรือเพื่อการเปลี่ยนสไลด์ที่รวดเร็ว อาจเก็บเฉพาะสไลด์ปัจจุบันและสไลด์ถัดไปไว้ใน memory ก็ได้
  • การบันทึกก็เขียนใหม่เฉพาะหน้าที่เปลี่ยนแปลงได้ จึงทำให้ File/Save เร็วยิ่งขึ้น
  • สำหรับข้อความสั้น ๆ ประสิทธิภาพการบีบอัดจะลดลงและอาจทำให้ขนาดเอกสารเพิ่มขึ้น
    • อย่างไรก็ตาม พื้นที่ส่วนใหญ่ของเอกสารถูกใช้โดยรูปภาพ ดังนั้นต้นทุนจากประสิทธิภาพการบีบอัดข้อความที่ลดลงจึงถือว่าน้อยเมื่อเทียบกับประสบการณ์ผู้ใช้ที่ดีขึ้น

การปรับปรุงที่สาม: การจัดการเวอร์ชัน

  • หากเก็บสไลด์เป็นวัตถุแยกกัน ก็สามารถบรรจุ ประวัติเวอร์ชัน ไว้ในเอกสารเดียวกันได้
CREATE TABLE slide(
  slideId INTEGER PRIMARY KEY,
  derivedFrom INTEGER REFERENCES slide,
  content TEXT
);
CREATE TABLE version(
  versionId INTEGER PRIMARY KEY,
  priorVersion INTEGER REFERENCES version,
  checkinTime DATETIME,
  comment TEXT,
  manifest TEXT
);
  • แต่ละสไลด์มี slideId ที่ไม่ซ้ำ แทนที่จะมีเลขหน้า และลำดับถูกกำหนดโดยรายการ slideId ที่เก็บไว้ใน manifest ของตาราง version
  • ตอนเริ่มต้น แอปพลิเคชันจะเลือกเวอร์ชันที่จะแสดงก่อน และโดยปกติสามารถดึงเวอร์ชันล่าสุดมาได้
SELECT manifest, versionId FROM version ORDER BY versionId DESC LIMIT 1;
  • สามารถใช้ query เพื่อดึงเวอร์ชันล่าสุดตาม checkinTime ได้เช่นกัน
SELECT manifest, versionId, max(checkinTime) FROM version;
  • ใน SQLite query max(checkinTime) ข้างต้นจะคืนผลลัพธ์ที่กำหนดไว้ชัดเจน แต่ในฐานข้อมูล SQL อื่น ๆ จำนวนมากอาจคืนผลลัพธ์ที่ไม่ถูกกำหนดไว้หรือเกิด error ได้
  • เมื่อผู้ใช้สั่ง File/Save สามารถเพิ่มเฉพาะสไลด์ที่แก้ไขเป็นแถวใหม่ในตาราง slide และสร้างแถว version ใหม่ที่มี manifest ที่แก้ไขแล้วได้
  • ตาราง version บันทึกเวลา check-in, คอมเมนต์ของผู้ใช้ และเวอร์ชันแม่ เพื่อรักษาประวัติการเปลี่ยนแปลง
  • ยังสามารถเก็บงานนำเสนอหลายชุดไว้ในเอกสารเดียวกันได้ด้วย
  • หากใช้เวอร์ชันพิเศษชื่อ pending แทนไฟล์สำรองแยกต่างหาก ก็สามารถบันทึกการเปลี่ยนแปลงที่ยังไม่ได้ save ได้บ่อย ๆ อย่างเงียบ ๆ
    • เพราะเขียนเฉพาะส่วนที่เปลี่ยน ไม่ใช่เอกสารทั้งหมด งานเขียนข้อมูลจึงเป็นระดับไม่กี่ KB ไม่ใช่หลาย MB
    • เวลาในการบันทึกอาจอยู่ในระดับมิลลิวินาที ไม่ใช่หลายวินาที
    • แม้รีบูตหลังแครช ก็สามารถรักษางานของผู้ใช้ไว้ได้ส่วนใหญ่หรือเกือบทั้งหมด
    • หากผู้ใช้ต้องการทิ้งการเปลี่ยนแปลงที่ยังไม่ได้ save ก็เพียงย้อนกลับไปเวอร์ชันก่อนหน้า

ความสามารถเพิ่มเติมที่เป็นไปได้ในฟอร์แมตไฟล์ SQLite

  • คอนเทนเนอร์ SQLite สามารถเพิ่มความสามารถสำคัญให้ฟอร์แมตไฟล์ของแอปพลิเคชันได้แม้ใช้เพียงสามตาราง
  • นอกจากนี้ยังสามารถใช้ schema, index, trigger, view และ constraint เพื่อเพิ่มประสิทธิภาพ ความสะดวก และความสอดคล้องได้
  • แนวคิดในการขยายมีดังนี้
    • เก็บ สแตก undo/redo อัตโนมัติ ไว้ในตารางฐานข้อมูล เพื่อให้ย้อนกลับไปถึง session การแก้ไขก่อนหน้าได้
    • เพิ่มความสามารถ full-text search ให้ slide deck หนึ่งชุดหรือหลายชุด
    • แยก settings.xml ออกเป็นตาราง SQL เพื่อให้แอปพลิเคชันอื่นดูและแก้ไขได้ง่ายขึ้น
    • แยก speaker notes ของแต่ละสไลด์ออกเป็นตารางต่างหาก เพื่อให้แอปหรือสคริปต์ third-party เข้าถึงได้ง่าย
    • รองรับโครงสร้างงานนำเสนอที่มีเส้นทางและทางเบี่ยงต่างกันตามปฏิกิริยาของผู้ชม นอกเหนือจากลำดับสไลด์แบบเส้นตรงธรรมดา

ความรู้สึกต่อต้าน SQLite ที่พบบ่อยและข้อโต้แย้ง

  • ประสบการณ์กับฐานข้อมูล SQL ระดับ enterprise อาจทำให้รู้สึกต่อต้านการใช้ SQLite เป็นฟอร์แมตไฟล์ของแอปพลิเคชัน
  • ฐานข้อมูล enterprise จำนวนมากแนะนำว่าไม่ควรใส่ string ขนาดใหญ่หรือ BLOB ไว้ในฐานข้อมูล แต่ให้เก็บเป็นไฟล์แยก ทว่า SQLite แตกต่างออกไป
    • คอลัมน์ใด ๆ ของ SQLite สามารถเก็บ string หรือ BLOB ได้ถึงประมาณ 1GB
    • สำหรับ string และ BLOB ขนาดไม่เกิน 100KB ประสิทธิภาพ I/O ดีกว่า การใช้ไฟล์แยก
  • ความคิดที่ว่า schema SQL ทุกแบบต้องเป็น รูปแบบปกติที่สาม (3NF) และต้องเก็บเฉพาะ primitive type ขนาดเล็ก ก็อาจเป็นข้อจำกัดได้
    • ทฤษฎี relational มีความสำคัญ แต่ในฟอร์แมตไฟล์จริง การเก็บข้อมูลซับซ้อนอย่าง XML หรือ JSON ไว้ใน text field ก็เป็นตัวเลือกที่ยอมรับได้

SQLite ในฐานะฟอร์แมตไฟล์ของแอปพลิเคชัน

  • ไฟล์ฐานข้อมูล SQLite มีขนาดเกือบเท่ากับ ZIP archive ที่บรรจุข้อมูลเดียวกัน และบางกรณีอาจเล็กกว่า
  • ด้วย การอัปเดตแบบ atomic จึงสามารถบันทึกการเปลี่ยนแปลงเล็ก ๆ ลงในเอกสารได้อย่างปลอดภัย ลด disk I/O และเพิ่มประสิทธิภาพ File/Save
  • แอปพลิเคชันสามารถอ่านเฉพาะเนื้อหาที่จำเป็นสำหรับหน้าจอแรก เพื่อลดเวลาเริ่มต้น
  • สามารถเก็บเฉพาะเนื้อหาที่เกี่ยวข้องกับสิ่งที่กำลังแสดงไว้ใน memory และปล่อยส่วนที่เหลือไว้บนดิสก์ ทำให้ลดการใช้หน่วยความจำได้มาก
  • schema SQL สามารถแสดงข้อมูลได้ตรงและกระชับกว่าโครงสร้าง key/value อย่าง ZIP
    • แอปและสคริปต์ third-party เข้าถึงได้ดีขึ้น
    • การทำฟีเจอร์ขั้นสูงอย่างการจัดการเวอร์ชันเอกสารในตัวและการกู้คืนงานหลังแครชทำได้ง่ายขึ้น
  • OpenDocument เป็นฟอร์แมตที่ได้รับการยอมรับและออกแบบมาอย่างดีอยู่แล้ว อีกทั้ง SQLite เกิดขึ้นภายหลัง OpenDocument ดังนั้นเนื้อหานี้ไม่ได้เป็นการวิจารณ์ตัวเลือกเดิม
  • เอกสาร Application File Format ให้แนวคิดเพิ่มเติมเกี่ยวกับการใช้ SQLite เป็นฟอร์แมตไฟล์ของแอปพลิเคชัน

1 ความคิดเห็น

 
GN⁺ 2023-09-19
ความเห็นบน Hacker News
  • กำลังทำแอปที่ใช้ SQLite เป็น รูปแบบไฟล์
    เพราะอยากคงโฟลว์ทั่วไปที่ไฟล์จะเปลี่ยนเฉพาะตอนผู้ใช้แก้ไขเอกสารแล้วกดบันทึก จึงคัดลอกเป็นฐานข้อมูล :memory: ตอนเปิดไฟล์: https://www.sqlite.org/inmemorydb.html
    ผู้ใช้แก้ไขได้ตามต้องการ และแอปสะท้อนการเปลี่ยนแปลงลงในรูปแบบฐานข้อมูลโดยตรงโดยไม่ต้องมีโมเดลเอกสารแยกต่างหาก ตอนบันทึกก็ใช้ VACUUM เขียนกลับไปเป็นไฟล์ฐานข้อมูลอีกครั้ง: https://www.sqlite.org/lang_vacuum.html
    ใช้งานได้ดีสำหรับไฟล์ขนาดพอเหมาะ และในแอปของฉันก็อยู่ในช่วงนั้นเสมอ

    • ไม่เข้าใจว่าทำไมต้องใช้ ฐานข้อมูลชั่วคราวแบบ volatile เป็นตัวช่วยด้วย ถ้าเป็นสถานการณ์ที่ผู้ใช้กำลังแก้ไขไฟล์ การเขียนคงไม่ถึง 1 ครั้งต่อวินาทีอยู่แล้ว ประโยชน์ด้านประสิทธิภาพก็คงไม่มาก
      สู้บันทึกอัตโนมัติลงฐานข้อมูลโดยตรงแล้วตัดปุ่มบันทึกออกไปเลยจะดีกว่า ทนต่อการชนกันได้ดี มีฐานข้อมูลเดียวทำให้โค้ดและบั๊กน้อยลง และการเขียนของ SQLite ก็มีแค่สำเร็จหรือไม่สำเร็จ ไม่มีสถานะค้างกลางทาง ในทางกลับกัน ตามที่เอกสารที่อ้างถึงระบุไว้ VACUUM INTO อาจทำให้ฐานข้อมูลเอาต์พุตไม่สมบูรณ์หรือเสียหายได้หากโปรแกรมปิดแบบไม่คาดคิดหรือไฟดับ
      ถ้าใช้ SQLite ตามวิธีที่มันตั้งใจไว้แต่แรก ก็ไม่ต้องกังวลเรื่องส่วนนี้ตลอดอายุการใช้งานของ SQLite
    • การทำงานเหมือนแอปทั่วไปหมายความว่าถ้าแอปตายหรือไฟดับ ก็จะสูญเสีย ข้อมูลที่ยังไม่ได้บันทึก
      การบันทึกไว้ในตำแหน่งชั่วคราวหลังแต่ละการทำงาน เช่น ตามไดเรกทอรี XDG อย่าง ~/.local/share/application/yourapp แล้วเมื่อผู้ใช้กดบันทึกค่อยคัดลอกไฟล์ไปยังตำแหน่งที่ต้องการ จะดีกว่ามาก หลังไฟดับแล้วเปิดแอปใหม่ก็จะกู้คืนได้เกือบถึงจุดเดิม และอาจเสียไปแค่ไม่กี่วินาทีสุดท้ายเท่านั้น
    • วิธีที่ง่ายกว่านั้นคือ ตอนเปิดฐานข้อมูลอาจแค่เปลี่ยนเป็น โหมด WAL แล้วปิด automatic checkpoint ก็ได้: https://www.sqlite.org/pragma.html#pragma_wal_autocheckpoint
      เมื่อผู้ใช้บันทึก ก็ทำ checkpoint เพื่อรวมเนื้อหาใน WAL เข้ากับฐานข้อมูลหลักก็พอ
    • ตามเอกสาร VACUUM จะคัดลอกเนื้อหาไปยังไฟล์ฐานข้อมูลชั่วคราวก่อน แล้วเขียนทับไฟล์ต้นฉบับ และตอนเขียนทับจะใช้ rollback journal หรือ WAL เหมือนทรานแซกชันทั่วไป ดังนั้นจึงต้องมีพื้นที่ว่างประมาณไม่เกินสองเท่าของไฟล์ต้นฉบับ
      VACUUM INTO จะใช้ไฟล์ที่ระบุใน INTO แทนฐานข้อมูลชั่วคราว และข้ามขั้นตอนคัดลอกกลับไปทับต้นฉบับ สิ่งสำคัญคือของที่ใช้อยู่จริงเป็น VACUUM ที่ทนต่อการตัดไฟได้ หรือเป็น VACUUM INTO ที่เปราะบางต่อไฟดับระหว่างเขียนและถ้าใช้ชื่อไฟล์เดิมก็ดูเหมือนอาจทำให้ไฟล์เสียหายได้
    • เคยใช้วิธีคล้ายกัน คือรันฐานข้อมูลในหน่วยความจำเป็นแคช แล้วบันทึกลงดิสก์เป็นระยะ ๆ โดยใช้ backup API: https://www.sqlite.org/backup.html
  • ปัญหาของ SQLite คือมันไม่ใช่ รูปแบบไฟล์มาตรฐาน
    มันมีเอกสารดีและเป็นที่เข้าใจกันกว้างขวาง แต่ไม่มีมาตรฐาน ISO ที่นิยามอย่างละเอียดว่าต้องตีความไฟล์ SQLite อย่างไร การทำ implementation ทดแทนก็เช่นกัน
    Zip และ XML มีพื้นผิว API เล็กกว่า SQLite มาก API ของ SQLite ไม่ได้มีแค่ฟังก์ชัน C ไม่กี่ตัว แต่รวมถึงภาษา SQL เองด้วย และการ implement parser ของ SQL, query optimizer, compiler, bytecode virtual machine, full-text search engine ฯลฯ โดยไม่ทำให้ข้อมูลเสียหาย เป็นงานที่ใหญ่กว่า XML parser มาก
    ถ้าเป็นแอปปิดเฉพาะโดเมนที่ไม่ให้ความสำคัญกับ interoperability หรือมาตรฐาน ISO SQLite ก็เป็นรูปแบบไฟล์ที่ดี แต่เข้าใจว่าใน OpenOffice เคยมีความกังวลแบบนั้นจริง ๆ

    • ไม่ชัดเจนว่าปัญหานี้หมายถึงอะไร รูปแบบไฟล์ SQLite เป็น public domain และมีเอกสารดี มี parser หลายภาษาอยู่แล้ว
      ไลบรารี SQLite C ก็เป็น public domain ซอร์สเปิดทั้งหมด จัดการรูปแบบไฟล์ได้ และมีระดับเอกสารดีกว่ามาตรฐาน ISO ส่วนใหญ่ด้วยซ้ำ binding สำหรับภาษาหลัก ๆ ก็มีแทบครบ
      ถ้าปัญหาคือรูปแบบ OpenDocument บางอย่างที่จะถูกเก็บไว้ในไฟล์ SQLite ยังต้องถูกสร้างและจัดทำเอกสารขึ้นมาก่อน นั่นก็เป็นอีกเรื่องหนึ่ง มาตรฐาน ISO เป็นสิ่งที่ดี แต่ถ้าต้องรอให้ ISO นิยามรูปแบบไฟล์ก่อน เราคงมีของให้ใช้ได้น้อยเกินไปมาก
    • การจะเป็นรูปแบบไฟล์มาตรฐาน ไม่จำเป็นต้อง implement parser ของ SQL, query optimizer, compiler, bytecode virtual machine และ full-text search engine ทั้งหมด
      ก็เหมือนกับที่ไม่จำเป็นต้อง implement ฟังก์ชันสเปรดชีตทั้งหมดเพื่ออ่านสเปรดชีตของ LibreOffice สิ่งที่ต้องมีคือความสามารถในการประกอบตารางกลับขึ้นมา และหลังจากนั้นข้อมูลที่ต้องการก็ไล่ดูได้ด้วยโค้ด imperative ที่เขียนในภาษาที่เลือก
    • เรื่องนี้ไม่เป็นปัญหาแม้แต่กับ หอสมุดรัฐสภาสหรัฐฯ หอสมุดรัฐสภากำหนดให้ SQLite เป็นรูปแบบจัดเก็บที่แนะนำสำหรับชุดข้อมูล ร่วมกับ CSV, XML และ JSON
    • ดูเหมือนจะปนกันระหว่างรูปแบบไฟล์กับวิธีใช้งานมัน แอปที่ใช้รูปแบบไฟล์ SQLite ก็แค่ใช้ไลบรารี SQLite เป็นส่วนหนึ่งของแอป
      การ implement ไลบรารีนั้นใหม่คงเป็นเรื่องใหญ่ แต่ก็เป็นงานประเภทเดียวกับการ implement โค้ดที่ใช้รูปแบบไฟล์ OpenDocument ใหม่ ตัวรูปแบบไฟล์เองค่อนข้างเรียบง่าย
    • มาตรฐานไม่จำเป็นเสมอไป การโต้ตอบทั้งหมดระหว่างแอปพลิเคชันกับเอกสารเกิดขึ้นผ่าน SQL และ SQL ก็มีมาตรฐานอย่างน้อยในส่วนสำคัญ ๆ
      ถ้ากังวลเรื่องความเข้ากันได้ ก็ทำให้เอกสารสามารถเข้าถึงผ่านฐานข้อมูลอื่นอย่าง MySQL ได้ด้วยก็ได้
  • ตอนแรกคิดว่าเมื่อ Audacity หันมาใช้ SQLite ฟีเจอร์การบันทึกไฟล์จะดีขึ้นมาก แต่ในทางปฏิบัติมีหลุมพรางเยอะ
    บน Linux ถ้าบันทึกเป็นไฟล์ใหม่ลงในเมานต์ NTFS ที่ root เป็นเจ้าของแต่ทุกคนเขียนได้ ซึ่งสร้างไว้ด้วย /etc/fstab จะล้มเหลวด้วยเหตุผลอย่างข้อผิดพลาดเรื่องสิทธิ์ แต่ถ้าบันทึกทับไฟล์เดิมจะทำงานปกติ
    ทันทีที่เริ่มแก้ไขโปรเจกต์ ไฟล์บนดิสก์ก็ถูกแก้ไข ทำให้ถ้าใส่โปรเจกต์ Audacity ไว้ใน Git ในรูปก้อนข้อมูลไบนารี จะเกิด diff ของ Git ที่ไม่จำเป็น แม้จะบันทึกแล้ว ข้อมูลเก่าหรือข้อมูลที่ลบไปก็ยังค้างอยู่ในไฟล์ SQLite จนกว่าจะปิดหน้าต่างโปรเจกต์ ดังนั้นถ้าไม่ปิดหน้าต่างก่อน commit มันอาจเข้าไปอยู่ใน repository ได้ เท่าที่จำได้ เมื่อก่อนต้อง VACUUM ไฟล์ .aup3 เอง แต่ตอนนี้แค่ปิดหน้าต่างก็พอ ให้ความรู้สึกเหมือน Fast Save ของ Word 2003

    • ถ้า Audacity แครชหรือปิดผิดปกติ จะไม่มีการเก็บกวาดเกิดขึ้นเลย จึงยุ่งยาก เมื่อก่อนในขั้นตอนกู้คืนจะแจ้งว่ามีบล็อกกำพร้าอยู่ และให้เลือกว่าจะเก็บไว้หรือลบทิ้ง
      ตอนที่โปรเจกต์ซึ่งควรมีขนาดไม่กี่ร้อย MB กลายเป็นหลาย GB และต้องประหยัดพื้นที่ดิสก์ สำหรับงานแทร็กเดียวแบบง่าย ๆ Mix and Render คือทางออก มันไม่เปลี่ยนเสียง แต่ช่วยเก็บกวาดเศษข้อมูลตอนบันทึกและปิดได้
      เรื่องนี้ไม่ใช่ปัญหาของ SQLite เอง แต่เป็นปัญหาระดับแอปพลิเคชันอย่างชัดเจน Audacity 2 ดูเหมือนจะมีแนวคิดเรื่องพื้นที่ทำงานชั่วคราว แต่ Audacity 3 ดูเหมือนจะใช้ไฟล์ .aup3 เองเป็นพื้นที่ทำงาน
      เคยลองดูรูปแบบของ Audacity 3 แล้วพบว่า แม้จะเก็บข้อมูลโปรเจกต์ที่เทียบเท่ากับไฟล์ .aup เดิมไว้เป็น XML ในตารางแถวเดียว แต่กลับไม่ได้เขียนเป็นข้อความตรง ๆ และเข้ารหัสด้วยตัวเข้ารหัสพจนานุกรมแบบง่าย ๆ ซึ่งน่างงมาก ทำให้การทำงานร่วมกันและการตรวจสอบยากขึ้นมาก มีผลเสียต่อประสิทธิภาพเล็กน้อย และการประหยัดพื้นที่ก็คงเป็นแค่ระดับไม่กี่ KB ซึ่งเป็นความคลาดเคลื่อนจากการปัดเศษเมื่อเทียบกับไฟล์เสียงขนาดหลายร้อย MB
    • ต้องจำลองพฤติกรรมที่ผู้ใช้คาดหวังให้เหมือนเดิม ทุกอย่างควรถูกบันทึกลงใน ไฟล์ชั่วคราว และควรเขียนทับไฟล์ต้นฉบับเฉพาะเมื่อมีการสั่งบันทึกอย่างชัดเจนเท่านั้น
      จากมุมมองของ Git การใช้รูปแบบข้อความที่ diff และ merge ได้ง่ายจะได้เปรียบกว่า ไม่รู้ว่า dump ของ SQLite จะง่ายในแง่นี้แค่ไหน
    • ภรรยาผมใช้ Audacity ทั้งวัน และทุก ๆ ไม่กี่วันจะเกิด ไฟล์ SQLite เสียหาย มีข้อผิดพลาดคีย์ซ้ำ และใน Audacity ก็ไม่รู้วิธีซ่อมหรือนำกลับเข้าใหม่
      ถ้าสำคัญจริง ๆ ก็พอจะแก้ด้วยมือได้ แต่ปกติแค่ทิ้งไฟล์นั้นไปก็กลับมาใช้ได้
  • เป็นบทความที่ดี อย่างไรก็ตาม ผมชอบตรงที่ OpenDocument เป็นชุดไฟล์ XML ภายใน Zip archive
    สามารถสร้างเอกสารอย่างสเปรดชีตได้ค่อนข้างง่าย แม้ไม่มีไลบรารีหนัก ๆ ที่เข้าใจรูปแบบเอกสาร
    บางครั้งผู้ใช้เว็บเซอร์วิสต้องการใช้ข้อมูลที่ส่งออกเป็นแถวตารางกับเครื่องมือหลายแบบ UTF-8 CSV เปิดกว้าง เป็นธรรมเนียมที่ใช้กัน และพอใช้ได้ แต่ใครที่เคยให้ CSV กับผู้ใช้ปลายทางคงรู้ดีถึงความเจ็บปวดเมื่อไปติดกับแอปสเปรดชีต
    ผมบันทึกสเปรดชีตตัวอย่างเป็น ODS ของ OpenDocument และ XLSX ของ OOXML ซึ่งเป็นสัตว์ประหลาด XML ของ Microsoft แล้วทำความเข้าใจพื้นฐานของรูปแบบ XML เท่านั้น จากนั้นลด Zip archive ให้เหลือเฉพาะส่วนจำเป็น ทำเครื่องหมายตำแหน่งที่จะใส่เนื้อหาไว้ และสร้างไฟล์สเปรดชีตใหม่เมื่อมี request ตอนนี้จึงส่งออกข้อมูลเดียวกันได้เป็น CSV, ODS, XLSX และ JSON
    SQLite ก็ทำได้ แต่จะซับซ้อนกว่าเล็กน้อยและทำให้พัฒนาช้าลง การที่สามารถสร้างเอกสารเทมเพลตด้วยชุดโปรแกรมออฟฟิศแล้วขุดเข้าไปใน XML ของไฟล์ที่บันทึกได้ เป็นฟีเจอร์เฉพาะทางแต่ดี
    โดยเฉพาะ Excel ใน locale อย่าง nl_NL มีปัญหา เพราะมันทำเหมือน hardcode ว่าตัวคั่นคอลัมน์ของไฟล์ CSV คือเซมิโคลอน เนื่องจาก Microsoft ตัดสินใจอย่างฉาวโฉ่วว่าชาวดัตช์ไม่ใช้จุลภาคในไฟล์ comma separated values

    • พฤติกรรมนั้นไม่ได้ hardcode ตายตัวเสียทีเดียว แต่ขึ้นกับค่า localeconv()->decimal_point ถ้าค่าเป็น , Excel จะใช้ เซมิโคลอน ทั้งในไฟล์ CSV และภาษาสำหรับเขียนสูตร
      เมื่อก่อนตอนเปิด CSV/TXT ใน Excel สามารถตั้งค่าได้ และใน LibreOffice ยังทำได้อยู่ แต่ในกระบวนการทำให้ UI เรียบง่ายขึ้นโดยรวม มันถูกย้ายไปอยู่ที่ไหนสักแห่งในเมนู/แท็บริบบอน Data ต้องเปิดเวิร์กบุ๊กใหม่แล้วหา option ที่ถูกต้อง และถ้าอยากประหยัดเวลา ใช้ LibreOffice จะดีกว่า
  • ส่วนนี้น่าทึ่งจริง ๆ ไม่น่าเชื่อว่าไม่ต้องใช้ nested query
    SELECT manifest, versionId, max(checkinTime) FROM version;
    ว่ากันว่าใน SQLite query ที่สองนี้ซึ่งใช้ max(checkinTime) ทำงานได้จริงและคืนคำตอบที่กำหนดไว้ชัดเจน ในเอนจินฐานข้อมูล SQL อื่น ๆ จะคืนคำตอบที่ไม่กำหนดหรือเกิดข้อผิดพลาด แต่ใน SQLite จะคืน manifest และ versionId ของรายการที่มี checkinTime สูงสุด

    • อาจเป็นฟีเจอร์ที่มีประโยชน์ แต่พูดตรง ๆ ผมไม่คาดหวังว่า query แบบนั้นจะคืนผลแบบนั้น
      กรณีนี้ไม่จำเป็นต้องใช้ nested query แค่เรียงตาม checkinTime แล้วจำกัดให้เหลือหนึ่งรายการก็พอ: select manifest, versionId, checkinTime from version order by checkinTime desc limit 1
      อย่างน้อยก็น่าจะใช้ได้ใน SQLite และ PostgreSQL ส่วน Oracle เท่าที่จำได้ต้องใช้ where rownum=1 เลยเคยต้องใช้ nested query
    • มองได้คร่าว ๆ ว่าเป็นรูปย่อของ GROUP BY manifest, versionId ORDER BY 3 DESC LIMIT 1 หรือ CTE ที่หา checkinTime สูงสุดแล้ว join กลับ
      อย่างไรก็ตาม ถ้ามีหลายแถวที่มีค่า checkinTime สูงสุดเท่ากัน ก็มีความสุ่มอยู่ จึงอาจกลายเป็น ปืนจ่อเท้าตัวเอง ได้ เลยไม่ค่อยใช้ฟีเจอร์เฉพาะของ SQLite3 แบบนี้ ถ้าต้องเลือกแถวที่ดีที่สุดอย่างกำหนดแน่นอน ต้องใช้วิธีที่ชัดเจนคล้ายกับข้างบน
    • ไม่ใช่พฤติกรรมที่โดยทั่วไปคาดหวังใน SQL แต่ SQLite มักออกนอกความคาดหมายอยู่บ่อย ๆ กรณีนี้สะดวก แต่เป็น นอกมาตรฐาน
    • ดูเหมือนผลข้างเคียงที่สะดวกจากการ implement ภายหลังกลายเป็นพฤติกรรมทางการ คล้ายกับลำดับ key ของ dictionary ใน Python
      ใน Postgres สามารถทำสิ่งคล้ายกันได้ด้วย query DISTINCT ON เป็นหนึ่งในงานที่ดูเรียบง่ายใน SQL แต่ผมรู้สึกว่ายากที่สุดงานหนึ่ง
    • ค่อนข้างน่าทึ่งที่สามารถอ้างได้ว่านี่เป็นพฤติกรรมที่กำหนดไว้แล้ว เพราะ manifest และ versionId ไม่ได้ ขึ้นต่อเชิงฟังก์ชัน กับ max(checkinTime)
      เช่น อาจมีสองแถวที่มีค่า checkinTime เดียวกัน และค่านั้นเป็นค่าสูงสุด
  • เคยเปิดตัวผลิตภัณฑ์ที่ใช้ทั้ง SQLite และไฟล์ XML
    หนึ่งในส่วนที่ปรับปรุงคือการย้ายบางตารางที่มีข้อมูลน้อยไปไว้ในไฟล์ XML เนื่องจากไฟล์มีขนาดเล็กและแทบไม่ถูกใช้งาน ทำให้เลเยอร์การเข้าถึงข้อมูลและการวินิจฉัยปัญหาง่ายขึ้น และทำเป็น XML แบบหลายบรรทัดพร้อมการเยื้องด้วยแท็บ
    การขอให้เจ้าหน้าที่เทคนิคที่ต้องวินิจฉัยผลิตภัณฑ์เปิดดูฐานข้อมูล SQLite ถือเป็นภาระค่อนข้างมาก แต่ในส่วนหลักของผลิตภัณฑ์ SQLite เหนือกว่าไฟล์ XML อย่างท่วมท้น เวอร์ชันก่อนหน้าใช้ไฟล์ XML แต่ไฟล์ XML ไม่มีวิธีที่ดีสำหรับการ อัปเดตแบบ incremental จึงมีปัญหาด้าน scalability
    ข้อดีของ XML ที่มนุษย์อ่านง่ายจะทำงานได้ดีจริงก็ต่อเมื่อไฟล์มีขนาดเล็กและการออกแบบ schema ถูกปรับให้เหมาะกับ XML ที่อ่านง่ายเท่านั้น การต้องเขียนไฟล์ XML ทั้งหมดใหม่ทุกครั้ง รวมถึงความซับซ้อนที่เกิดขึ้นเมื่อมีฟีเจอร์มากขึ้น บั่นทอนข้อดีที่ใหญ่ที่สุดของ XML อย่างรวดเร็ว
    กรณีที่ผู้ใช้ทั่วไปจำเป็นต้องแก้ไขภายในเอกสารออฟฟิศโดยตรงนั้นพบได้น้อยพอสมควร ดังนั้นการเรียนรู้วิธีใช้ SQLite reader จึงเป็นอุปสรรคในการเริ่มต้นที่ยอมรับได้ ข้อจำกัดของ XML+Zip ในการเขียนแบบสุ่มตรงกลางไฟล์นั้น แม้แต่กฎของมัวร์ก็เอาชนะไม่ได้

    • ไม่แน่ใจว่ารูปแบบ native ของ SQLite ทำให้ได้ ขนาดใกล้เคียงกับ XML+Zip โดยไม่ใช้ Zip ได้อย่างไร สงสัยว่าฟิลด์ TEXT หรือ BLOB ของ SQLite ถูกบีบอัดหรือไม่ หรือถือว่าผู้เรียกจะบีบอัด BLOB ก่อนเขียนเอง
  • ODT ถูกออกแบบมาโดยคำนึงถึงการทำให้เป็นมาตรฐาน รูปแบบก่อนหน้าก็คล้ายกันมาก แต่พึ่งพามาตรฐานเดิมอย่าง XHTML, SVG, CSS อย่างมาก
    หากไม่สามารถอ้างอิงมาตรฐานเดิมได้ ข้อกำหนดของ ODT เองก็คงจะใหญ่โตขึ้นทันที ความพยายามในการปรับปรุงมาตรฐานก็ดูไม่น้อย และในช่วงไม่กี่ปีที่ผ่านมาก็แทบไม่มีความคืบหน้า
    ในทางปฏิบัติ รูปแบบ SQLite อาจนำเสนอเป็นตัวเลือกได้ แต่ดูเหมือนว่าเรือของรูปแบบเอกสารออฟฟิศได้ออกจากท่าไปแล้ว อย่างไรก็ดี นี่เป็นเหตุผลสนับสนุนที่ดีต่อข้อเสนอให้จัดทำข้อกำหนด SQLite เป็น มาตรฐานทางการ

    • ข้อกำหนดเขียนไว้กระชับมาก และแม้จะกำหนดหลัก ๆ แค่ไวยากรณ์มากกว่าผลลัพธ์และพฤติกรรม แต่ก็ยังหนาถึง 840 หน้า
      หากไม่นับข้อบกพร่องบางอย่าง เช่น ปัญหาที่แอตทริบิวต์ ooo:rsid ทำให้ local style และช่วงข้อความเพิ่มจำนวนพรวดพราด, สเปรดชีตที่ไม่ sparse, และกลไกแปลก ๆ ของการจัดสไตล์ตาราง นี่เป็นมาร์กอัปที่ออกแบบมาดีมากสำหรับข้อมูลเอกสารแบบนี้ มีสมดุลที่ดีระหว่าง semantic markup กับการแสดงผลที่ผู้ใช้ต้องการทำจริง
      ในทางกลับกัน Office OpenXML มีแท็กว่างสำหรับการจัดรูปแบบที่มีสถานะ และใน DOCX มันจะ toggle ว่าข้อความถัดไปจะแสดงเป็นตัวหนาหรือไม่
  • การผูกฟอร์แมตไฟล์เข้ากับ SQLite มันให้ความรู้สึกเหมือนมีอะไรไม่ถูกต้อง
    SQLite นั้นดี แต่ในพื้นที่นี้ค่อนข้างมีลักษณะเฉพาะ เพราะมันทำหลายอย่างมาก จึงยากที่จะลอกเลียนแบบได้ตรง ๆ
    แต่ในกรณีนี้ ถ้าถามว่าจำเป็นต้องมีฟังก์ชันมากขนาดนั้นไหม ก็ไม่จำเป็น ต้องการแค่ semantics ของทรานแซกชันที่พื้นฐานและปลอดภัย กับความสามารถในการเก็บโครงสร้างตารางแบบง่าย ๆ เท่านั้น ไม่จำเป็นถึงขั้นมาตรฐาน SQL ทั้งหมดหรือ query optimizer
    อาจมีฟอร์แมตไฟล์ที่ดีกว่านี้ได้ แต่ถ้าเป็น ฟอร์แมตที่แยกจาก SQLite ก็น่าจะดีกว่า

    • ผมไม่เห็นว่าทำไมจะไม่ได้: https://www.sqlite.org/appfileformat.html
      ขนาดต่ำกว่า 1MB และ https://sqlite.org/footprint.html แม้เปิดฟีเจอร์ทั้งหมดก็ 750KB: https://www.sqlite.org/about.html
      ตอนคอมไพล์สามารถตัดฟีเจอร์ออกได้มากพอสมควร และดูเหมือนจะมีตัวเลือกสำหรับปรับหรือย่อ query planner ด้วย: https://www.sqlite.org/compile.html
      แถมยังมีคำกล่าวว่า “SQLite ไม่ได้แข่งขันกับฐานข้อมูลแบบ client/server แต่ SQLite แข่งขันกับ fopen()”: https://www.sqlite.org/whentouse.html
      สุดท้ายสิ่งที่ต้องการไม่ใช่ตัวฐานข้อมูลเอง แต่เป็นไลบรารีที่ให้ API และพฤติกรรมแบบฐานข้อมูล
    • ประเด็นเรื่องทรานแซกชัน โดยเฉพาะการเข้าถึงไฟล์พร้อมกัน ยากกว่าที่คิด ตอนนั้น การจัดการ SQLITE_BUSY ค่อนข้างลำบาก
      ผมรู้ว่าในการจัดการทรานแซกชัน ความล้มเหลวจาก serialization เป็นสิ่งที่คาดได้ แต่ใน SQLite แยกความล้มเหลวถาวรอย่างภาวะ deadlock กับตัวเอง ออกจากปัญหาการอัปเดตพร้อมกันแบบชั่วคราวได้ยาก ถ้าเป็นความล้มเหลวชั่วคราว ก็แค่รัน closure ที่นิยามงานของทรานแซกชันใหม่ แต่ถ้าเป็นความล้มเหลวถาวร ก็ไม่มีความหมาย
      ส่วนหนึ่งของปัญหาคือ sqlite3_stmt รวมลักษณะของทั้ง prepared statement และ result set ไว้ด้วยกัน เรามักถือมันไว้นานเพื่อแคช bytecode ที่คอมไพล์แล้ว แต่ถ้าหยุดกลางการวนซ้ำ มันอาจถือ lock อยู่ ณ จุดนั้น ส่งผลให้เกิดความล้มเหลวในการอัปเกรด lock ที่ไม่คาดคิดได้
      สุดท้ายผมใช้ sqlite3_next_stmt, sqlite3_stmt_busy, sqlite3_sql ทำรายงานข้อผิดพลาดแบบละเอียดแล้วกำจัดปัญหาไปได้ แม้เป็นงานใช้ส่วนตัว โค้ด retry ทรานแซกชันก็ยังเต็มไปด้วย logging แบบเลือกเปิดได้และคอมเมนต์ ส่วน logic retry ทรานแซกชันสำหรับ PostgreSQL ง่ายกว่ามาก
      อีกเรื่องที่ทำให้ประหลาดใจคือเอกสารระบุว่าในโหมด WAL ที่ synchronous=NORMAL ทรานแซกชันที่ commit แล้วอาจถูก rollback หลังไฟดับหรือระบบ crash ได้: https://sqlite.org/pragma.html#pragma_synchronous
      สำหรับแอปพลิเคชันของผมไม่เกี่ยวข้อง
    • SQLite ถูกใช้เพื่อจุดประสงค์แบบนี้อยู่แล้วจริง ๆ ใช้เป็น OGC GeoPackage และชุดข้อมูลของ Mapbox/Maptiler ก็ใช้ด้วย
    • ฟอร์แมตบางอย่างถูกออกแบบมาเพื่อ การแลกเปลี่ยน เป็นหลัก ข้อโต้แย้งฝั่ง SQLite คือให้เจ้าของแอปบังคับฟอร์แมต SQLite กับผู้ใช้จนกลายเป็นมาตรฐานโดยพฤตินัย และละเว้นงานทำให้เป็นมาตรฐานทางกฎหมาย
      ถ้า Richard Hipp และบริษัทแสดงให้เห็นมาตรฐาน SQLite แบบ ISO/IEC/ANSI/ETSI ที่จะไม่หลุดออกนอกกรอบอย่างเด็ดขาด, การตรวจสอบทางกฎหมายว่าไม่มีสิทธิบัตรใด ๆ ที่จะส่งผลกระทบ, และ implementation SQLite ที่เข้ากันได้หลายตัวโดยยังรักษาข้อดีทั้งหมดไว้ได้ ถึงตอนนั้นค่อยคุยกันเรื่องการแนะนำให้ใช้เป็นฟอร์แมตไฟล์ มิฉะนั้นก็เท่ากับบอกให้ผูกพึ่งพา implementation จากแหล่งเดียวอย่างหนัก แล้วผลักภาระนั้นไปให้ผู้ใช้ด้วย
      XML, ASN.1, JFIF เป็นมาตรฐานทางการ และ ZIP ก็เป็นมาตรฐานทางการที่ถูกนำมาใช้เป็น ISO/IEC 21320-1:2015 ในกระบวนการทำมาตรฐาน OpenDocument
      สิ่งสำคัญที่สุดของเอกสารคือทุกคนอื่นต้องอ่านได้ การลดเวลาอัปเดตดิสก์เป็นเรื่องรอง เราไม่ควรไม่ได้เรียนรู้อะไรจากกรณีที่ Microsoft พยายามบิดเบือนองค์กรกำหนดมาตรฐานเพื่อรักษาการผูกขาด: https://arstechnica.com/uncategorized/2008/10/norwegian-standards-body-implodes-over-ooxml-controversy/
    • ถ้าดูแอปของ Apple ส่วนใหญ่ใช้ SQLite เป็นฟอร์แมตจัดเก็บข้อมูล เช่น iMovie, iPhoto, Voice recording และ Docker ก็เช่นกัน
      มันคงไม่ใช่ตัวเลือกที่ผิดได้ขนาดนั้น
  • อีกตัวอย่างคือไทล์แผนที่แบบ raster จริง ๆ แล้วมันคือรูปภาพสี่เหลี่ยมเล็ก ๆ ที่อาจมีได้ถึงหลักล้านไฟล์
    ผมลองทั้ง Zip, tar, file system และ SQLite แล้ว SQLite เร็วที่สุดและเล็กที่สุด ดีกว่า archive ปกติที่ไม่มี overhead เสียอีก

    • file system หลายตัวมีปัญหาเมื่อมีไฟล์มากกว่าหลักหมื่นในไดเรกทอรีเดียว ซึ่งในกรณีไทล์แผนที่ก็เกิดสถานการณ์แบบนั้นพอดี จึงไม่น่าแปลกใจที่ SQLite เร็วกว่า
    • ถ้า SQLite เร็วกว่า ปัญหาก็อยู่ที่ ไลบรารี Zip ที่ใช้
      SQLite มีข้อเสียใหญ่ BLOB ที่ได้จากฐานข้อมูลไม่สามารถ mmap ได้ จึงต้องคัดลอกไปที่อื่น ส่วนไฟล์ Zip ถ้าไม่ได้บีบอัด หรือถูกบีบอัดด้วย encoding แปลก ๆ อย่าง PVRTC ก็สามารถ mmap ได้ตรง ๆ
  • OpenDocument คือรูปภาพที่ถูกบีบอัดกับ XML กล่าวคือสุดท้ายก็หมายถึงต้องพาร์สรูปแบบทั้งหมดแล้วโหลดขึ้นหน่วยความจำ
    ไม่ค่อยแน่ใจว่า SQLite จะปรับปรุงเรื่องนี้ได้อย่างไร XML อาจไม่ใช่อุดมคติ แต่ถูกบีบอัดด้วย Zip อยู่แล้ว จึงไม่ได้มีโทษด้านขนาดมากนัก
    ข้อดีทั้งหมดที่บทความ SQLite ยกมา สามารถทำได้หากใช้ SQLite เป็นโมเดลรันไทม์ของเอกสาร ทำได้ทั้งบนดิสก์และในหน่วยความจำ แต่ SQLite ไม่จำเป็นต้องเป็น รูปแบบสำหรับการส่งต่อ
    ตรงกันข้าม SQLite อาจมีขนาดใหญ่กว่ารูปแบบปัจจุบันด้วยซ้ำ เมื่อผ่านการแก้ไขจะเกิดพื้นที่ที่ไม่ได้ใช้ แตกกระจาย และอาจกลายเป็นไฟล์โปร่งได้ หากต้อง optimize ทุกครั้ง ข้อดีอย่างการบันทึกอย่างรวดเร็วก็จะหายไป
    รูปแบบที่ต้องการเดลตาอัปเดตและการค้นหาดัชนีอย่างรวดเร็ว และไม่ควรโหลดไฟล์ทั้งก้อนขึ้นหน่วยความจำ จริง ๆ แล้วมักใช้ SQLite เป็นรูปแบบไฟล์กันมาก เพียงแต่รู้สึกว่า OpenDocument เป็นตัวอย่างที่เลือกมาไม่ดีสำหรับการเล็งใช้ SQLite ในสถานการณ์สมมตินี้

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