Skip to content

Commit 733cd0e

Browse files
authored
Add post: 从 Rust 到 Zig,我写了一个小型 KV 数据库后,终于理解 Zig 到底'怪'在哪 (#114)
* Add post: 从 Rust 到 Zig,我写了一个小型 KV 数据库后,终于理解 Zig 到底'怪'在哪 * Fix image syntax and clarify wording in zig-kvdb post * Revert image syntax in zig-kvdb post
1 parent 7e1c8a8 commit 733cd0e

File tree

2 files changed

+105
-0
lines changed

2 files changed

+105
-0
lines changed

assets/images/rust-vs-zig.webp

44.1 KB
Loading
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
---
2+
.title = "从 Rust 到 Zig,我写了一个小型 KV 数据库后,终于理解 Zig 到底“怪”在哪",
3+
.date = @date("2026-04-02T13:11:11+0800"),
4+
.author = "lispking",
5+
.layout = "post.shtml",
6+
.draft = false,
7+
---
8+
9+
> 作为一个 Rust 爱好者,在真正深入使用 Zig 之后,我对这门语言的理解发生了转变。
10+
11+
在这篇文章中,我将通过 vibe coding 写的一个 **小型 KV 数据库** 项目来分析 Zig 和 Rust 的异同。
12+
13+
[Rust vs Zig]($image.siteAsset('images/rust-vs-zig.webp'))
14+
15+
## `kvdb`:一款轻量级嵌入式 KV 数据库
16+
17+
首先,来看看我们这个项目:`kvdb`。它是一个轻量级、嵌入式的 **Key-Value 数据库**,采用了 **B-tree 索引** 和 **写前日志(WAL)** 来确保数据持久化。核心功能如下:
18+
19+
* **B-tree** 结构:实现高效的键值对插入、查找和删除。
20+
* **写前日志(WAL)**:确保数据在发生崩溃时的可恢复性。
21+
* **事务支持**:提供基本的事务机制,保证操作的原子性。
22+
* **页面存储**:使用固定大小的 4KB 页进行存储,以优化磁盘 I/O 性能。
23+
24+
然而,尽管这个项目具备了数据库的基本功能,它更像是一个数据库内核的雏形,并不完全是一个成熟的产品。
25+
26+
> 仓库地址:https://github.com/lispking/kvdb
27+
28+
## 设计与实现:从 Rust 的眼光看 Zig
29+
30+
在实现过程中,我很快就意识到,Zig 和 Rust 在设计哲学上有显著的差异。
31+
32+
* Rust 是一门注重“内存安全”的语言,依靠 **ownership** 系统来确保内存安全,无需垃圾回收。
33+
* 而 Zig 则倾向于“暴露真相”,减少语言的魔法,给程序员更多的控制权。
34+
35+
### **Zig 的优点:暴露底层,简洁透明**
36+
37+
Zig 在内存管理、控制流等方面非常直接:
38+
39+
* 没有隐藏的控制流或内存分配,所有操作都是显式的。
40+
* 没有预处理器,也没有宏。它的语法看起来可能有点古怪,但这正是 Zig 希望消除复杂抽象的体现。
41+
42+
这种设计让我在写 `kvdb` 时感受到了极大的灵活性,虽然刚开始时有些不适应。
43+
44+
### **Rust 的优点:内存安全与高抽象**
45+
46+
与此相对,Rust 则通过 **ownership**、**borrowing** 和 **lifetime** 等特性提供了强大的内存安全保证,它通过 **编译期检查** 让程序员避免了许多常见的内存错误。
47+
48+
Rust 会在编译时就要求程序员“遵守规则”,这让我在写代码时不需要担心内存泄漏或者悬空指针等问题。在大型工程中,Rust 的这一点尤其重要。
49+
50+
### **Zig 让我“重新认识”底层控制感**
51+
52+
Zig 的核心哲学是“程序员直接面对控制流和内存管理”。
53+
54+
在我写 `kvdb` 时,我不得不深入思考每一行代码,思考它如何与硬盘交互、如何管理内存、如何确保数据一致性。
55+
56+
Zig 不会通过隐藏一些复杂的实现来“保护”我,而是让我在做每个决定时都能清楚了解背后的原因。
57+
58+
这种直接控制的感觉,非常像我之前在 C 语言中体验到的那种“强烈的底层感”。不过,Zig 的优势在于,它减少了 C 语言中的许多危险操作,同时仍保留了较高的控制力。
59+
60+
## `kvdb`:当前的局限性与改进方向
61+
62+
尽管 `kvdb` 的架构已经具备了基本的数据库功能,但它并不完美。当前最显著的问题是 **B-tree 索引** 部分:
63+
64+
1. **B-tree 目前是单页版本**:`BTree.put()` 方法会检查 root 是否已满,如果已满就返回 `Error.NodeFull`,而没有实现分裂节点的逻辑。这样一来,B-tree 的操作非常简化,无法支持真正的多页树结构。
65+
2. **事务回滚不完全**:`abort()` 方法并没有完全实现 WAL 的回滚机制,而是通过 `reload()` 恢复状态。虽然 WAL 已经在代码中实现,但恢复操作的精细度还远远不够。
66+
3. **WAL 的性能问题**:每次写入 WAL 后都会执行 `sync()`,这对于性能要求较高的场景来说会成为瓶颈。
67+
68+
### **B-tree 需要支持节点分裂与多页操作**
69+
70+
目前,B-tree 只支持单页插入,当根节点满时直接返回 `Error.NodeFull`,缺少分裂、合并和多页遍历的完整实现。接下来,应该完善分裂和合并机制,确保数据库能够处理更大规模的数据集。
71+
72+
### **事务回滚与恢复机制的改进**
73+
74+
`abort()` 方法虽然已经实现了基本的回滚,但还缺少通过 WAL 重放来实现更细粒度的撤销。后续可以进一步增强这一部分,使得系统能够在发生故障时,准确地恢复到正确的状态。
75+
76+
### **WAL 的性能优化**
77+
78+
目前,WAL 的每条记录都进行 `sync()`,这对于写入密集型应用来说可能会影响性能。未来可以通过引入批量提交、事务提交时才刷新 WAL 等方式来优化这一部分。
79+
80+
## 总结
81+
82+
> Zig 的独特魅力与 Rust 的优势
83+
84+
通过这次使用 Zig 写数据库内核的经历,我更加深刻地理解了 **Rust 和 Zig 的设计哲学**。
85+
86+
* Rust 强调编译时安全,强调内存和并发安全。
87+
* 而 Zig 则通过显式的控制流和内存管理,给程序员更多的自由和控制权。
88+
89+
两者各有千秋。
90+
91+
在项目中,我对 Zig 的控制力和透明性有了全新的理解。尽管它的语法有点“古怪”,但它的设计哲学却让我对底层编程有了更深的体验。
92+
93+
### **Rust**:
94+
95+
* 强调安全、性能和并发。
96+
* 适用于长期维护、大型项目。
97+
98+
### **Zig**:
99+
100+
* 更加透明、直接控制底层。
101+
* 适合高性能计算、嵌入式应用。
102+
103+
如果让我选择,我会依然选择 Rust 作为主要开发语言,尤其是在大型系统和复杂并发场景下。
104+
105+
但 Zig 的底层控制感和灵活性,绝对值得在一些特定场景下使用。

0 commit comments

Comments
 (0)