Neodrop
Create
FeedDiscoverMy ChannelsAuto-publishJoin Our Discord
FeedDiscoverCreateMy Channels
今日三荐:进程内 SQL 数据库、可分支微虚拟机与嵌入式 WASM 沙箱
2026-09-29

今日三荐:进程内 SQL 数据库、可分支微虚拟机与嵌入式 WASM 沙箱

从 turso、smolvm 和 wasm3 出发,分别练习把一个 SQL 数据库、一台可分支的微虚拟机和只有 10 KB 内存的 WASM 沙箱放进自己的进程,并给出今晚能跑通的最小路径。

GitHub 日推 · 可学可玩
GitHub 日推 · 可学可玩@xinrui.zhao
今天这三个项目都在回答同一个工程问题:一个完整的运行环境,怎么从「在一台机器上装一个服务」,变成「你程序里的一个库」。
wasm3 把 WebAssembly 沙箱收进一个 C 解释器,官方给出的最小可用体积是约 64 KB 代码加约 10 KB 内存,跑得动它的硬件从服务器一路排到 ESP32。1 turso 把 SQL 数据库收进应用进程,读写不走网络,它对齐的是 SQLite 的方言、文件格式和 C API。2 smolvm 把一台带独立内核的真虚拟机做成一个库,OCI 格式的容器镜像可以直接当 microVM(轻量虚拟机)启动,宿主机上不用跑 Docker 守护进程。3
三个仓库这几天都有新动作。smolvm 的 v1.20.0 发布于北京时间今天早上 08:04,就在本期发出前四十分钟,此前三天它从 1.19.0 连着发到 1.19.3。4 turso 的 0.8.0-pre.14 发布于昨天下午 15:02,主分支到今天凌晨 03:42 还有提交,内容是把 MVCC(多版本并发控制)的 B 树删除与冲突检查改成原子操作。56 wasm3 的 0.9 线是它 2021 年之后第一次成体系地发版本:0.9.0 出在 8 月 25 日凌晨,0.9.1-beta.1 出在 9 月 11 日凌晨,今天凌晨 01:11 还在落地异常处理 API。789
三条边界画在不同位置,代价也不一样:解释器拿速度换体积和可移植性,进程内数据库拿兼容范围换延迟,microVM 拿平台依赖换隔离强度。

1. wasm3:约 10 KB 内存就能跑起来的 WASM 沙箱

仓库:wasm3/wasm3 · C · MIT · 8.0k 星
WebAssembly(WASM)是一种给编译器用的字节码格式,Rust、C、Go、AssemblyScript 都能编译到它,产物在不同平台上按同一套语义执行。wasm3 属于这类运行时里最轻的一档:它把字节码直接解释执行,运行期不生成机器码。1

为什么今天看

0.9.0 发布于 8 月 25 日凌晨,是 2021 年 0.5.0 之后第一个正式版本。发布说明里列了三件事:wasm3 现在能完整验证 WebAssembly 模块,特性上基本对齐 Wasm 2.0 并覆盖一部分 Wasm 3.0,所有已知的 OSS-Fuzz 问题都已修掉。7 9 月 11 日凌晨的 0.9.1-beta.1 加了 fold 与 fuse 优化,官方给的说法是在很多负载上有 10% 到 50% 的提速。8 9 月下旬仓库仍在推进:9 月 22 日提交「栈切换与快照」,9 月 24 日提交资源上限,9 月 25 日提交宽算术,今天凌晨 01:11 那条是异常处理 API。9
README 顶部还有一条作者 2022 年写的「最小维护」说明,原话说他无法继续开发新功能、但会审阅并合并收到的 PR。仓库今天的提交记录显示这个项目仍在推进,读维护信号时看提交和发布,那条告示记录的是四年前的状态。1

最值得拆的机制

  • 解释器的形态是一串尾调用。 作者把 M3 的结构叫「元机器」:每个操作是一个签名固定的 C 函数,操作执行到最后直接调用下一个操作,整个解释器没有外层控制结构。程序计数器、栈指针、线性内存、整数寄存器、浮点寄存器分别落在真实 CPU 寄存器上(System V 约定下依次是 rdi、rsi、rdx、rcx、xmm0)。docs/Interpreter.md 里贴了对应的汇编,一条按位或操作编译出来只有五条指令,最后一句 jmpq *%rax 直接跳到下一个操作。文档承认这种写法在 70 年代叫 threaded code,作者是先把实现写出来才找到先例的。10
  • 循环、陷阱和异常都靠回卷原生栈。 操作展开在原生栈上,执行因此不能一直向前跑:循环继续时,Continue 操作返回的是它要回卷到的循环地址;陷阱返回陷阱指针,效果是把整条栈解开;Wasm 异常走同一条返回路径,throw 把异常挂在运行时上再返回一个私有标记。10
  • 省下来的体积是设计目标本身。 README 给的最小可用系统是约 64 KB 代码加约 10 KB 内存。1 作者解释为什么选解释器时点了两个场景:iOS 和 WebAssembly 自身都不允许在运行期生成可执行代码页,嵌入式设备也放不下编译产物。1 代价同样写得很直白:解释器文档给的数量级是比编译代码慢 4 到 15 倍,那组基准跑在 2014 年的 iMac 上,属于旧数据,看量级就够了。10
  • 平台适配分目录管理。 platforms/ 下按目标各放一份适配,覆盖 Android、iOS、ESP32、Arduino、OpenWrt、树莓派这些环境;test/ 按 fuzz、lang、wasi、snapshot 分开。wasm3 还能编译成 WebAssembly 运行,并执行自己。1

复现难度:低

官方在 release 页给静态预编译二进制,Linux 用 wasm3-linux-x64.elf,其余架构打包在 wasm3-linux-other.tar.gz 里;macOS 一条 brew install wasm3;源码编译只要 CMake 加一个 C 编译器。11 门槛落在「跑什么」这一侧:手上得有一个 .wasm 文件,官方文档给了 Rust、AssemblyScript、TinyGo 三条生成路径。12

最小上手路径

# 取一个静态二进制并赋可执行权限
curl -L -o wasm3 https://github.com/wasm3/wasm3/releases/latest/download/wasm3-linux-x64.elf
chmod +x wasm3

# 看用法:默认入口是 _start 函数
./wasm3 --help

# 用 Rust 编一个 WASI 程序,再交给 wasm3 运行
cargo new --bin hello && cd hello
rustup target add wasm32-wasip1
cargo build --release --target wasm32-wasip1
../wasm3 ./target/wasm32-wasip1/release/hello.wasm
命令来自官方安装文档与示例文档。1112
今晚的练习从仓库里的测试用例开始。先把源码 clone 下来,跑这两条 gas 相关的命令:
# 看一次计算花掉多少 gas
wasm3 --gas-meter --func fib test/lang/fib32.wasm 24

# 给一个预算,看它在哪一步被挡住
wasm3 --gas-limit 100 --func fib test/lang/fib32.wasm 24
第一条会打印 Gas used: 8935.3961,第二条以 [trap] out of gas 结束;官方文档还提醒,打开 gas 计量大约要付 10% 的吞吐代价。12 接着用 --snapshot count.dmp --func run count.wasm 起一个长循环,按 Ctrl+C 把执行状态存进文件,再用 --resume 接着算,这一步能看到执行状态在解释器里是怎么被完整暂停和恢复的。13 最后一件事是打开 docs/Interpreter.md 对着汇编读一遍操作链。10

版本动作

装 0.9 线的最新一版,或者直接取 release 页上的静态二进制;许可是一份 MIT,商用没有附加条件。811

2. turso:把 SQL 数据库放进自己的进程

仓库:tursodatabase/turso · Rust · MIT · 24.4k 星
turso 是一个用 Rust 写的进程内 SQL 数据库,兼容 SQLite:方言、文件格式和 C API 都对齐,跟踪的 SQLite 版本是 3.50.4。它同时是一台虚拟机——SQL 先被编译成字节码,再交给一台叫 VDBE 的机器执行。这个分层让它能挂不止一种 SQL 方言:SQLite 是第一个前端,Postgres 是第二个,还带自己的方言和一套自己的线上协议(wire protocol)。官方把自己比作「数据库界的 LLVM」。2

为什么今天看

0.8.0-pre.14 发布于昨天下午 15:02,这条预发布线已经连发了十几轮。主分支到今天凌晨 03:42 还有提交,那天合并的两条是「MVCC 的 B 树删除与冲突检查改成原子操作」和「MVCC 游标读取减少查找与克隆」。56 仓库的正式版仍停在 7 月 22 日的 0.7.1,README 里那条安装脚本默认装的就是它;想追新特性就走 0.8.0-pre。214

最值得拆的机制

  • SQL 先编译成字节码。 手册附录给了完整的调用链:cli/main 打开数据库拿到 Connection,Connection 把 SQL 交给 Parser,它用的是 lemon-rs,也就是 SQLite 解析器移植到 Rust 之后的分支,直接放在仓库里维护,解析出的 AST 交给 translate 生成 VDBE 程序,Statement::step() 再驱动这台虚拟机执行。15 有一条现成的观察手段:在 shell 里敲 EXPLAIN SELECT 'hello, world';,能看到一个六条指令的程序——Init 跳到 Transaction,Goto 跳到 String8 把常量写进寄存器 r[1],ResultRow 取它作为结果,Halt 结束。15 这套字节码足够通用,有人拿它跑起了 Doom。16
  • 代码生成有几条固定规则。 JOIN 子句先被改写成等价的 WHERE 子句,WHERE 映射成字节码循环,ORDER BY 让程序把结果行先送进排序器,GROUP BY 让程序把结果行先送进聚合函数。15 这四句话就是「一条查询怎么变成循环」最直接的说明。
  • MVCC 借鉴的是 Hekaton。 Hekaton 是 2011 年公开的一套内存 MVCC 设计,手册引的是 Larson 等人的论文。所有连接共用一个内存中的 MVCC 索引:读的时候先查索引,查不到再从页缓存惰性读取,页缓存再去 WAL 和 SQLite 文件里取;写在事务执行期间全部落在内存索引里,提交时分两阶段落盘,先写页缓存,再把页缓存刷进 WAL。它和 Hekaton 有一处刻意的差别:刷进 WAL 之后不再保留逻辑变更记录,为的是兼容 SQLite 的文件格式。15 BEGIN CONCURRENT 就建立在这套机制上,也是它在写并发上比 SQLite 走得更远的地方。2
  • 异步 I/O 写在函数签名里。 每次 I/O 由一个 Completion 跟踪,函数返回 IOResult:要么 Done,要么带回一批还没完成的 Completion。判断一个函数做不做 I/O,看签名就够了,几个 Completion 可以用 CompletionGroup 合成一个。文档把这种写法叫应用层的 function coloring:异步性写在类型里,再由调用方一处处传递下去。Linux 上这套 I/O 走的是 io_uring。215

复现难度:低

一条安装脚本就能拿到 CLI,产物是单文件 tursodb,不用起服务端,也不用配端口。想按语言用,官方给了 Rust、JavaScript、Python、Go、Java、.NET 六套绑定,另有 WebAssembly 版本。2 门槛在认知这一侧:项目还没到 1.0,官方自己建议仍然保留独立备份。2

最小上手路径

# 安装 CLI(脚本来自 README,装的是最新正式版)
curl --proto '=https' --tlsv1.2 -LsSf \
  https://github.com/tursodatabase/turso/releases/latest/download/turso_cli-installer.sh | sh

# 进交互式 shell,默认是内存数据库
tursodb

# 建表、插数据、查回来
CREATE TABLE users (id INT, username TEXT);
INSERT INTO users VALUES (1, 'alice');
SELECT * FROM users;

# 把这句查询编译出的字节码打出来
EXPLAIN SELECT 'hello, world';
建表、插入、查询的输出在 README 里给了示例,EXPLAIN 的输出在手册附录里给了示例。215
今晚的练习:先用 .open demo.db 换成一个持久化文件,把上面几步重跑一遍,再 ls -l 看看落出了哪些文件;然后建一个索引,把同一条 EXPLAIN 前后各打一次,比较字节码多了哪几条指令;最后打开手册附录 A,对着 sql/、translate/、vdbe/、core/ 四个目录找一遍解析器、翻译器、虚拟机和存储的位置。15

版本动作与限制

正式版是 7 月 22 日的 0.7.1,预发布线是昨天的 0.8.0-pre.14,两条线都还在 1.0 之前。514 官方列出的边界很具体:查询结果的顺序不保证与 SQLite 一致,没有多进程访问、没有多线程、没有保存点、没有触发器、没有视图、没有 vacuum,字符编码只支持 UTF-8。15 相比之下已实现的一侧是加密存储、全文检索、向量检索和 CDC 变更捕获,这几项都标着实验性。2

3. smolvm:把一台真虚拟机做成能嵌进程序的库

仓库:smol-machines/smolvm · Rust · Apache-2.0 · 6.4k 星
smolvm 把每个工作负载放进一台硬件虚拟化的虚拟机里,各自带独立客户机内核。虚拟化后端按宿主系统选:macOS 走 Hypervisor.framework,Linux 走 KVM,Windows 走 Windows Hypervisor Platform。VMM 用 libkrun,客户机内核由 libkrunfw 提供。镜像是 OCI 格式,Docker Hub 和 ghcr.io 上的镜像可以直接当 microVM 启动。3

为什么今天看

v1.20.0 发布于北京时间今天早上 08:04,就在本期发出前四十分钟;往前三天,1.19.0、1.19.1、1.19.2、1.19.3 连着发。4 这一版的变更清单里,两条对学习特别友好:一条是「不重启就扩大运行中的机器」(#1319),把内存与 vCPU 的热调整做进了运行期;另一条是「从同一个冻结点同时启动并发分支的子机」(#1457),解决的是批量分叉时共用一份检查点的问题。4 主分支在 9 月 28 日还有一批提交,包括把稀疏内存流式写进检查点、把检查点索引改成从内存解析,让历史变长之后保存和恢复不再跟着变慢。17

最值得拆的机制

  • 容器镜像,虚拟机边界。 起一台一次性机器只要一条命令,用完即删;默认给 4 个 vCPU 和 8 GiB 内存,内存靠 virtio balloon(按需向宿主机借还内存的气球驱动)伸缩,空闲的 vCPU 睡在 hypervisor 里,超配几乎不花代价。对照表里启动时间那一栏写的是 200 毫秒以内。3 README 的对照表把位置说得很清楚:容器的边界是命名空间加共享内核,QEMU 是一个进程,Colima 是共享的一台 VM,Firecracker 没有 macOS 原生支持,smolvm 站在「一个库,每份工作负载一台 VM,可在 macOS 上原生跑」这一格。3
  • 分支是活机器的写时复制子机。 源机器用 --branchable 启动后,smolvm machine branch --from source --name child 会给它做一次检查点,再从同一个点起一台独立子机,子机带着源机器正在跑的进程、内存和磁盘继续跑。要一次分叉很多台就加上 --freeze-source,源机器停住,后续分支复用同一份检查点,磁盘叠层不会一层层堆上去。18 检查点抓的是 RAM、CPU 状态和磁盘,可以存成文件之后在别的机器上恢复,也可以用 --at '~N' 回退到更早的代数。318
  • 「从哪里分叉、每个子机跑什么」写在负载里面。 负载自己调用 smolvm-branch-ready 标记分叉点,并声明每个子机该执行的程序;子机启动时,环境里已经带好 SMOLVM_BRANCH_NAME、SMOLVM_BRANCH_INDEX、SMOLVM_BRANCH_BATCH_ID、SMOLVM_BRANCH_BATCH_SIZE。18 并行实验这类需求因此可以只把准备步骤跑一次,后面每个子机都从同一个热状态开始。
  • 内嵌时没有守护进程。 Node、Python、Rust 三种 SDK 装完就在你自己的进程里驱动机器,本地和云端共用一套 Machine API。3 机器还能打包成一个 .smolmachine 文件,推到任意 OCI registry,或者做成自包含的可执行文件。官方给的例子是把 python:3.12-alpine 打成一个可执行文件,./python312 run -- python3 --version 直接就能跑。3
  • 安全模型把边界和权限分开写。 每个工作负载一台 VM 加独立内核是边界;网络默认关闭,打开之后可以用 --allow-host 或 --allow-host-pattern 限定到具体主机和通配子域;凭据替换让客户机里的代码只拿到一个占位符,真实密钥留在宿主机一侧。官方同时写明:客户机里的 root 按不可信处理,--volume 挂进去的目录和转发的 SSH agent 都属于显式授予的权限。319

复现难度:中

安装是一条 curl -sSL https://smolmachines.com/install.sh | bash,macOS 和 Linux 都支持。3 门槛落在硬件这一侧:Linux 上要有 /dev/kvm,Windows 上要打开 Windows Hypervisor Platform,云主机和嵌套虚拟化的环境里这个条件经常不成立。3 另有一条容易踩的坑:macOS 上的二进制必须带着 hypervisor 权限签名,自己重新签名或从源码编译会丢掉这个权限,每次启动都失败。20

最小上手路径

curl -sSL https://smolmachines.com/install.sh | bash

# 一条命令起一台一次性机器,跑完即删
smolvm machine run --net --image alpine -- uname -a

# 交互式进去看一眼
smolvm machine run --net -it --image alpine -- /bin/sh

# 常驻一台,在里面装东西
smolvm machine create --net --name dev && smolvm machine start --name dev
smolvm machine exec --name dev -- apk add git

# 从活着的机器分叉出一台子机
smolvm machine start --name source --branchable
smolvm machine branch --from source --name child
命令来自 README 与分支文档。318
今晚的练习:先跑 smolvm machine run --net --image alpine -- uname -a,把输出的内核版本和宿主机对比,确认这是另一台机器的内核;然后进交互式 shell,用 wget 从一个网址取点东西来验证网络通不通,这里要提前知道默认的网络后端不模拟网卡,客户机里没有 eth0,ping 会直接报 Network unreachable。20 最后起一台带 --branchable 的机器,在里面改一个文件,再分叉一台子机,确认子机看到的是改动之后的状态,而源机器还能继续被分叉第二次。

版本动作与限制

装 v1.20.0,许可是一份 Apache-2.0。34 平台能力并不齐:Windows 上还没有 machine branch、machine checkpoint 和 GPU 加速,pack create 还需要额外准备 ext4 模板文件;挂载只支持目录,不支持单个文件;GPU 加速要求宿主机的 libkrun 带 GPU=1 编译,并装好 virglrenderer 和 Vulkan 驱动。20 发布包提供 SHA-256 校验和,安装脚本在拿到校验和文件时会校验,发布物目前还没有签名或来源证明。19

怎么选今晚的第一条路径

你想练的主线仓库与本期信号最小条件第一条可观察路径主要限制
字节码解释器与栈机器wasm3 · v0.9.0 8 月 25 日凌晨发布,v0.9.1-beta.1 9 月 11 日凌晨发布一个静态二进制,或 CMake 加 C 编译器wasm3 hello.wasm 跑出 Hello, world需要自备 .wasm 文件;解释执行比编译代码慢 4 到 15 倍。(README)
数据库内部:字节码虚拟机与 MVCCturso · v0.8.0-pre.14 昨天下午发布,正式版仍是 v0.7.1一条 curl 安装脚本在 tursodb 里敲 EXPLAIN SELECT 'hello, world';还没到 1.0,没有触发器、视图、保存点和多进程访问。(README)
虚拟化边界:microVM 与快照分叉smolvm · v1.20.0 今天早上 08:04 发布/dev/kvm,或 macOS、Hyper-V 上的虚拟化平台smolvm machine run --net --image alpine -- uname -aWindows 上暂无分支与检查点;发布物未签名。(README)
三条路径今晚都能上手:一条 curl 取二进制、一条脚本装 CLI、一条脚本装虚拟机工具。先让第一个看得见的结果出现,再决定往源码的哪一层读。

References

  1. 1
    wasm3 README

    github.com

  2. 2
    turso README

    github.com

  3. 3
    smolvm README

    github.com

  4. 4
    smolvm v1.20.0 发布记录

    github.com

  5. 5
    turso v0.8.0-pre.14 发布记录

    github.com

  6. 6
    turso 提交记录

    github.com

  7. 7
    wasm3 v0.9.0 发布记录

    github.com

  8. 8
    wasm3 v0.9.1-beta.1 发布记录

    github.com

  9. 9
    wasm3 提交记录

    github.com

  10. 10
    wasm3 解释器架构文档

    github.com

  11. 11
    wasm3 安装文档

    github.com

  12. 12
    wasm3 示例文档

    github.com

  13. 13
    wasm3 快照文档

    github.com

  14. 14
    turso v0.7.1 发布记录

    github.com

  15. 15
    turso 手册(含内部实现附录)

    github.com

  16. 16
    turso-vdbe-doom-example

    github.com

  17. 17
    smolvm 提交记录

    github.com

  18. 18
    smolvm 分支文档

    github.com

  19. 19
    smolvm 安全模型

    github.com

  20. 20
    smolvm 已知限制

    github.com

GitHub 日推 · 可学可玩
GitHub 日推 · 可学可玩@xinrui.zhao

This story was produced automatically by a channel. One sentence is all it takes for Neodrop to keep producing for you.

Related content

  • 1. wasm3:约 10 KB 内存就能跑起来的 WASM 沙箱
  • 为什么今天看
  • 最值得拆的机制
  • 复现难度:低
  • 最小上手路径
  • 版本动作
  • 2. turso:把 SQL 数据库放进自己的进程
  • 为什么今天看
  • 最值得拆的机制
  • 复现难度:低
  • 最小上手路径
  • 版本动作与限制
  • 3. smolvm:把一台真虚拟机做成能嵌进程序的库
  • 为什么今天看
  • 最值得拆的机制
  • 复现难度:中
  • 最小上手路径
  • 版本动作与限制
  • 怎么选今晚的第一条路径