Electron 本地数据库实践指南(上)

本文首发同名微信公众号:前端徐徐 

大家好,我是徐徐。今天我们聊聊如何在 Electron 中集成本地数据库,这一节主要是对比各个数据库,给大家一个合适的选择。

前言

数据本地持久化是大多数客户端都会使用到的技术,根据业务场景的不同,可能是存储一些简单的系统配置,也可能是复杂的业务数据,我们在使用一些数据本地持久化工具的时候也是需要根据自己的应用场景来选择适合自己的工具。常规情况就会引入一个本地数据库来支撑这种数据本地持久化的需求,下面我们来看看如何在 Electron 应用中如何选择一些常规的数据库工具吧。

数据库工具对比

我们选择 sqlite3,lowdb,dexie 这几种数据库工具来做对比。为什么选择这三个数据库工具呢?因为这三个数据库分别代表了三种不同的应用场景以及相应的典型案例。

上图是三种数据库工具的一个 npm 排行,可以简单的看出下载的情况及更新和维护的情况,但是我们在选择的时候不能以下载量来看,这个只是一个参考,最主要的选择还是自己的业务场景,下面我们就来详细对比一下这三个数据库工具。我们来对比一下 sqlite3、lowdb 和 dexie 这三种数据库工具,看看它们各自的特点、优势和劣势。

SQLite3

特点

  • SQLite 是一个 C 语言库,提供了一个轻量级的、基于磁盘的数据库。
  • 通过 node-sqlite3 包可以在 Node.js 中使用 SQLite。

优势

  • 轻量级:无需安装独立的数据库服务器,数据库文件可以直接嵌入应用中。
  • 跨平台:支持 Windows、macOS 和 Linux。
  • SQL 支持:支持标准的 SQL 语法,便于管理和查询数据。
  • 事务支持:支持事务管理,保证数据的一致性和完整性。

劣势

  • 并发写性能有限:由于单个文件锁机制,可能会影响高并发写操作的性能。
  • 功能相对简单:不支持复杂的数据库功能(如存储过程)。
  • 库体积较大:相对于其他轻量级数据库工具,可能会增加应用程序的体积。

Lowdb

特点

  • Lowdb 是一个小型的 JSON 文件数据库,使用简单的 JavaScript API。

优势

  • 简单易用:API 设计非常直观,适合快速开发和小型项目。
  • 无依赖性:完全嵌入式,无需独立的数据库服务器或其他依赖。
  • 灵活性:数据以 JSON 格式存储,便于操作和维护。

劣势

  • 性能有限:处理大量数据时性能可能不如其他数据库。
  • 并发支持有限:由于是文件存储,可能会遇到并发写入的问题。
  • 功能简单:不支持复杂的查询和索引,适合简单的数据操作。

Dexie.js

特点

  • Dexie.js 是一个封装了 IndexedDB 的 JavaScript 库,提供了更友好的 API。

优势

  • 高性能:基于浏览器原生的 IndexedDB,支持大数据量和高并发操作。
  • 事务支持:支持事务管理,保证数据的一致性和完整性。
  • 灵活查询:支持复杂的查询和索引,提供强大的查询能力。
  • 跨平台:适用于浏览器环境和 Electron 应用。

劣势

  • 浏览器依赖:主要用于浏览器环境,其他环境下的使用可能受限。
  • API 学习曲线:虽然比直接使用 IndexedDB 简单,但仍然需要学习新的 API。
  • 持久化存储受限:存储空间和持久化存储可能受限于浏览器的限制。

总结对比表格

工具名称

SQLite3

Lowdb

Dexie

轻量级

跨平台

SQL 支持

事务支持

并发支持

有限

有限

性能

中高

中低

易用性

中高

存储格式

二进制文件

JSON 文件

IndexedDB

适用场景

中小型应用、跨平台应用

小型应用、快速开发

大数据量、高并发的Web 应用

SQLite3 适合需要标准 SQL 支持和事务管理的项目,Lowdb 适合简单、轻量级的应用,而 Dexie 则适合需要高性能和复杂查询的 Web 应用。三种数据库工具在 Electron 应用中使用的进程类型也有区别,SQLite3 和 Lowdb适用于主进程,Dexie 适用于渲染进程。

不过上面的对比都是比较官方的话术,也都是官网上获取到的信息,我觉得要真正的时候用时候我们需要写一个压测脚本来看看效果。

实际测试对比

for循环插入 10000 条数据,批量查询 10000 条数据

  • SQLite3
import sqlite3 from 'sqlite3'
function testSQLite3() {
  console.log('Testing SQLite3...');
  const db = new sqlite3.Database("mydatebase.db");

  db.serialize(() => {
      db.run('CREATE TABLE test (info TEXT)');

      console.time('SQLite3 Insert');
      const stmt = db.prepare('INSERT INTO test VALUES (?)');
      for (let i = 0; i < 10000; i++) {
          stmt.run(`Data ${i}`);
      }
      stmt.finalize();
      console.timeEnd('SQLite3 Insert');

      console.time('SQLite3 Query');
      db.all('SELECT * FROM test', (err, rows) => {
          console.log(rows);
          if (err) throw err;
          console.timeEnd('SQLite3 Query');
      });

      db.close();
  });
}

  • Lowdb
import { LowSync } from 'lowdb'
import { JSONFileSync } from 'lowdb/node'
function testLowdb() {
  console.log('Testing Lowdb...');
  const defaultData = { test: [] }
  const db = new LowSync(new JSONFileSync('test.json'), defaultData)

  console.time('Lowdb Insert');
  for (let i = 0; i < 10000; i++) {
    db.update(({ test }) => test.push(`Data ${i}`))
  }
  console.timeEnd('Lowdb Insert');

  console.time('Lowdb Query');
  db.read()
  console.log(db.data.test);
  console.timeEnd('Lowdb Query');
}

  • Dexie
const testDexie = async () => {
    console.log('Testing Dexie...');
    const db:any = new Dexie('TestDatabase');
    db.version(1).stores({
      test: '++id,value' // 自增 id 和 value
    });
  
    console.time('Dexie Insert');
    for (let i = 0; i < 10000; i++) {
      db.test.add({ value: `Data ${i}` })
    }
    console.timeEnd('Dexie Insert');
    console.time('Dexie Query');
    const allData = await db.test.toArray();
    console.log(allData);
    console.timeEnd('Dexie Query');
  }

对比总结:

数据库

插入时间

查询时间

SQLite3

8.899ms

7.278s

Lowdb

5.483s

4.292ms

Dexie

219.875ms

41955.162ms

批量插入 10000 条数据,批量查询 10000 条数据

  • SQLite3
import sqlite3 from "sqlite3";
function testSQLite3() {
  console.log("Testing SQLite3...");
  const db = new sqlite3.Database("mydatebase.db");

  db.serialize(() => {
    db.run("CREATE TABLE test (info TEXT)");

    console.time("SQLite3 Insert");
    db.run("BEGIN TRANSACTION");

    const stmt = db.prepare("INSERT INTO test VALUES (?)");
    for (let i = 0; i < 10000; i++) {
      stmt.run(`Data ${i}`);
    }
    stmt.finalize();

    db.run("COMMIT");
    console.timeEnd("SQLite3 Insert");

    console.time("SQLite3 Query");
    db.all("SELECT * FROM test", (err, rows) => {
      console.log(rows);
      if (err) throw err;
      console.timeEnd("SQLite3 Query");
    });

    db.close();
  });
}

  • Lowdb
import { LowSync } from "lowdb";
import { JSONFileSync } from "lowdb/node";
function testLowdb() {
  console.log("Testing Lowdb...");
  const defaultData = { test: [] };
  const db = new LowSync(new JSONFileSync("test.json"), defaultData);
  const dataToInsert = Array.from({ length: 10000 }, (_, i) => `Data ${i}`);
  console.time("Lowdb Insert");
  db.update(({ test }) => test.push(...dataToInsert));
  console.timeEnd("Lowdb Insert");

  console.time("Lowdb Query");
  db.read();
  console.log(db.data.test);
  console.timeEnd("Lowdb Query");
}

  • Dexie
const testDexie = async () => {
  console.log('Testing Dexie...');
  // 创建数据库
  const db: any = new Dexie('TestDatabase');
  db.version(1).stores({
    test: '++id,value' // 自增 id 和 value
  });

  console.time('Dexie Insert');
  // 批量插入数据
  const data = Array.from({ length: 10000 }, (_, i) => ({ value: `Data ${i}` }));
  await db.test.bulkAdd(data);
  console.timeEnd('Dexie Insert');

  console.time('Dexie Query');
  // 查询数据
  const allData = await db.test.toArray();
  console.log(allData);
  console.timeEnd('Dexie Query');
}

对比总结:

数据库

插入时间

查询时间

SQLite3

9.896ms

161.23ms

Lowdb

3.544ms

5.97ms

Dexie

1397.203ms

115.137ms

一边写入一边查询

  • SQLite3
function testSQLite3ByTask() {
  console.log("Testing SQLite3...");
  const db = new sqlite3.Database("mydatebase.db");

  db.serialize(() => {
    db.run("CREATE TABLE IF NOT EXISTS test (info TEXT)");

    let insertCounter = 0;
    let queryCounter = 0;

    // 定时插入数据
    setInterval(() => {
      console.time("SQLite3 Insert");
      db.run("BEGIN TRANSACTION", (err) => {
        if (err) {
          console.error("Error beginning transaction:", err);
          clearInterval(insertInterval);
          return;
        }

        const stmt = db.prepare("INSERT INTO test VALUES (?)");
        for (let i = 0; i < 1000; i++) {
          stmt.run(`Data ${insertCounter++}`);
        }
        stmt.finalize((err) => {
          if (err) {
            console.error("Error finalizing statement:", err);
            clearInterval(insertInterval);
            return;
          }

          db.run("COMMIT", (err) => {
            if (err) {
              console.error("Error committing transaction:", err);
              clearInterval(insertInterval);
              return;
            }
            console.log(`Inserted up to Data ${insertCounter - 1}`);
            console.timeEnd("SQLite3 Insert");
          });
        });
      });
    }, 100)

    // 定时查询数据
    setInterval(() => {
      console.time("SQLite3 Query");
      db.all("SELECT COUNT(*) as count FROM test", (err, rows) => {
        if (err) {
          console.error("Error querying database:", err);
          clearInterval(queryInterval);
        } else {
          console.log(`Query ${++queryCounter}: Total rows = ${rows[0].count}`);
        }
        console.timeEnd("SQLite3 Query");
      });
    }, 50);
  });
}

100ms 定时写入,50ms 定时查询

  • Lowdb
function testLowdbByTask() {
  console.log("Testing Lowdb...");
  const defaultData = { test: [] };
  const db = new LowSync(new JSONFileSync("test.json"), defaultData);
  let insertCounter = 0;
  let queryCounter = 0;
  // 定时插入数据
  setInterval(() => {
    console.time("Lowdb Insert");
    db.update(({ test }) => test.push(...Array.from({ length: 1000 }, (_, i) => `Data ${insertCounter++}`)));
    console.log(`Inserted up to Data ${insertCounter - 1}`);
    console.timeEnd("Lowdb Insert");
  }, 100);

  // 定时查询数据
  setInterval(() => {
    console.time("Lowdb Query");
    db.read();
    console.log(`Query ${++queryCounter}: Total rows = ${db.data.test.length}`);
    console.timeEnd("Lowdb Query");
  }, 50);
}

  • Dexie
  const testDexieByTask = async () => {
    console.log('Testing Dexie...');
    const db:any = new Dexie('TestDatabase');
    db.version(1).stores({
      test: '++id,value'
    });
  
    let insertCounter = 0;
    let queryCounter = 0;
  
    setInterval(async () => {
      console.time('Dexie Insert');
      const data = Array.from({ length: 1000 }, (_, i) => ({ value: `Data ${insertCounter++}` }));
      await db.test.bulkAdd(data);
      console.log(`Inserted up to Data ${insertCounter - 1}`);
      console.timeEnd('Dexie Insert');
    }, 100);
  
    // 定时查询数据
    setInterval(async () => {
      console.time('Dexie Query');
      const allData = await db.test.toArray();
      console.log(`Query ${++queryCounter}: Total rows = ${allData.length}`);
      console.timeEnd('Dexie Query');
    }, 50);
  
  };

对比总结:

数据库

并发读写速度

SQLite3

Lowdb

Dexie

如何选择数据库工具

上面我们对三种数据库都做了相应的对比,大概大家心里面都有一个清楚的认知了,我大概总结一下如何选择:

SQLite3 更适合需要标准 SQL 支持的项目,适用场景广泛,中大型项目适用;Lowdb 适合开发一个轻量级、功能较简单的本地应用,因为它使用简单,适合快速开发,并且性能在可接受范围内;如果你的应用主进程涉及的开发和存储并不多,大部分都是 Web 操作,Dexie 可以选择考虑一下。

结语

在实际项目中,选择合适的数据库工具不仅需要考虑功能需求和性能要求,还需要考虑开发的复杂性和团队的技术栈。希望通过本文的对比分析,能够帮助开发者在不同的应用场景中做出更明智的选择,找到最适合自己项目的数据库工具。无论选择哪种工具,都要根据实际需求和业务特点进行权衡,充分发挥每种工具的优势,才能实现最佳的开发效果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值