Initial commit: SRDB - High-performance LSM-Tree database

- Core engine with MemTable, SST, WAL
- B+Tree indexing for SST files  
- Leveled compaction strategy
- Multi-table database management
- Schema validation and secondary indexes
- Query builder with complex conditions
- Web UI with HTMX for data visualization
- Command-line tools for diagnostics
This commit is contained in:
2025-10-08 06:38:12 +08:00
commit ae87c38776
61 changed files with 15475 additions and 0 deletions

View File

@@ -0,0 +1,48 @@
package manifest
import (
"encoding/binary"
"io"
)
// Reader MANIFEST 读取器
type Reader struct {
file io.Reader
}
// NewReader 创建 MANIFEST 读取器
func NewReader(file io.Reader) *Reader {
return &Reader{
file: file,
}
}
// ReadEdit 读取版本变更
func (r *Reader) ReadEdit() (*VersionEdit, error) {
// 读取 CRC32 和 Length
header := make([]byte, 8)
_, err := io.ReadFull(r.file, header)
if err != nil {
return nil, err
}
// 读取长度
length := binary.LittleEndian.Uint32(header[4:8])
// 读取数据
data := make([]byte, 8+length)
copy(data[0:8], header)
_, err = io.ReadFull(r.file, data[8:])
if err != nil {
return nil, err
}
// 解码
edit := NewVersionEdit()
err = edit.Decode(data)
if err != nil {
return nil, err
}
return edit, nil
}

View File

@@ -0,0 +1,35 @@
package manifest
import (
"io"
"sync"
)
// Writer MANIFEST 写入器
type Writer struct {
file io.Writer
mu sync.Mutex
}
// NewWriter 创建 MANIFEST 写入器
func NewWriter(file io.Writer) *Writer {
return &Writer{
file: file,
}
}
// WriteEdit 写入版本变更
func (w *Writer) WriteEdit(edit *VersionEdit) error {
w.mu.Lock()
defer w.mu.Unlock()
// 编码
data, err := edit.Encode()
if err != nil {
return err
}
// 写入
_, err = w.file.Write(data)
return err
}

187
manifest/version.go Normal file
View File

@@ -0,0 +1,187 @@
package manifest
import (
"fmt"
"sync"
)
// FileMetadata SST 文件元数据
type FileMetadata struct {
FileNumber int64 // 文件编号
Level int // 所在层级 (0-6)
FileSize int64 // 文件大小
MinKey int64 // 最小 key
MaxKey int64 // 最大 key
RowCount int64 // 行数
}
const (
NumLevels = 7 // L0-L6
)
// Version 数据库的一个版本快照
type Version struct {
// 分层存储 SST 文件 (L0-L6)
Levels [NumLevels][]*FileMetadata
// 下一个文件编号
NextFileNumber int64
// 最后序列号
LastSequence int64
// 版本号
VersionNumber int64
mu sync.RWMutex
}
// NewVersion 创建新版本
func NewVersion() *Version {
v := &Version{
NextFileNumber: 1,
LastSequence: 0,
VersionNumber: 0,
}
// 初始化每一层
for i := 0; i < NumLevels; i++ {
v.Levels[i] = make([]*FileMetadata, 0)
}
return v
}
// Clone 克隆版本
func (v *Version) Clone() *Version {
v.mu.RLock()
defer v.mu.RUnlock()
newVersion := &Version{
NextFileNumber: v.NextFileNumber,
LastSequence: v.LastSequence,
VersionNumber: v.VersionNumber + 1,
}
// 克隆每一层
for level := 0; level < NumLevels; level++ {
newVersion.Levels[level] = make([]*FileMetadata, len(v.Levels[level]))
copy(newVersion.Levels[level], v.Levels[level])
}
return newVersion
}
// Apply 应用版本变更
func (v *Version) Apply(edit *VersionEdit) {
v.mu.Lock()
defer v.mu.Unlock()
// 删除文件(按层级删除)
if len(edit.DeletedFiles) > 0 {
deleteSet := make(map[int64]bool)
for _, fileNum := range edit.DeletedFiles {
deleteSet[fileNum] = true
}
// 遍历每一层,删除文件
for level := 0; level < NumLevels; level++ {
newFiles := make([]*FileMetadata, 0)
deletedCount := 0
for _, file := range v.Levels[level] {
if !deleteSet[file.FileNumber] {
newFiles = append(newFiles, file)
} else {
deletedCount++
}
}
if deletedCount > 0 {
fmt.Printf("[Version.Apply] L%d: deleted %d files\n", level, deletedCount)
}
v.Levels[level] = newFiles
}
}
// 添加文件(按层级添加)
if len(edit.AddedFiles) > 0 {
for _, file := range edit.AddedFiles {
if file.Level >= 0 && file.Level < NumLevels {
fmt.Printf("[Version.Apply] Adding file #%d to L%d (keys %d-%d)\n",
file.FileNumber, file.Level, file.MinKey, file.MaxKey)
v.Levels[file.Level] = append(v.Levels[file.Level], file)
}
}
}
// 更新下一个文件编号
if edit.NextFileNumber != nil {
v.NextFileNumber = *edit.NextFileNumber
}
// 更新最后序列号
if edit.LastSequence != nil {
v.LastSequence = *edit.LastSequence
}
}
// GetLevel 获取指定层级的文件
func (v *Version) GetLevel(level int) []*FileMetadata {
v.mu.RLock()
defer v.mu.RUnlock()
if level < 0 || level >= NumLevels {
return nil
}
files := make([]*FileMetadata, len(v.Levels[level]))
copy(files, v.Levels[level])
return files
}
// GetSSTFiles 获取所有 SST 文件(副本,兼容旧接口)
func (v *Version) GetSSTFiles() []*FileMetadata {
v.mu.RLock()
defer v.mu.RUnlock()
// 收集所有层级的文件
allFiles := make([]*FileMetadata, 0)
for level := 0; level < NumLevels; level++ {
allFiles = append(allFiles, v.Levels[level]...)
}
return allFiles
}
// GetNextFileNumber 获取下一个文件编号
func (v *Version) GetNextFileNumber() int64 {
v.mu.RLock()
defer v.mu.RUnlock()
return v.NextFileNumber
}
// GetLastSequence 获取最后序列号
func (v *Version) GetLastSequence() int64 {
v.mu.RLock()
defer v.mu.RUnlock()
return v.LastSequence
}
// GetFileCount 获取文件数量
func (v *Version) GetFileCount() int {
v.mu.RLock()
defer v.mu.RUnlock()
total := 0
for level := 0; level < NumLevels; level++ {
total += len(v.Levels[level])
}
return total
}
// GetLevelFileCount 获取指定层级的文件数量
func (v *Version) GetLevelFileCount(level int) int {
v.mu.RLock()
defer v.mu.RUnlock()
if level < 0 || level >= NumLevels {
return 0
}
return len(v.Levels[level])
}

114
manifest/version_edit.go Normal file
View File

@@ -0,0 +1,114 @@
package manifest
import (
"encoding/binary"
"encoding/json"
"hash/crc32"
"io"
)
// EditType 变更类型
type EditType byte
const (
EditTypeAddFile EditType = 1 // 添加文件
EditTypeDeleteFile EditType = 2 // 删除文件
EditTypeSetNextFile EditType = 3 // 设置下一个文件编号
EditTypeSetLastSeq EditType = 4 // 设置最后序列号
)
// VersionEdit 版本变更记录
type VersionEdit struct {
// 添加的文件
AddedFiles []*FileMetadata
// 删除的文件(文件编号列表)
DeletedFiles []int64
// 下一个文件编号
NextFileNumber *int64
// 最后序列号
LastSequence *int64
}
// NewVersionEdit 创建版本变更
func NewVersionEdit() *VersionEdit {
return &VersionEdit{
AddedFiles: make([]*FileMetadata, 0),
DeletedFiles: make([]int64, 0),
}
}
// AddFile 添加文件
func (e *VersionEdit) AddFile(file *FileMetadata) {
e.AddedFiles = append(e.AddedFiles, file)
}
// DeleteFile 删除文件
func (e *VersionEdit) DeleteFile(fileNumber int64) {
e.DeletedFiles = append(e.DeletedFiles, fileNumber)
}
// SetNextFileNumber 设置下一个文件编号
func (e *VersionEdit) SetNextFileNumber(num int64) {
e.NextFileNumber = &num
}
// SetLastSequence 设置最后序列号
func (e *VersionEdit) SetLastSequence(seq int64) {
e.LastSequence = &seq
}
// Encode 编码为字节
func (e *VersionEdit) Encode() ([]byte, error) {
// 使用 JSON 编码(简单实现)
data, err := json.Marshal(e)
if err != nil {
return nil, err
}
// 格式: CRC32(4) + Length(4) + Data
totalLen := 8 + len(data)
buf := make([]byte, totalLen)
// 计算 CRC32
crc := crc32.ChecksumIEEE(data)
binary.LittleEndian.PutUint32(buf[0:4], crc)
// 写入长度
binary.LittleEndian.PutUint32(buf[4:8], uint32(len(data)))
// 写入数据
copy(buf[8:], data)
return buf, nil
}
// Decode 从字节解码
func (e *VersionEdit) Decode(data []byte) error {
if len(data) < 8 {
return io.ErrUnexpectedEOF
}
// 读取 CRC32
crc := binary.LittleEndian.Uint32(data[0:4])
// 读取长度
length := binary.LittleEndian.Uint32(data[4:8])
if len(data) < int(8+length) {
return io.ErrUnexpectedEOF
}
// 读取数据
editData := data[8 : 8+length]
// 验证 CRC32
if crc32.ChecksumIEEE(editData) != crc {
return io.ErrUnexpectedEOF
}
// JSON 解码
return json.Unmarshal(editData, e)
}

251
manifest/version_set.go Normal file
View File

@@ -0,0 +1,251 @@
package manifest
import (
"fmt"
"io"
"os"
"path/filepath"
"strings"
"sync"
"sync/atomic"
)
// VersionSet 版本集合管理器
type VersionSet struct {
// 当前版本
current *Version
// MANIFEST 文件
manifestFile *os.File
manifestWriter *Writer
manifestNumber int64
// 下一个文件编号
nextFileNumber atomic.Int64
// 最后序列号
lastSequence atomic.Int64
// 目录
dir string
// 锁
mu sync.RWMutex
}
// NewVersionSet 创建版本集合
func NewVersionSet(dir string) (*VersionSet, error) {
vs := &VersionSet{
dir: dir,
}
// 确保目录存在
err := os.MkdirAll(dir, 0755)
if err != nil {
return nil, err
}
// 读取 CURRENT 文件
currentFile := filepath.Join(dir, "CURRENT")
data, err := os.ReadFile(currentFile)
if err != nil {
// CURRENT 不存在,创建新的 MANIFEST
return vs, vs.createNewManifest()
}
// 读取 MANIFEST 文件
manifestName := strings.TrimSpace(string(data))
manifestPath := filepath.Join(dir, manifestName)
// 恢复版本信息
version, err := vs.recoverFromManifest(manifestPath)
if err != nil {
return nil, err
}
vs.current = version
vs.nextFileNumber.Store(version.NextFileNumber)
vs.lastSequence.Store(version.LastSequence)
// 解析 MANIFEST 编号
fmt.Sscanf(manifestName, "MANIFEST-%d", &vs.manifestNumber)
// 打开 MANIFEST 用于追加
file, err := os.OpenFile(manifestPath, os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
return nil, err
}
vs.manifestFile = file
vs.manifestWriter = NewWriter(file)
return vs, nil
}
// createNewManifest 创建新的 MANIFEST
func (vs *VersionSet) createNewManifest() error {
// 生成新的 MANIFEST 文件名
vs.manifestNumber = vs.nextFileNumber.Add(1)
manifestName := fmt.Sprintf("MANIFEST-%06d", vs.manifestNumber)
manifestPath := filepath.Join(vs.dir, manifestName)
// 创建 MANIFEST 文件
file, err := os.Create(manifestPath)
if err != nil {
return err
}
vs.manifestFile = file
vs.manifestWriter = NewWriter(file)
// 创建初始版本
vs.current = NewVersion()
// 写入初始版本
edit := NewVersionEdit()
nextFile := vs.manifestNumber
edit.SetNextFileNumber(nextFile)
lastSeq := int64(0)
edit.SetLastSequence(lastSeq)
err = vs.manifestWriter.WriteEdit(edit)
if err != nil {
return err
}
// 同步到磁盘
err = vs.manifestFile.Sync()
if err != nil {
return err
}
// 更新 CURRENT 文件
return vs.updateCurrent(manifestName)
}
// recoverFromManifest 从 MANIFEST 恢复版本
func (vs *VersionSet) recoverFromManifest(manifestPath string) (*Version, error) {
// 打开 MANIFEST 文件
file, err := os.Open(manifestPath)
if err != nil {
return nil, err
}
defer file.Close()
reader := NewReader(file)
// 创建初始版本
version := NewVersion()
// 读取所有 VersionEdit
for {
edit, err := reader.ReadEdit()
if err == io.EOF {
break
}
if err != nil {
return nil, err
}
// 应用变更
version.Apply(edit)
}
return version, nil
}
// updateCurrent 更新 CURRENT 文件
func (vs *VersionSet) updateCurrent(manifestName string) error {
currentPath := filepath.Join(vs.dir, "CURRENT")
tmpPath := currentPath + ".tmp"
// 1. 写入临时文件
err := os.WriteFile(tmpPath, []byte(manifestName+"\n"), 0644)
if err != nil {
return err
}
// 2. 原子性重命名
err = os.Rename(tmpPath, currentPath)
if err != nil {
os.Remove(tmpPath)
return err
}
return nil
}
// LogAndApply 记录并应用版本变更
func (vs *VersionSet) LogAndApply(edit *VersionEdit) error {
vs.mu.Lock()
defer vs.mu.Unlock()
// 1. 创建新版本
newVersion := vs.current.Clone()
// 2. 应用变更
newVersion.Apply(edit)
// 3. 写入 MANIFEST
err := vs.manifestWriter.WriteEdit(edit)
if err != nil {
return err
}
// 4. 同步到磁盘
err = vs.manifestFile.Sync()
if err != nil {
return err
}
// 5. 更新当前版本
vs.current = newVersion
// 6. 更新原子变量
if edit.NextFileNumber != nil {
vs.nextFileNumber.Store(*edit.NextFileNumber)
}
if edit.LastSequence != nil {
vs.lastSequence.Store(*edit.LastSequence)
}
return nil
}
// GetCurrent 获取当前版本
func (vs *VersionSet) GetCurrent() *Version {
vs.mu.RLock()
defer vs.mu.RUnlock()
return vs.current
}
// GetNextFileNumber 获取下一个文件编号
func (vs *VersionSet) GetNextFileNumber() int64 {
return vs.nextFileNumber.Load()
}
// AllocateFileNumber 分配文件编号
func (vs *VersionSet) AllocateFileNumber() int64 {
return vs.nextFileNumber.Add(1)
}
// GetLastSequence 获取最后序列号
func (vs *VersionSet) GetLastSequence() int64 {
return vs.lastSequence.Load()
}
// SetLastSequence 设置最后序列号
func (vs *VersionSet) SetLastSequence(seq int64) {
vs.lastSequence.Store(seq)
}
// Close 关闭 VersionSet
func (vs *VersionSet) Close() error {
vs.mu.Lock()
defer vs.mu.Unlock()
if vs.manifestFile != nil {
return vs.manifestFile.Close()
}
return nil
}

View File

@@ -0,0 +1,220 @@
package manifest
import (
"os"
"testing"
)
func TestVersionSetBasic(t *testing.T) {
dir := "./test_manifest"
os.RemoveAll(dir)
defer os.RemoveAll(dir)
// 创建 VersionSet
vs, err := NewVersionSet(dir)
if err != nil {
t.Fatalf("NewVersionSet failed: %v", err)
}
defer vs.Close()
// 检查初始状态
version := vs.GetCurrent()
if version.GetFileCount() != 0 {
t.Errorf("Expected 0 files, got %d", version.GetFileCount())
}
t.Log("VersionSet basic test passed!")
}
func TestVersionSetAddFile(t *testing.T) {
dir := "./test_manifest_add"
os.RemoveAll(dir)
defer os.RemoveAll(dir)
vs, err := NewVersionSet(dir)
if err != nil {
t.Fatalf("NewVersionSet failed: %v", err)
}
defer vs.Close()
// 添加文件
edit := NewVersionEdit()
edit.AddFile(&FileMetadata{
FileNumber: 1,
FileSize: 1024,
MinKey: 1,
MaxKey: 100,
RowCount: 100,
})
err = vs.LogAndApply(edit)
if err != nil {
t.Fatalf("LogAndApply failed: %v", err)
}
// 检查
version := vs.GetCurrent()
if version.GetFileCount() != 1 {
t.Errorf("Expected 1 file, got %d", version.GetFileCount())
}
files := version.GetSSTFiles()
if files[0].FileNumber != 1 {
t.Errorf("Expected file number 1, got %d", files[0].FileNumber)
}
t.Log("VersionSet add file test passed!")
}
func TestVersionSetDeleteFile(t *testing.T) {
dir := "./test_manifest_delete"
os.RemoveAll(dir)
defer os.RemoveAll(dir)
vs, err := NewVersionSet(dir)
if err != nil {
t.Fatalf("NewVersionSet failed: %v", err)
}
defer vs.Close()
// 添加两个文件
edit1 := NewVersionEdit()
edit1.AddFile(&FileMetadata{FileNumber: 1, FileSize: 1024, MinKey: 1, MaxKey: 100, RowCount: 100})
edit1.AddFile(&FileMetadata{FileNumber: 2, FileSize: 2048, MinKey: 101, MaxKey: 200, RowCount: 100})
vs.LogAndApply(edit1)
// 删除一个文件
edit2 := NewVersionEdit()
edit2.DeleteFile(1)
err = vs.LogAndApply(edit2)
if err != nil {
t.Fatalf("LogAndApply failed: %v", err)
}
// 检查
version := vs.GetCurrent()
if version.GetFileCount() != 1 {
t.Errorf("Expected 1 file, got %d", version.GetFileCount())
}
files := version.GetSSTFiles()
if files[0].FileNumber != 2 {
t.Errorf("Expected file number 2, got %d", files[0].FileNumber)
}
t.Log("VersionSet delete file test passed!")
}
func TestVersionSetRecover(t *testing.T) {
dir := "./test_manifest_recover"
os.RemoveAll(dir)
defer os.RemoveAll(dir)
// 第一次:创建并添加文件
vs1, err := NewVersionSet(dir)
if err != nil {
t.Fatalf("NewVersionSet failed: %v", err)
}
edit := NewVersionEdit()
edit.AddFile(&FileMetadata{FileNumber: 1, FileSize: 1024, MinKey: 1, MaxKey: 100, RowCount: 100})
edit.AddFile(&FileMetadata{FileNumber: 2, FileSize: 2048, MinKey: 101, MaxKey: 200, RowCount: 100})
vs1.LogAndApply(edit)
vs1.Close()
// 第二次:重新打开并恢复
vs2, err := NewVersionSet(dir)
if err != nil {
t.Fatalf("NewVersionSet recover failed: %v", err)
}
defer vs2.Close()
// 检查恢复的数据
version := vs2.GetCurrent()
if version.GetFileCount() != 2 {
t.Errorf("Expected 2 files after recover, got %d", version.GetFileCount())
}
files := version.GetSSTFiles()
if files[0].FileNumber != 1 || files[1].FileNumber != 2 {
t.Errorf("File numbers not correct after recover")
}
t.Log("VersionSet recover test passed!")
}
func TestVersionSetMultipleEdits(t *testing.T) {
dir := "./test_manifest_multiple"
os.RemoveAll(dir)
defer os.RemoveAll(dir)
vs, err := NewVersionSet(dir)
if err != nil {
t.Fatalf("NewVersionSet failed: %v", err)
}
defer vs.Close()
// 多次变更
for i := int64(1); i <= 10; i++ {
edit := NewVersionEdit()
edit.AddFile(&FileMetadata{
FileNumber: i,
FileSize: 1024 * i,
MinKey: (i-1)*100 + 1,
MaxKey: i * 100,
RowCount: 100,
})
err = vs.LogAndApply(edit)
if err != nil {
t.Fatalf("LogAndApply failed: %v", err)
}
}
// 检查
version := vs.GetCurrent()
if version.GetFileCount() != 10 {
t.Errorf("Expected 10 files, got %d", version.GetFileCount())
}
t.Log("VersionSet multiple edits test passed!")
}
func TestVersionEditEncodeDecode(t *testing.T) {
// 创建 VersionEdit
edit1 := NewVersionEdit()
edit1.AddFile(&FileMetadata{FileNumber: 1, FileSize: 1024, MinKey: 1, MaxKey: 100, RowCount: 100})
edit1.DeleteFile(2)
nextFile := int64(10)
edit1.SetNextFileNumber(nextFile)
lastSeq := int64(1000)
edit1.SetLastSequence(lastSeq)
// 编码
data, err := edit1.Encode()
if err != nil {
t.Fatalf("Encode failed: %v", err)
}
// 解码
edit2 := NewVersionEdit()
err = edit2.Decode(data)
if err != nil {
t.Fatalf("Decode failed: %v", err)
}
// 检查
if len(edit2.AddedFiles) != 1 {
t.Errorf("Expected 1 added file, got %d", len(edit2.AddedFiles))
}
if len(edit2.DeletedFiles) != 1 {
t.Errorf("Expected 1 deleted file, got %d", len(edit2.DeletedFiles))
}
if *edit2.NextFileNumber != 10 {
t.Errorf("Expected NextFileNumber 10, got %d", *edit2.NextFileNumber)
}
if *edit2.LastSequence != 1000 {
t.Errorf("Expected LastSequence 1000, got %d", *edit2.LastSequence)
}
t.Log("VersionEdit encode/decode test passed!")
}