Embedded LSM-tree storage engine for MoonBit
社区项目,非 MoonBit 官方出品。
「嵌入式」指 embedded database —— 像 SQLite 那样链接进宿主进程, 不是跑在单片机 / MCU 上。它依赖 libc 与带 fsync 语义的文件系统。
put(k, v)
├─→ WAL(顺序追加,fsync 成功后才回 ack)
└─→ MemTable(内存有序数组)
└─ flush ─→ L0 SSTable(可重叠)
└─ 满 4 个 ─→ L1(不重叠)
└─ 超 1 MiB ─→ L2(最底层,墓碑在这里丢弃)let db = @moonlsm.Db::open(@moonlsm.NativeStorage::new("./data"), 8 * 1024 * 1024)
let seq = db.put(b"key", b"value") // 返回即代表已落盘(fsync 已成功)
let v = db.get(b"key") // Bytes?,删除也是 None
let rows = db.scan(b"a", b"z", 100) // [start, end)
db.flush() // memtable -> L0,必要时触发归并
db.close()for i in 0..<200 {
db.put_deferred(key(i), value(i)) |> ignore
}
db.sync() // 返回即代表整批已落盘| 指标 | 数值 |
|---|---|
| 顺序写 | 55 658 ops/s |
| 随机写(覆盖写) | 12 699 ops/s |
| 读延迟 P50 / P99 | 0.053 / 0.111 ms |
| 范围扫描 | 117 483 rows/s |
| 空间放大 | 1.13× |
pub trait Storage {
fn prepare(Self) -> Unit raise LsmError
fn read_file(Self, String) -> Bytes? raise LsmError
fn write_file(Self, String, Bytes) -> Unit raise LsmError
fn append_file(Self, String, Bytes) -> Unit raise LsmError
fn append_frames(Self, String, Array[Bytes]) -> Unit raise LsmError
fn sync_file(Self, String) -> Unit raise LsmError
fn rename_file(Self, String, String) -> Unit raise LsmError
fn remove_file(Self, String) -> Unit raise LsmError
fn file_size(Self, String) -> Int? raise LsmError
fn list_files(Self, String) -> Array[String] raise LsmError
}pub suberror LsmError {
Io(String)
Corruption(String)
Closed
}type AppendHandle分配 seq → 追加 WAL 帧 → fsync WAL → 写 memtable → 可能刷盘1. 算出 src 层覆盖的 key 范围,找出 dst 层里与之重叠的文件
2. 读入全部输入条目 → 归并(同 key 取 seq 最大)→ 到最底层时丢墓碑
3. 按大小切成若干 SST,逐个写盘 + fsync
4. 更新 version:src 清空、dst = 未参与的文件 + 新文件
5. persist_version() ← 原子点
6. 删除输入文件
7. 重建内存里的 ssts 视图1. 写 sst/<n>.sst → fsync (数据先变成持久事实)
2. 建 wal/<m>.wal 空文件 → fsync (新 WAL 必须存在才能被 MANIFEST 引用)
3. 更新 version(L0 加一项、log_number=m、last_seq)
4. persist_version() (写 MANIFEST + 原子切换 CURRENT)
5. 删旧 WAL (CURRENT 已经不再指向它)
6. 清 memtable / pendingfor item in batch { db.put_deferred(k, v) } // 只追加,不刷盘
db.sync() // 一次刷盘
// ← 到这里才能返回 okpub struct Entry {
key : Bytes
value : Bytes
seq : UInt64
deleted : Bool
}pub struct KvPair {
key : Bytes
value : Bytes
}impl Storage for MemStoragefn MemStorage::append_frames(self : MemStorage, name : String, chunks : Array[Bytes]) -> Unit raise LsmErrorimpl Storage for NativeStorage循环 append_file : 200 × 5.9ms ≈ 1180 ms
append_frames : 1 × 5.9ms + 200 × ~0 ≈ 6 msfn NativeStorage::append_file(self : NativeStorage, name : String, data : Bytes) -> Unit raise LsmErrorfn NativeStorage::append_frames(self : NativeStorage, name : String, chunks : Array[Bytes]) -> Unit raise LsmErrorfn NativeStorage::rename_file(self : NativeStorage, from : String, to : String) -> Unit raise LsmErrorfn NativeStorage::write_file(self : NativeStorage, name : String, data : Bytes) -> Unit raise LsmErrorpub enum Op {
Put(Bytes, Bytes)
Delete(Bytes)
}type Sourcepub struct SstFooter {
filter_offset : Int
filter_size : Int
index_offset : Int
index_size : Int
smallest_seq : UInt64
version : Int
}pub struct SstMeta {
number : UInt64
file_size : UInt64
smallest : Bytes
largest : Bytes
}pub struct SstReader {
data : Bytes
footer : SstFooter
index : Block
bloom : BloomFilter
range : (Bytes, Bytes)?
}pub struct SstWriter {
out : Buffer
block : BlockBuilder
index : BlockBuilder
count : Int
block_count : Int
smallest_seq : UInt64
keys : Array[Bytes]
version : Int
}let BLOCK_CRC_SIZE : Intlet BLOCK_TARGET_SIZE : Intlet L0_COMPACT_TRIGGER : Intlet LEVEL1_TARGET_BYTES : UInt64let SST_VERSION : Intlet SST_VERSION_V1 : Intlet WAL_DIR : String<db_path>/
├── CURRENT # 文本,内容是当前 MANIFEST 的文件名 + "\n"
├── MANIFEST-000007 # 版本快照
├── wal/
│ └── 000008.wal # 活跃 WAL
└── sst/
└── 000004.sstlet WAL_MAX_RECORD : Intfn bloom_hashes(key : Bytes) -> (UInt64, UInt64)fn compare_bytes(a : Bytes, b : Bytes) -> Intfn compare_strings(a : String, b : String) -> Intfn crc32c(data : Bytes) -> UInt64fn flush_stdio() -> Unitfn level_target_bytes(lv : Int) -> UInt64fn read_fixed32(data : Bytes, pos : Int) -> Int?fn read_fixed32u(data : Bytes, pos : Int) -> UInt64?fn read_varint32(data : Bytes, pos : Int, limit : Int) -> (Int, Int)?fn read_varint64(data : Bytes, pos : Int, limit : Int) -> (UInt64, Int)?fn should_compact_level(lv : Int, sst_count : Int, bytes : UInt64) -> BoolInstall
Download zipEmbedded LSM-tree storage engine for MoonBit