优化:改进 Compaction 和 WebUI 日志
- 简化 Compaction 日志输出,使用结构化日志 - 优化 WebUI 命令行参数处理 - 改进 WebUI 服务器启动和错误处理 - 统一日志格式和输出方式
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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{
|
||||
|
||||
Reference in New Issue
Block a user