Skip to main content
Bun 原生实现了高性能的 SQLite3 驱动。要使用它,请从内置的 bun:sqlite 模块导入。
db.ts
API 是同步的,并且速度很快。感谢 better-sqlite3 及其贡献者对 bun:sqlite 的 API 提供了启发。 功能包括:
  • 事务
  • 参数(命名参数和位置参数)
  • 预编译语句
  • 数据类型转换(BLOB 转换为 Uint8Array
  • 无需 ORM 即可将查询结果映射到类 - query.as(MyClass)
  • 在 JavaScript 的所有 SQLite 驱动中拥有最快的性能
  • 支持 bigint
  • 多查询语句(例如 SELECT 1; SELECT 2;)可在一次调用 database.run(query) 中执行
bun:sqlite 在读取查询上比 better-sqlite3 快约 3-6 倍,比 deno.land/x/sqlite 快约 8-9 倍。各驱动均基于 Northwind Traders 数据集进行基准测试。查看并运行基准测试源码
Bun、better-sqlite3 与 deno.land/x/sqlite 的 SQLite 基准测试

在搭载 macOS 12.3.1 的 M1 MacBook Pro 上的基准测试


数据库

打开或创建 SQLite3 数据库:
db.ts
打开内存数据库:
db.ts
以只读模式打开:
db.ts
如果文件不存在则创建数据库:
db.ts

严格模式

默认情况下,bun:sqlite 绑定参数时需要包含 $:@ 前缀,且如果缺少参数不会抛出错误。 如果希望缺少参数时抛出错误,并且允许绑定时不使用前缀,可以在 Database 构造函数中设置 strict: true
db.ts

通过 ES 模块导入属性加载

你还可以使用导入属性加载数据库。
db.ts
此用法等同于:
db.ts

.close(throwOnError: boolean = false)

要关闭数据库连接,同时让通过 .prepare() 创建的语句继续工作,直到它们被终结或垃圾回收,请调用 .close(false)
db.ts
通过 .query() 创建的语句由 Database 所有,无论使用哪种方式都会立即被终结。底层连接(以及数据库文件句柄)会在最后一个未完成的 .prepare() 语句被终结后释放。 要终结所有未完成的语句、立即释放连接,并在 SQLite 报告关闭错误时抛出异常,请调用 .close(true)
db.ts
使用已被 close() 终结的语句会抛出 Database has closed,但 toString() 除外:它会返回空字符串;finalize() 也除外:它仍然可以安全调用。
close() 可以安全地多次调用,但第一次调用后不会产生任何效果(不过在调用 close(false) 后调用 close(true) 仍会终结所有剩余的 .prepare() 语句)。如果 Database 在未关闭的情况下被垃圾回收,连接会在从其创建的每个语句也都被终结或回收后释放。 using 语句会调用 close(true)

using 语句

using 语句会在代码块退出时关闭数据库连接。
db.ts

.serialize()

bun:sqlite 支持 SQLite 内置的数据库序列化和反序列化机制,详见 serializedeserialize
db.ts
内部调用了 sqlite3_serialize

.query()

Database 实例上使用 db.query() 方法来预处理 SQL 查询。该方法返回一个缓存在 Database 实例上的 Statement 实例。查询不会被执行。
db.ts
“缓存”是什么意思?这里的缓存指的是已编译的预处理语句(SQL 字节码),而不是查询结果。当你多次使用相同的 SQL 字符串调用 db.query() 时,Bun 会返回同一个已缓存的 Statement 对象,而不是重新编译 SQL。缓存会保存最近使用的 Database.MAX_QUERY_CACHE_SIZE 个 SQL 字符串(默认值为 20);被移出的语句仍然可以继续使用,但之后使用相同字符串调用 db.query() 时会重新编译出一个新的语句。使用不同的参数值重复使用已缓存的语句是安全的:
如果需要每次都生成新的 Statement 实例(不缓存),例如动态生成 SQL 语句且不想缓存临时查询,请使用 .prepare() 替代 .query()

WAL 模式

SQLite 支持预写日志模式(WAL),该模式可以显著提升性能,尤其是在存在许多并发读取者和单个写入者时。建议大多数应用启用 WAL 模式。 启用 WAL 模式,请在程序开始时执行此 pragma 查询:
db.ts
在 WAL 模式下,对数据库的写入会直接写入一个名为“WAL 文件”(-wal)的独立文件中。此外,还会创建一个共享内存索引文件(-shm)来协调读取操作。之后,WAL 文件会被整合到主数据库文件中。可以将其理解为待处理写入的缓冲区。有关更详细的概述,请参阅 SQLite 文档

WAL 附属文件清理

在基于文件的数据库中使用 WAL 模式时,SQLite 会在数据库旁创建两个附属文件:预写日志(-wal)和共享内存索引(-shm)。这些文件是否在 .close() 后自动删除取决于你的平台:
  • macOS:Bun 使用系统提供的 SQLite,而 Apple 构建的 SQLite 启用了持久化 WAL。-wal-shm 文件在关闭后仍会保留。这不是 bug——这是 Apple 对系统 SQLite 的配置方式。
  • LinuxWindows:Bun 静态链接了自带的 SQLite 构建版本,该版本遵循上游默认设置。当没有其他连接处于打开状态时,附属文件通常会在关闭后被删除。
若要确保在所有平台上都清理附属文件,请在关闭前禁用 WAL 持久化并运行截断检查点:
db.ts

语句

Statement 是一个_预编译查询_,也就是说,它已经被解析并编译成高效的二进制形式。它可以被执行多次。 通过 Database 实例的 .query 方法创建:
db.ts
查询中可以包含参数,既可以是数字(例如 ?1),也可以是命名参数($param:param@param)。
db.ts
执行查询时绑定参数值。Statement 可以通过多种方法执行,返回不同形式的结果。

绑定参数值

通过向 .all().get().run().values() 方法传入对象绑定参数值。
db.ts
也可以使用位置参数绑定:
db.ts

strict: true 允许无前缀绑定

默认绑定命名参数时需要包含 $:@ 前缀。启用 Database 构造函数中的 strict 选项可允许不带前缀绑定:
db.ts

.all()

使用 .all() 执行查询,返回所有结果数组。
db.ts
内部调用 sqlite3_reset,并重复调用 sqlite3_step 直到返回 SQLITE_DONE

.get()

使用 .get() 执行查询,并获取第一条结果对象。
db.ts
内部调用 sqlite3_reset,然后重复调用 sqlite3_step,直到其不再返回 SQLITE_ROW。如果查询不返回任何行,则返回 null

.run()

使用 .run() 运行查询,并返回一个包含执行元数据的对象。这对于修改架构的查询(例如 CREATE TABLE)或批量写入操作非常有用。
db.ts
内部调用 sqlite3_reset 并调用一次 sqlite3_step,不遍历所有结果行,提升性能。 lastInsertRowid 属性是最后插入数据库的行的 ID。changes 属性是查询影响的行数。

.as(Class) - 将结果映射为类实例

使用 .as(Class) 运行查询,并将结果作为类的实例返回。类的方法、获取器和设置器在每一行上都可用。
db.ts
作为性能优化,类构造函数不会被调用,默认初始化器不会运行,私有字段也无法访问。这更类似于 Object.create,而不是 new:类的原型会被分配给对象,因此其方法、获取器和设置器都能正常工作。 数据库列被设置为类实例的属性。

.iterate() (@@iterator)

使用 .iterate() 运行查询并逐条返回结果。适合处理巨大结果集,避免一次性加载大量数据。
db.ts
也可以使用 @@iterator 协议:
db.ts

.values()

使用 .values() 执行查询,返回结果的二维数组。
db.ts
内部调用 sqlite3_reset,并重复调用 sqlite3_step 直到返回 SQLITE_DONE

.finalize()

使用 .finalize() 销毁 Statement 并释放与其关联的所有资源。完成销毁后,Statement 无法再次执行。通常情况下,垃圾回收器会替你完成此操作,但在对性能敏感的应用中,显式销毁可能很有用。
db.ts

.toString()

Statement 调用 toString() 会打印出代入参数后的完整 SQL 语句,方便调试。
db.ts
内部调用 sqlite3_expanded_sql,参数使用最近绑定的值展开。

参数

查询支持数字参数(?1)或命名参数($param:param@param)。执行查询时绑定参数值:
query.ts
数字(位置)参数同样支持:
db.ts

整数

SQLite 支持有符号 64 位整数,但 JavaScript 仅支持有符号 52 位整数,或使用 bigint 支持任意精度整数。 bigint 输入在任何地方都受支持,但默认情况下,bun:sqlite 将整数返回为 number 类型。如果需要处理大于 2^53 的整数,请在创建 Database 实例时将 safeIntegers 选项设置为 true。这还会验证传递给 bun:sqlitebigint 值不会超过 64 位。

safeIntegers: true

safeIntegerstrue 时,bun:sqlite 将整数返回为 bigint 类型:
db.ts
safeIntegerstrue 时,如果绑定参数中的 bigint 值超过 64 位,bun:sqlite 会抛出错误:
db.ts

safeIntegers: false(默认)

safeIntegersfalse 时,bun:sqlite 将整数返回为 number 类型,并截断超出 53 位的所有位:
db.ts

事务

事务以_原子方式_执行多个查询:要么全部成功,要么一个也不成功。使用 db.transaction() 方法创建事务:
db.ts
目前还没有插入任何猫。db.transaction() 返回一个新函数(insertCats),该函数会_包装_执行查询的函数。 要执行事务,请调用此函数。参数会传递给被包装的函数,而被包装函数的返回值会由事务函数返回。被包装的函数还可以访问事务执行位置所定义的 this 上下文。
db.ts
驱动程序会在调用 insertCats 时自动开始事务,并在被包装的函数返回时提交事务。如果抛出异常,事务会回滚。异常会像往常一样继续向上传播,不会被捕获。
嵌套事务 — 事务函数中可调用其他事务函数。此时内层事务会变成 SQLite 的保存点(savepoint)
db.ts
事务函数还提供 deferredimmediateexclusive 等版本:

.loadExtension()

要加载 SQLite 扩展,请在 Database 实例上调用 .loadExtension(name)
db.ts
macOS 用户 默认情况下,macOS 自带 Apple 的专有 SQLite 构建版本,不支持扩展。要使用扩展,请安装原版 SQLite 构建版本。
terminal
在创建任何 Database 实例前,调用 Database.setCustomSQLite(path) 指向原生版本。此操作 macOS 以外系统无效。传入的是 SQLite 的 .dylib 文件路径,非可执行文件路径。Homebrew 近期版本路径形如 /opt/homebrew/Cellar/sqlite/<version>/libsqlite3.dylib
db.ts

.fileControl(cmd: number, value: any)

要使用高级的 sqlite3_file_control API,请在 Database 实例上调用 .fileControl(cmd, value)。实际示例请参见 WAL 附属文件清理
db.ts
value 可为以下类型:
  • number
  • TypedArray
  • undefinednull

参考

类型参考

数据类型