package main import ( "TsDB2/DB" "fmt" "os" "runtime" "time" ) type Record struct { DB.BaseModel Name string `vrtx:"maxlen:32"` Age int64 Score int64 Dept string `vrtx:"maxlen:16"` } const dbPath = "integrity_test.db" func main() { os.Remove(dbPath) fmt.Println("==============================================") fmt.Println(" TsDB2 完整性测试 (100万行)") fmt.Println("==============================================") fmt.Println() // ---- Phase 1: 打开数据库 ---- fmt.Println("========== Phase 1: 打开数据库 ==========") db, err := DB.Open(dbPath) if err != nil { panic(err) } db.SetAutoCommitDelay(0) tbl, err := DB.UseTable[Record](db, "test") if err != nil { panic(err) } printMemDisk("初始状态") // ---- Phase 2: 批量插入 100 万行 ---- fmt.Println("\n========== Phase 2: 批量插入 ==========") N := 1_000_000 t0 := time.Now() for i := 1; i <= N; i++ { err := tbl.Insert(&Record{ Name: fmt.Sprintf("Emp%07d", i), Age: int64(20 + (i % 50)), Score: int64(i % 1000), Dept: fmt.Sprintf("dept_%d", i%10), }) if err != nil { panic(fmt.Sprintf("insert row %d: %v", i, err)) } if i%100_000 == 0 { printMemDisk(fmt.Sprintf("插入 %d 行", i)) } } insertTime := time.Since(t0) fmt.Printf(" 插入 %d 行耗时: %.2fs (%.0f rows/s)\n", N, insertTime.Seconds(), float64(N)/insertTime.Seconds()) // ---- Phase 3: Commit ---- fmt.Println("\n========== Phase 3: Commit ==========") t1 := time.Now() if err := db.Commit(); err != nil { panic(err) } commitTime := time.Since(t1) printMemDisk("Commit 后") fmt.Printf(" Commit 耗时: %.2fs\n", commitTime.Seconds()) // ---- Phase 4: 主键精确查找 ---- fmt.Println("\n========== Phase 4: 主键精确查找 ==========") findTests := []uint64{1, 100, 1000, 10000, 100000, 500000, 999999, 1000000} findOK := 0 findFail := 0 t2 := time.Now() for _, id := range findTests { row, err := tbl.Find(id) if err != nil { fmt.Printf(" Find(%d): FAIL (%v)\n", id, err) findFail++ } else { expected := fmt.Sprintf("Emp%07d", id) if row.Name != expected { fmt.Printf(" Find(%d): DATA ERROR (Name=%s, expected %s)\n", id, row.Name, expected) findFail++ } else { findOK++ } } } findTime := time.Since(t2) fmt.Printf(" 查找 %d 个样本: %d 成功, %d 失败, 耗时 %.3fs\n", len(findTests), findOK, findFail, findTime.Seconds()) // ---- Phase 5: 全量扫描验证 ---- fmt.Println("\n========== Phase 5: 全量扫描验证 ==========") t3 := time.Now() count := 0 nameErrors := 0 ageErrors := 0 err = tbl.Select(func(rec *Record) bool { count++ expectedName := fmt.Sprintf("Emp%07d", rec.ID) if rec.Name != expectedName { nameErrors++ } expectedAge := int64(20 + (rec.ID % 50)) if rec.Age != expectedAge { ageErrors++ } return true }) selectTime := time.Since(t3) if err != nil { fmt.Printf(" Select error: %v\n", err) } fmt.Printf(" 扫描行数: %d\n", count) fmt.Printf(" Name 错误: %d\n", nameErrors) fmt.Printf(" Age 错误: %d\n", ageErrors) fmt.Printf(" 全量扫描耗时: %.2fs (%.0f rows/s)\n", selectTime.Seconds(), float64(count)/selectTime.Seconds()) // ---- Phase 6: 条件查询 ---- fmt.Println("\n========== Phase 6: 条件查询 ==========") // Age = 25 (约 20000 行) t4 := time.Now() ageRows, err := tbl.Where("Age", "=", int64(25)).Find() ageQueryTime := time.Since(t4) if err != nil { fmt.Printf(" WHERE Age=25: ERROR (%v)\n", err) } else { fmt.Printf(" WHERE Age=25: %d 行, 耗时 %.3fs\n", len(ageRows), ageQueryTime.Seconds()) } // Score = 500 (约 1000 行) t5 := time.Now() scoreRows, err := tbl.Where("Score", "=", int64(500)).Find() scoreQueryTime := time.Since(t5) if err != nil { fmt.Printf(" WHERE Score=500: ERROR (%v)\n", err) } else { fmt.Printf(" WHERE Score=500: %d 行, 耗时 %.3fs\n", len(scoreRows), scoreQueryTime.Seconds()) } // ---- Phase 7: 更新验证 ---- fmt.Println("\n========== Phase 7: 更新验证 ==========") t6 := time.Now() err = tbl.Update(&Record{ BaseModel: DB.BaseModel{ID: 500}, Dept: "UPDATED", }) if err != nil { fmt.Printf(" Update id=500: FAIL (%v)\n", err) } else { row, err := tbl.Find(500) if err != nil { fmt.Printf(" Update 后 Find(500): FAIL (%v)\n", err) } else if row.Dept == "UPDATED" { fmt.Printf(" Update id=500: OK (Dept=UPDATED)\n") } else { fmt.Printf(" Update id=500: DATA ERROR (Dept=%s)\n", row.Dept) } } db.Commit() fmt.Printf(" 更新+Commit 耗时: %.3fs\n", time.Since(t6).Seconds()) // ---- Phase 8: 删除验证 ---- fmt.Println("\n========== Phase 8: 删除验证 ==========") t7 := time.Now() err = tbl.Delete(999) if err != nil { fmt.Printf(" Delete id=999: FAIL (%v)\n", err) } else { _, err := tbl.Find(999) if err != nil { fmt.Printf(" Delete id=999: OK (已找不到)\n") } else { fmt.Printf(" Delete id=999: FAIL (仍然能找到)\n") } } db.Commit() fmt.Printf(" 删除+Commit 耗时: %.3fs\n", time.Since(t7).Seconds()) // 验证 Count cnt, _ := tbl.Count() fmt.Printf(" 删除后 Count: %d (应为 %d)\n", cnt, N-1) // ---- Phase 9: 关闭重开验证 ---- fmt.Println("\n========== Phase 9: 关闭重开验证 ==========") db.Close() printMemDisk("Close 后") db2, err := DB.Open(dbPath) if err != nil { panic(err) } tbl2, err := DB.UseTable[Record](db2, "test") if err != nil { panic(err) } cnt2, _ := tbl2.Count() fmt.Printf(" 重开后 Count: %d\n", cnt2) // 重开后查找 row, err := tbl2.Find(1) if err != nil { fmt.Printf(" 重开后 Find(1): FAIL (%v)\n", err) } else { fmt.Printf(" 重开后 Find(1): OK (Name=%s)\n", row.Name) } row, err = tbl2.Find(1000000) if err != nil { fmt.Printf(" 重开后 Find(1000000): FAIL (%v)\n", err) } else { fmt.Printf(" 重开后 Find(1000000): OK (Name=%s)\n", row.Name) } db2.Close() // ---- Phase 10: 最终统计 ---- fmt.Println("\n========== Phase 10: 最终统计 ==========") fi, _ := os.Stat(dbPath) sizeMB := float64(fi.Size()) / 1024 / 1024 fmt.Printf(" 数据库文件大小: %.2f MB\n", sizeMB) fmt.Printf(" 每行平均占用: %.0f bytes\n", float64(fi.Size())/float64(N)) fmt.Printf(" 理论最小行大小: ~72 bytes (32+8+8+16+8)\n") fmt.Printf(" 空间放大系数: %.1fx\n", float64(fi.Size())/float64(N)/72.0) var m runtime.MemStats runtime.ReadMemStats(&m) fmt.Printf(" Go 堆内存: %.1f MB\n", float64(m.Alloc)/1024/1024) fmt.Printf(" 总分配内存: %.1f MB\n", float64(m.TotalAlloc)/1024/1024) fmt.Println("\n==============================================") if findFail == 0 && nameErrors == 0 && ageErrors == 0 && cnt2 == int64(N-1) { fmt.Println(" ✅ 完整性测试全部通过!") } else { fmt.Printf(" ❌ 有失败项: Find=%d, Name=%d, Age=%d, Count=%d\n", findFail, nameErrors, ageErrors, int(int64(N-1)-cnt2)) } fmt.Println("==============================================") } func printMemDisk(label string) { fi, _ := os.Stat(dbPath) sizeMB := float64(fi.Size()) / 1024 / 1024 var m runtime.MemStats runtime.ReadMemStats(&m) heapMB := float64(m.Alloc) / 1024 / 1024 fmt.Printf(" [%s] 磁盘: %8.2f MB | Go堆: %6.1f MB | Sys: %6.1f MB | Goroutines: %d\n", label, sizeMB, heapMB, float64(m.Sys)/1024/1024, runtime.NumGoroutine()) }