main.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. package main
  2. import (
  3. "git.ycl.pub/yl32940/TsDB2/DB"
  4. "fmt"
  5. "os"
  6. "runtime"
  7. "time"
  8. )
  9. // ========== 数据模型 ==========
  10. type Record struct {
  11. DB.BaseModel
  12. Name string `vrtx:"maxlen:32"`
  13. Age int64
  14. Score int64
  15. Dept string `vrtx:"maxlen:16"`
  16. }
  17. const dbPath = "memtest.db"
  18. const N = 200_000 // 20万行测试
  19. func main() {
  20. os.Remove(dbPath)
  21. // ========== 1. 打开数据库, 禁用防抖, 只靠脏页阈值 ==========
  22. fmt.Println("========== 1. 打开数据库 ==========")
  23. db, err := DB.Open(dbPath)
  24. if err != nil {
  25. panic(err)
  26. }
  27. // 禁用防抖自动提交 — 测试脏页阈值是否独立工作
  28. db.SetAutoCommitDelay(0)
  29. tbl, err := DB.UseTable[Record](db, "test")
  30. if err != nil {
  31. panic(err)
  32. }
  33. fmt.Println(" 表创建/打开成功")
  34. // ========== 2. 批量插入 + 内存监控 ==========
  35. fmt.Printf("\n========== 2. 插入 %d 行 (禁用防抖, 仅脏页阈值触发) ==========\n", N)
  36. runtime.GC()
  37. var mStart runtime.MemStats
  38. runtime.ReadMemStats(&mStart)
  39. t0 := time.Now()
  40. lastCommitTime := t0
  41. commitCount := 0
  42. for i := 0; i < N; i++ {
  43. rec := &Record{
  44. Name: fmt.Sprintf("user_%d", i),
  45. Age: int64(20 + i%50),
  46. Score: int64(i % 1000),
  47. Dept: fmt.Sprintf("dept_%d", i%10),
  48. }
  49. if err := tbl.Insert(rec); err != nil {
  50. panic(err)
  51. }
  52. // 每 2 万行打印一次内存状态
  53. if (i+1)%20000 == 0 {
  54. var m runtime.MemStats
  55. runtime.ReadMemStats(&m)
  56. allocMB := float64(m.Alloc) / 1024 / 1024
  57. totalMB := float64(m.TotalAlloc) / 1024 / 1024
  58. elapsed := time.Since(t0).Seconds()
  59. // 检查 BufferPool 状态
  60. db.DebugDBState()
  61. fmt.Printf(" [%6d行] Alloc=%.1fMB Total=%.1fMB 耗时=%.1fs\n",
  62. i+1, allocMB, totalMB, elapsed)
  63. // 检查是否内存暴涨 (超过 500MB 就报警)
  64. if allocMB > 500 {
  65. fmt.Printf(" ⚠️ 内存Alloc超过500MB! 脏页阈值可能未生效!\n")
  66. }
  67. // 记录 commit 次数变化
  68. now := time.Now()
  69. if now.Sub(lastCommitTime) > time.Second {
  70. commitCount++
  71. lastCommitTime = now
  72. }
  73. }
  74. }
  75. insertTime := time.Since(t0)
  76. var mAfterInsert runtime.MemStats
  77. runtime.ReadMemStats(&mAfterInsert)
  78. fmt.Printf("\n 插入完成: 耗时=%.1fs, Alloc=%.1fMB\n",
  79. insertTime.Seconds(), float64(mAfterInsert.Alloc)/1024/1024)
  80. // ========== 3. 手动 Commit 剩余脏页 ==========
  81. fmt.Println("\n========== 3. 手动 Commit ==========")
  82. t1 := time.Now()
  83. if err := db.Commit(); err != nil {
  84. panic(err)
  85. }
  86. fmt.Printf(" Commit 耗时: %.1fs\n", time.Since(t1).Seconds())
  87. // ========== 4. 验证行数 ==========
  88. fmt.Println("\n========== 4. 验证行数 ==========")
  89. count, err := tbl.Count()
  90. if err != nil {
  91. panic(err)
  92. }
  93. fmt.Printf(" 当前行数: %d (期望 %d)\n", count, N)
  94. if count != int64(N) {
  95. fmt.Printf(" ❌ 行数不匹配!\n")
  96. } else {
  97. fmt.Printf(" ✅ 行数正确\n")
  98. }
  99. // ========== 5. 关闭并重开验证持久化 ==========
  100. fmt.Println("\n========== 5. 关闭 → 重开验证 ==========")
  101. if err := db.Close(); err != nil {
  102. panic(err)
  103. }
  104. db2, err := DB.Open(dbPath)
  105. if err != nil {
  106. panic(err)
  107. }
  108. defer db2.Close()
  109. tbl2, err := DB.UseTable[Record](db2, "test")
  110. if err != nil {
  111. panic(err)
  112. }
  113. count2, err := tbl2.Count()
  114. if err != nil {
  115. panic(err)
  116. }
  117. fmt.Printf(" 重开后行数: %d (期望 %d)\n", count2, N)
  118. if count2 != int64(N) {
  119. fmt.Printf(" ❌ 数据丢失!\n")
  120. } else {
  121. fmt.Printf(" ✅ 数据完整持久化\n")
  122. }
  123. // ========== 6. 抽查几条数据 ==========
  124. fmt.Println("\n========== 6. 抽查数据 ==========")
  125. rec1, err := tbl2.Find(1)
  126. if err != nil {
  127. fmt.Printf(" Find(1) error: %v\n", err)
  128. } else {
  129. fmt.Printf(" Row#1: Name=%s Age=%d Score=%d Dept=%s\n",
  130. rec1.Name, rec1.Age, rec1.Score, rec1.Dept)
  131. }
  132. recLast, err := tbl2.Find(uint64(N))
  133. if err != nil {
  134. fmt.Printf(" Find(%d) error: %v\n", N, err)
  135. } else {
  136. fmt.Printf(" Row#%d: Name=%s Age=%d Score=%d Dept=%s\n",
  137. N, recLast.Name, recLast.Age, recLast.Score, recLast.Dept)
  138. }
  139. // ========== 总结 ==========
  140. fmt.Println("\n========== 总结 ==========")
  141. var mFinal runtime.MemStats
  142. runtime.ReadMemStats(&mFinal)
  143. fmt.Printf(" 最终 Alloc=%.1fMB, TotalAlloc=%.1fMB\n",
  144. float64(mFinal.Alloc)/1024/1024,
  145. float64(mFinal.TotalAlloc)/1024/1024)
  146. fmt.Printf(" 插入耗时: %.1fs\n", insertTime.Seconds())
  147. fmt.Printf(" 脏页阈值: %d 页 (≈%.0fMB)\n",
  148. 10000, float64(10000*16384)/1024/1024)
  149. // 判断是否内存泄漏
  150. allocMB := float64(mFinal.Alloc) / 1024 / 1024
  151. if allocMB < 200 {
  152. fmt.Println(" ✅ 内存控制良好 (< 200MB), 脏页阈值生效!")
  153. } else if allocMB < 500 {
  154. fmt.Println(" ⚠️ 内存偏高 (200-500MB), 可能需要调低阈值")
  155. } else {
  156. fmt.Println(" ❌ 内存泄漏! (> 500MB)")
  157. os.Exit(1) // 让 make test / CI 能感知失败
  158. }
  159. //os.Remove(dbPath)
  160. }