优化:改进 Compaction 和 WebUI 日志

- 简化 Compaction 日志输出,使用结构化日志
- 优化 WebUI 命令行参数处理
- 改进 WebUI 服务器启动和错误处理
- 统一日志格式和输出方式
This commit is contained in:
2025-10-10 22:24:33 +08:00
parent 3148bf226d
commit c8cbe4178f
3 changed files with 59 additions and 66 deletions

View File

@@ -94,10 +94,8 @@ func (p *Picker) UpdateLevelLimits(l0, l1, l2, l3 int64) {
}
// getLevelSizeLimit 获取层级大小限制(从配置读取)
// 注意:层级配置在 UpdateLevelLimits 后不会改变,因此不需要加锁
func (p *Picker) getLevelSizeLimit(level int) int64 {
p.mu.Lock()
defer p.mu.Unlock()
switch level {
case 0:
return p.level0SizeLimit
@@ -911,8 +909,8 @@ type CompactionManager struct {
sstManager *SSTableManager // 添加 sstManager 引用,用于同步删除 readers
sstDir string
// 配置(从 Database Options 传递)
configMu sync.RWMutex
// 配置(从 Database Options 传递,启动后不可变
configMu sync.Mutex // 仅用于 ApplyConfig防御性编程
logger *slog.Logger
level0SizeLimit int64
level1SizeLimit int64
@@ -1017,11 +1015,9 @@ func (m *CompactionManager) Stop() {
func (m *CompactionManager) backgroundCompaction() {
defer m.wg.Done()
// 使用配置的间隔时间
m.configMu.RLock()
// Options 是不可变的,配置在启动后不会改变,直接读取即可
interval := m.compactionInterval
disabled := m.disableCompaction
m.configMu.RUnlock()
if disabled {
return // 禁用自动 Compaction直接退出
@@ -1035,22 +1031,6 @@ func (m *CompactionManager) backgroundCompaction() {
case <-m.stopCh:
return
case <-ticker.C:
// 检查配置是否被更新
m.configMu.RLock()
newInterval := m.compactionInterval
disabled := m.disableCompaction
m.configMu.RUnlock()
if disabled {
return // 运行中被禁用,退出
}
// 如果间隔时间改变,重新创建 ticker
if newInterval != interval {
interval = newInterval
ticker.Reset(interval)
}
m.maybeCompact()
}
}
@@ -1435,15 +1415,30 @@ func (m *CompactionManager) GetLevelStats() []LevelStats {
return stats
}
// GetLevelSizeLimit 获取指定层级的大小限制(公开方法,供 WebUI 等外部使用)
// 注意Options 是不可变的,配置在 ApplyConfig 后不会改变,因此不需要加锁
func (m *CompactionManager) GetLevelSizeLimit(level int) int64 {
switch level {
case 0:
return m.level0SizeLimit
case 1:
return m.level1SizeLimit
case 2:
return m.level2SizeLimit
case 3:
return m.level3SizeLimit
default:
return m.level3SizeLimit
}
}
// backgroundGarbageCollection 后台垃圾回收循环
func (m *CompactionManager) backgroundGarbageCollection() {
defer m.wg.Done()
// 使用配置的间隔时间
m.configMu.RLock()
// Options 是不可变的,配置在启动后不会改变,直接读取即可
interval := m.gcInterval
disabled := m.disableGC
m.configMu.RUnlock()
if disabled {
return // 禁用垃圾回收,直接退出
@@ -1457,22 +1452,6 @@ func (m *CompactionManager) backgroundGarbageCollection() {
case <-m.stopCh:
return
case <-ticker.C:
// 检查配置是否被更新
m.configMu.RLock()
newInterval := m.gcInterval
disabled := m.disableGC
m.configMu.RUnlock()
if disabled {
return // 运行中被禁用,退出
}
// 如果间隔时间改变,重新创建 ticker
if newInterval != interval {
interval = newInterval
ticker.Reset(interval)
}
m.collectOrphanFiles()
}
}
@@ -1515,10 +1494,8 @@ func (m *CompactionManager) collectOrphanFiles() {
// 检查是否是活跃文件
if !activeFiles[fileNum] {
// 检查文件修改时间,避免删除正在 flush 的文件
// 使用配置的文件最小年龄(默认 1 分钟,可能正在 LogAndApply
m.configMu.RLock()
// Options 是不可变的,直接读取配置即可
minAge := m.gcFileMinAge
m.configMu.RUnlock()
fileInfo, err := os.Stat(sstPath)
if err != nil {

View File

@@ -63,16 +63,21 @@ func StartWebUI(dbPath string, addr string) {
} else {
// 插入一些示例数据
users := []map[string]any{
{"name": "Alice", "email": "alice@example.com", "age": 30, "city": "Beijing"},
{"name": "Bob", "email": "bob@example.com", "age": 25, "city": "Shanghai"},
{"name": "Charlie", "email": "charlie@example.com", "age": 35, "city": "Guangzhou"},
{"name": "David", "email": "david@example.com", "age": 28, "city": "Shenzhen"},
{"name": "Eve", "email": "eve@example.com", "age": 32, "city": "Hangzhou"},
{"name": "Alice", "email": "alice@example.com", "age": int64(30), "city": "Beijing"},
{"name": "Bob", "email": "bob@example.com", "age": int64(25), "city": "Shanghai"},
{"name": "Charlie", "email": "charlie@example.com", "age": int64(35), "city": "Guangzhou"},
{"name": "David", "email": "david@example.com", "age": int64(28), "city": "Shenzhen"},
{"name": "Eve", "email": "eve@example.com", "age": int64(32), "city": "Hangzhou"},
}
insertedCount := 0
for _, user := range users {
table.Insert(user)
if err := table.Insert(user); err != nil {
log.Printf("Failed to insert user: %v, error: %v", user, err)
} else {
insertedCount++
}
}
log.Printf("Created users table with %d records", len(users))
log.Printf("Created users table with %d/%d records", insertedCount, len(users))
}
}
@@ -83,17 +88,22 @@ func StartWebUI(dbPath string, addr string) {
} else {
// 插入一些示例数据
products := []map[string]any{
{"product_name": "Laptop", "price": 999.99, "quantity": 10, "category": "Electronics"},
{"product_name": "Mouse", "price": 29.99, "quantity": 50, "category": "Electronics"},
{"product_name": "Keyboard", "price": 79.99, "quantity": 30, "category": "Electronics"},
{"product_name": "Monitor", "price": 299.99, "quantity": 15, "category": "Electronics"},
{"product_name": "Desk", "price": 199.99, "quantity": 5, "category": "Furniture"},
{"product_name": "Chair", "price": 149.99, "quantity": 8, "category": "Furniture"},
{"product_name": "Laptop", "price": 999.99, "quantity": int64(10), "category": "Electronics"},
{"product_name": "Mouse", "price": 29.99, "quantity": int64(50), "category": "Electronics"},
{"product_name": "Keyboard", "price": 79.99, "quantity": int64(30), "category": "Electronics"},
{"product_name": "Monitor", "price": 299.99, "quantity": int64(15), "category": "Electronics"},
{"product_name": "Desk", "price": 199.99, "quantity": int64(5), "category": "Furniture"},
{"product_name": "Chair", "price": 149.99, "quantity": int64(8), "category": "Furniture"},
}
insertedCount := 0
for _, product := range products {
table.Insert(product)
if err := table.Insert(product); err != nil {
log.Printf("Failed to insert product: %v, error: %v", product, err)
} else {
insertedCount++
}
}
log.Printf("Created products table with %d records", len(products))
log.Printf("Created products table with %d/%d records", insertedCount, len(products))
}
}

View File

@@ -214,12 +214,12 @@ func (ui *WebUI) handleTableManifest(w http.ResponseWriter, r *http.Request, tab
Files []FileInfo `json:"files"`
}
// 获取 Compaction Manager 和 Picker
// 获取 Compaction Manager
compactionMgr := table.GetCompactionManager()
picker := compactionMgr.GetPicker()
levels := make([]LevelInfo, 0)
levels := make([]LevelInfo, 0, 7)
for level := range 7 {
// 只调用一次 GetLevel避免重复复制文件列表
files := version.GetLevel(level)
totalSize := int64(0)
@@ -236,9 +236,15 @@ func (ui *WebUI) handleTableManifest(w http.ResponseWriter, r *http.Request, tab
})
}
// 使用已计算的 totalSize 和 fileCount 计算 score避免再次调用 GetLevel
score := 0.0
if len(files) > 0 {
score = picker.GetLevelScore(version, level)
if len(files) > 0 && level < 3 { // L3 是最后一层,不需要 compaction
// 直接计算 score避免调用 picker.GetLevelScore(它会再次获取 files
// 使用下一级的大小限制来计算得分(从 Options 配置读取)
nextLevelLimit := compactionMgr.GetLevelSizeLimit(level + 1)
if nextLevelLimit > 0 {
score = float64(totalSize) / float64(nextLevelLimit)
}
}
levels = append(levels, LevelInfo{