Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added assets/images/rust-vs-zig.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
105 changes: 105 additions & 0 deletions content/post/2026-04-02-zig-kvdb.smd
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
---
.title = "从 Rust 到 Zig,我写了一个小型 KV 数据库后,终于理解 Zig 到底“怪”在哪",
.date = @date("2026-04-02T13:11:11+0800"),
.author = "lispking",
.layout = "post.shtml",
.draft = false,
---

> 作为一个 Rust 爱好者,在真正深入使用 Zig 之后,我对这门语言的理解发生了转变。

在这篇文章中,我将通过 vibe coding 写的一个 **小型 KV 数据库** 项目来分析 Zig 和 Rust 的异同。

[Rust vs Zig]($image.siteAsset('images/rust-vs-zig.webp'))

## `kvdb`:一款轻量级嵌入式 KV 数据库

首先,来看看我们这个项目:`kvdb`。它是一个轻量级、嵌入式的 **Key-Value 数据库**,采用了 **B-tree 索引** 和 **写前日志(WAL)** 来确保数据持久化。核心功能如下:

* **B-tree** 结构:实现高效的键值对插入、查找和删除。
* **写前日志(WAL)**:确保数据在发生崩溃时的可恢复性。
* **事务支持**:提供基本的事务机制,保证操作的原子性。
* **页面存储**:使用固定大小的 4KB 页进行存储,以优化磁盘 I/O 性能。

然而,尽管这个项目具备了数据库的基本功能,它更像是一个数据库内核的雏形,并不完全是一个成熟的产品。

> 仓库地址:https://github.com/lispking/kvdb

## 设计与实现:从 Rust 的眼光看 Zig

在实现过程中,我很快就意识到,Zig 和 Rust 在设计哲学上有显著的差异。

* Rust 是一门注重“内存安全”的语言,依靠 **ownership** 系统来确保内存安全,无需垃圾回收。
* 而 Zig 则倾向于“暴露真相”,减少语言的魔法,给程序员更多的控制权。

### **Zig 的优点:暴露底层,简洁透明**

Zig 在内存管理、控制流等方面非常直接:

* 没有隐藏的控制流或内存分配,所有操作都是显式的。
* 没有预处理器,也没有宏。它的语法看起来可能有点古怪,但这正是 Zig 希望消除复杂抽象的体现。

这种设计让我在写 `kvdb` 时感受到了极大的灵活性,虽然刚开始时有些不适应。

### **Rust 的优点:内存安全与高抽象**

与此相对,Rust 则通过 **ownership**、**borrowing** 和 **lifetime** 等特性提供了强大的内存安全保证,它通过 **编译期检查** 让程序员避免了许多常见的内存错误。

Rust 会在编译时就要求程序员“遵守规则”,这让我在写代码时不需要担心内存泄漏或者悬空指针等问题。在大型工程中,Rust 的这一点尤其重要。

### **Zig 让我“重新认识”底层控制感**

Zig 的核心哲学是“程序员直接面对控制流和内存管理”。

在我写 `kvdb` 时,我不得不深入思考每一行代码,思考它如何与硬盘交互、如何管理内存、如何确保数据一致性。

Zig 不会通过隐藏一些复杂的实现来“保护”我,而是让我在做每个决定时都能清楚了解背后的原因。

这种直接控制的感觉,非常像我之前在 C 语言中体验到的那种“强烈的底层感”。不过,Zig 的优势在于,它减少了 C 语言中的许多危险操作,同时仍保留了较高的控制力。

## `kvdb`:当前的局限性与改进方向

尽管 `kvdb` 的架构已经具备了基本的数据库功能,但它并不完美。当前最显著的问题是 **B-tree 索引** 部分:

1. **B-tree 目前是单页版本**:`BTree.put()` 方法会检查 root 是否已满,如果已满就返回 `Error.NodeFull`,而没有实现分裂节点的逻辑。这样一来,B-tree 的操作非常简化,无法支持真正的多页树结构。
2. **事务回滚不完全**:`abort()` 方法并没有完全实现 WAL 的回滚机制,而是通过 `reload()` 恢复状态。虽然 WAL 已经在代码中实现,但恢复操作的精细度还远远不够。
3. **WAL 的性能问题**:每次写入 WAL 后都会执行 `sync()`,这对于性能要求较高的场景来说会成为瓶颈。

### **B-tree 需要支持节点分裂与多页操作**

目前,B-tree 只支持单页插入,当根节点满时直接返回 `Error.NodeFull`,缺少分裂、合并和多页遍历的完整实现。接下来,应该完善分裂和合并机制,确保数据库能够处理更大规模的数据集。

### **事务回滚与恢复机制的改进**

`abort()` 方法虽然已经实现了基本的回滚,但还缺少通过 WAL 重放来实现更细粒度的撤销。后续可以进一步增强这一部分,使得系统能够在发生故障时,准确地恢复到正确的状态。

### **WAL 的性能优化**

目前,WAL 的每条记录都进行 `sync()`,这对于写入密集型应用来说可能会影响性能。未来可以通过引入批量提交、事务提交时才刷新 WAL 等方式来优化这一部分。

## 总结

> Zig 的独特魅力与 Rust 的优势

通过这次使用 Zig 写数据库内核的经历,我更加深刻地理解了 **Rust 和 Zig 的设计哲学**。

* Rust 强调编译时安全,强调内存和并发安全。
* 而 Zig 则通过显式的控制流和内存管理,给程序员更多的自由和控制权。

两者各有千秋。

在项目中,我对 Zig 的控制力和透明性有了全新的理解。尽管它的语法有点“古怪”,但它的设计哲学却让我对底层编程有了更深的体验。

### **Rust**:

* 强调安全、性能和并发。
* 适用于长期维护、大型项目。

### **Zig**:

* 更加透明、直接控制底层。
* 适合高性能计算、嵌入式应用。

如果让我选择,我会依然选择 Rust 作为主要开发语言,尤其是在大型系统和复杂并发场景下。

但 Zig 的底层控制感和灵活性,绝对值得在一些特定场景下使用。
Loading