SQLite Wasm ในเบราว์เซอร์ที่สนับสนุนโดยระบบไฟล์ส่วนตัวต้นทาง

ใช้ SQLite เพื่อจัดการพื้นที่เก็บข้อมูลทั้งหมดบนเว็บได้อย่างมีประสิทธิภาพ

เกี่ยวกับ SQLite

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

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

โลโก้ SQLite

SQLite ที่อิงตาม Web Assembly

มีเวอร์ชัน SQLite ที่ไม่เป็นทางการจำนวนมากที่อิงตาม Web Assembly (Wasm) ทำให้ใช้ในเว็บเบราว์เซอร์ได้ เช่น sql.js โปรเจ็กต์ย่อย sqlite3 WASM/JS เป็นความพยายามแรกที่มีการเชื่อมโยงอย่างเป็นทางการกับโปรเจ็กต์ SQLite เพื่อสร้างบิลด์ Wasm ของไลบรารีที่สมาชิกสร้างขึ้นในชุดการนำส่ง SQLite ที่รองรับ เป้าหมายที่เป็นรูปธรรมของโปรเจ็กต์นี้ ได้แก่

  • การเชื่อมโยง API sqlite3 ระดับต่ำซึ่งใกล้เคียงกับ sqlite3 มากที่สุด ในแง่ของการใช้งาน
  • API ระดับออบเจ็กต์ระดับสูงขึ้น คล้ายกับ sql.js และการติดตั้งใช้งานสไตล์ Node.js ซึ่งสื่อสารกับ API ระดับต่ำโดยตรง ต้องใช้ API นี้จากเทรดเดียวกันกับ API ระดับล่าง
  • API ที่อิงกับผู้ปฏิบัติงาน ซึ่งจะพูดกับ API ก่อนหน้าผ่านข้อความของ Worker โดย API นี้มีไว้สำหรับใช้ในเทรดหลัก โดยมี API ระดับล่างติดตั้งไว้ในเทรดผู้ปฏิบัติงาน และพูดคุยกับพวกเขาผ่านข้อความของผู้ปฏิบัติงาน
  • Worker API ตัวแปรแบบ Promise ซึ่งจะซ่อนแง่มุมต่างๆ ของการสื่อสารแบบข้ามชุดข้อความจากผู้ใช้อย่างสิ้นเชิง
  • การรองรับพื้นที่เก็บข้อมูลฝั่งไคลเอ็นต์แบบถาวรโดยใช้ JavaScript API ที่พร้อมใช้งาน ซึ่งรวมถึงระบบไฟล์ส่วนตัวต้นทาง (OPFS)

การใช้ SQLite Wasm กับแบ็กเอนด์ความต่อเนื่องของระบบไฟล์ส่วนตัวต้นทาง

การติดตั้งไลบรารีจาก npm

ติดตั้งแพ็กเกจ @sqlite.org/sqlite-wasm จาก npm ด้วยคำสั่งต่อไปนี้

npm install @sqlite.org/sqlite-wasm

ระบบไฟล์ส่วนตัวต้นทาง

ระบบไฟล์ส่วนตัวต้นทาง (OPFS ซึ่งเป็นส่วนหนึ่งของ File System Access API) ได้รับการเพิ่มประสิทธิภาพด้วยแพลตฟอร์มพิเศษที่ช่วยให้เข้าถึงข้อมูลได้อย่างมีประสิทธิภาพมากขึ้น แพลตฟอร์มใหม่นี้แตกต่างจากเดิมด้วยการให้สิทธิ์เข้าถึงการเขียนแบบเฉพาะตัวสำหรับเนื้อหาของไฟล์ การเปลี่ยนแปลงนี้ พร้อมกับความสามารถในการอ่านการแก้ไขที่ยกเลิกการล้างอย่างสม่ำเสมอและความพร้อมใช้งานของตัวแปรแบบพร้อมกันสำหรับผู้ปฏิบัติงานที่ได้รับมอบหมายโดยเฉพาะ ซึ่งช่วยเพิ่มประสิทธิภาพได้อย่างมากและเป็นการเลิกบล็อกกรณีการใช้งานใหม่ๆ

คุณคงพอจะนึกออกว่าจุดสุดท้ายของเป้าหมายของโครงการ การสนับสนุนพื้นที่เก็บข้อมูลฝั่งไคลเอ็นต์แบบถาวรโดยใช้ JavaScript API ที่มีอยู่นั้นมาพร้อมกับข้อกำหนดด้านประสิทธิภาพที่เข้มงวดเกี่ยวกับการเก็บรักษาข้อมูลในไฟล์ฐานข้อมูล ซึ่งเป็นที่ที่ระบบไฟล์ส่วนตัวต้นทาง โดยเฉพาะวิธีการ createSyncAccessHandle() ของออบเจ็กต์ FileSystemFileHandle ที่จะเข้ามามีบทบาท เมธอดนี้แสดงผล Promise ซึ่งแปลค่าเป็นออบเจ็กต์ FileSystemSyncAccessHandle ซึ่งสามารถใช้ในการอ่านและเขียนแบบพร้อมกันจากและเขียนไปยังไฟล์ เมธอดแบบซิงโครนัสของวิธีนี้มีข้อได้เปรียบด้านประสิทธิภาพ แต่ก็ใช้งานได้เฉพาะใน Web Workers โดยเฉพาะสำหรับไฟล์ภายในระบบไฟล์ส่วนตัวของต้นทาง ดังนั้นจึงไม่สามารถบล็อกเทรดหลักได้

การตั้งค่าส่วนหัวที่จำเป็น

นอกจากไฟล์อื่นๆ แล้ว ที่เก็บ SQLite Wasm ที่ดาวน์โหลดมายังมีไฟล์ sqlite3.js และ sqlite3.wasm ซึ่งประกอบกันเป็นบิลด์ sqlite3 WASM/JS ไดเรกทอรี jswasm มีรายการนำส่ง sqlite3 หลัก และไดเรกทอรีระดับบนสุดจะมีแอปสาธิตและแอปทดสอบ เบราว์เซอร์จะไม่แสดงไฟล์ Wasm จาก URL ของ file:// ดังนั้นแอปที่คุณสร้างด้วยไฟล์นี้จะต้องใช้เว็บเซิร์ฟเวอร์และเซิร์ฟเวอร์ดังกล่าวต้องมีส่วนหัวต่อไปนี้ในการตอบสนองเมื่อแสดงไฟล์

  • Cross-Origin-Opener-Policy ตั้งค่าเป็นคำสั่ง same-origin ซึ่งจะแยกบริบทการท่องเว็บเป็นเอกสารต้นทางเดียวกันโดยเฉพาะ เอกสารแบบข้ามต้นทางไม่โหลดในบริบทการท่องเว็บเดียวกัน
  • Cross-Origin-Embedder-Policy ตั้งค่าเป็นคำสั่ง require-corp เพื่อให้เอกสารโหลดทรัพยากรได้จากต้นทางเดียวกันเท่านั้น หรือมีการทำเครื่องหมายอย่างชัดแจ้งว่าโหลดได้จากต้นทางอื่น

เหตุผลสำหรับส่วนหัวเหล่านี้เพราะ SQLite Wasm ต้องใช้ SharedArrayBuffer และการตั้งค่าส่วนหัวเหล่านี้เป็นส่วนหนึ่งของข้อกำหนดด้านความปลอดภัย

หากคุณตรวจสอบการเข้าชมด้วยเครื่องมือสำหรับนักพัฒนาเว็บ คุณจะเห็นข้อมูลต่อไปนี้

ส่วนหัว 2 แบบที่กล่าวถึงข้างต้น ได้แก่ Cross-Origin-embeddedder-Policy และ Cross-Origin-Opener-Policy ซึ่งไฮไลต์ไว้ในเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome

ทดสอบความเร็ว

ทีม SQLite ได้ทำการเปรียบเทียบในการใช้งาน WebAssembly ของตนโดยเปรียบเทียบกับ Web SQL ที่เลิกใช้งานแล้ว เกณฑ์เปรียบเทียบเหล่านี้แสดงให้เห็นว่า SQLite Wasm นั้น ทำงานเร็วพอๆ กับ Web SQL บางครั้งอาจช้ากว่า หรือบางครั้งอาจเร็วนิดหน่อย ดูรายละเอียดทั้งหมดได้ที่หน้าผลการค้นหา

ตัวอย่างโค้ดสำหรับเริ่มต้นใช้งาน

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

import { sqlite3Worker1Promiser } from '@sqlite.org/sqlite-wasm';

(async () => {
  try {
    console.log('Loading and initializing SQLite3 module...');

    const promiser = await new Promise((resolve) => {
      const _promiser = sqlite3Worker1Promiser({
        onready: () => {
          resolve(_promiser);
        },
      });
    });

    console.log('Done initializing. Running demo...');

    let response;

    response = await promiser('config-get', {});
    console.log('Running SQLite3 version', response.result.version.libVersion);

    response = await promiser('open', {
      filename: 'file:worker-promiser.sqlite3?vfs=opfs',
    });
    const { dbId } = response;
    console.log(
      'OPFS is available, created persisted database at',
      response.result.filename.replace(/^file:(.*?)\?vfs=opfs$/, '$1'),
    );

    await promiser('exec', { dbId, sql: 'CREATE TABLE IF NOT EXISTS t(a,b)' });
    console.log('Creating a table...');

    console.log('Insert some data using exec()...');
    for (let i = 20; i <= 25; ++i) {
      await promiser('exec', {
        dbId,
        sql: 'INSERT INTO t(a,b) VALUES (?,?)',
        bind: [i, i * 2],
      });
    }

    console.log('Query data with exec()');
    await promiser('exec', {
      dbId,
      sql: 'SELECT a FROM t ORDER BY a LIMIT 3',
      callback: (result) => {
        if (!result.row) {
          return;
        }
        console.log(result.row);
      },
    });

    await promiser('close', { dbId });
  } catch (err) {
    if (!(err instanceof Error)) {
      err = new Error(err.result.message);
    }
    console.error(err.name, err.message);
  }
})();

ข้อมูลประชากร

ดูการทำงานของโค้ดด้านบนในการสาธิต อย่าลืมดูซอร์สโค้ดใน Glitch โปรดทราบว่าเวอร์ชันที่ฝังอยู่ด้านล่างไม่ได้ใช้แบ็กเอนด์ OPFS แต่เมื่อคุณเปิดการสาธิตในแท็บแยกต่างหาก ก็จะใช้แบ็กเอนด์นี้

การแก้ไขข้อบกพร่องของระบบไฟล์ส่วนตัวต้นทาง

หากต้องการแก้ไขข้อบกพร่องของเอาต์พุตระบบไฟล์ส่วนตัวต้นทางของ SQLite Wasm ให้ใช้ส่วนขยาย Chrome OPFS Explorer

OPFS Explorer ใน Chrome เว็บสโตร์

หลังจากติดตั้งส่วนขยายแล้ว ให้เปิด Chrome DevTools เลือกแท็บ OPFS Explorer จากนั้นคุณก็พร้อมตรวจสอบสิ่งที่ SQLite Wasm เขียนลงในระบบไฟล์ส่วนตัวต้นทางแล้ว

ส่วนขยาย OPFS Explorer ของ Chrome ที่แสดงโครงสร้างระบบไฟล์ส่วนตัวต้นทางของแอปเดโม

หากคลิกไฟล์ใดก็ตามในหน้าต่าง OPFS Explorer ในเครื่องมือสำหรับนักพัฒนาเว็บ คุณจะบันทึกลงในดิสก์ในเครื่องได้ จากนั้นคุณจะใช้แอปอย่างเช่น SQLite Viewer เพื่อตรวจสอบฐานข้อมูลเพื่อให้มั่นใจว่า SQLite Wasm ทำงานได้จริงตามที่สัญญาไว้

แอป SQLite Viewer ใช้สำหรับเปิดไฟล์ฐานข้อมูลจากการสาธิต SQLite Wasm

การรับความช่วยเหลือและแสดงความคิดเห็น

SQLite Wasm ได้รับการพัฒนาและดูแลโดยชุมชน SQLite รับความช่วยเหลือและแสดงความคิดเห็นโดยค้นหาและโพสต์ในฟอรัมการสนับสนุน ดูเอกสารประกอบฉบับเต็มได้ในเว็บไซต์ SQLite

ข้อความแสดงการยอมรับ

รูปภาพหลักโดย Tobias Fischer ในUnsplash