main.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. package main
  2. import (
  3. "git.ycl.pub/yl32940/TsDB2/DB"
  4. "fmt"
  5. "os"
  6. "runtime"
  7. "time"
  8. )
  9. type Record struct {
  10. DB.BaseModel
  11. Name string `vrtx:"maxlen:32"`
  12. Age int64
  13. Score int64
  14. Dept string `vrtx:"maxlen:16"`
  15. }
  16. const dbPath = "integrity_test.db"
  17. func main() {
  18. os.Remove(dbPath)
  19. fmt.Println("==============================================")
  20. fmt.Println(" TsDB2 完整性测试 (100万行)")
  21. fmt.Println("==============================================")
  22. fmt.Println()
  23. // ---- Phase 1: 打开数据库 ----
  24. fmt.Println("========== Phase 1: 打开数据库 ==========")
  25. db, err := DB.Open(dbPath)
  26. if err != nil {
  27. panic(err)
  28. }
  29. db.SetAutoCommitDelay(0)
  30. tbl, err := DB.UseTable[Record](db, "test")
  31. if err != nil {
  32. panic(err)
  33. }
  34. printMemDisk("初始状态")
  35. // ---- Phase 2: 批量插入 100 万行 ----
  36. fmt.Println("\n========== Phase 2: 批量插入 ==========")
  37. N := 1_000_000
  38. t0 := time.Now()
  39. for i := 1; i <= N; i++ {
  40. err := tbl.Insert(&Record{
  41. Name: fmt.Sprintf("Emp%07d", i),
  42. Age: int64(20 + (i % 50)),
  43. Score: int64(i % 1000),
  44. Dept: fmt.Sprintf("dept_%d", i%10),
  45. })
  46. if err != nil {
  47. panic(fmt.Sprintf("insert row %d: %v", i, err))
  48. }
  49. if i%100_000 == 0 {
  50. printMemDisk(fmt.Sprintf("插入 %d 行", i))
  51. }
  52. }
  53. insertTime := time.Since(t0)
  54. fmt.Printf(" 插入 %d 行耗时: %.2fs (%.0f rows/s)\n", N, insertTime.Seconds(), float64(N)/insertTime.Seconds())
  55. // ---- Phase 3: Commit ----
  56. fmt.Println("\n========== Phase 3: Commit ==========")
  57. t1 := time.Now()
  58. if err := db.Commit(); err != nil {
  59. panic(err)
  60. }
  61. commitTime := time.Since(t1)
  62. printMemDisk("Commit 后")
  63. fmt.Printf(" Commit 耗时: %.2fs\n", commitTime.Seconds())
  64. // ---- Phase 4: 主键精确查找 ----
  65. fmt.Println("\n========== Phase 4: 主键精确查找 ==========")
  66. findTests := []uint64{1, 100, 1000, 10000, 100000, 500000, 999999, 1000000}
  67. findOK := 0
  68. findFail := 0
  69. t2 := time.Now()
  70. for _, id := range findTests {
  71. row, err := tbl.Find(id)
  72. if err != nil {
  73. fmt.Printf(" Find(%d): FAIL (%v)\n", id, err)
  74. findFail++
  75. } else {
  76. expected := fmt.Sprintf("Emp%07d", id)
  77. if row.Name != expected {
  78. fmt.Printf(" Find(%d): DATA ERROR (Name=%s, expected %s)\n", id, row.Name, expected)
  79. findFail++
  80. } else {
  81. findOK++
  82. }
  83. }
  84. }
  85. findTime := time.Since(t2)
  86. fmt.Printf(" 查找 %d 个样本: %d 成功, %d 失败, 耗时 %.3fs\n", len(findTests), findOK, findFail, findTime.Seconds())
  87. // ---- Phase 5: 全量扫描验证 ----
  88. fmt.Println("\n========== Phase 5: 全量扫描验证 ==========")
  89. t3 := time.Now()
  90. count := 0
  91. nameErrors := 0
  92. ageErrors := 0
  93. err = tbl.Select(func(rec *Record) bool {
  94. count++
  95. expectedName := fmt.Sprintf("Emp%07d", rec.ID)
  96. if rec.Name != expectedName {
  97. nameErrors++
  98. }
  99. expectedAge := int64(20 + (rec.ID % 50))
  100. if rec.Age != expectedAge {
  101. ageErrors++
  102. }
  103. return true
  104. })
  105. selectTime := time.Since(t3)
  106. if err != nil {
  107. fmt.Printf(" Select error: %v\n", err)
  108. }
  109. fmt.Printf(" 扫描行数: %d\n", count)
  110. fmt.Printf(" Name 错误: %d\n", nameErrors)
  111. fmt.Printf(" Age 错误: %d\n", ageErrors)
  112. fmt.Printf(" 全量扫描耗时: %.2fs (%.0f rows/s)\n", selectTime.Seconds(), float64(count)/selectTime.Seconds())
  113. // ---- Phase 6: 条件查询 ----
  114. fmt.Println("\n========== Phase 6: 条件查询 ==========")
  115. // Age = 25 (约 20000 行)
  116. t4 := time.Now()
  117. ageRows, err := tbl.Where("Age", "=", int64(25)).Find()
  118. ageQueryTime := time.Since(t4)
  119. if err != nil {
  120. fmt.Printf(" WHERE Age=25: ERROR (%v)\n", err)
  121. } else {
  122. fmt.Printf(" WHERE Age=25: %d 行, 耗时 %.3fs\n", len(ageRows), ageQueryTime.Seconds())
  123. }
  124. // Score = 500 (约 1000 行)
  125. t5 := time.Now()
  126. scoreRows, err := tbl.Where("Score", "=", int64(500)).Find()
  127. scoreQueryTime := time.Since(t5)
  128. if err != nil {
  129. fmt.Printf(" WHERE Score=500: ERROR (%v)\n", err)
  130. } else {
  131. fmt.Printf(" WHERE Score=500: %d 行, 耗时 %.3fs\n", len(scoreRows), scoreQueryTime.Seconds())
  132. }
  133. // ---- Phase 7: 更新验证 ----
  134. fmt.Println("\n========== Phase 7: 更新验证 ==========")
  135. t6 := time.Now()
  136. err = tbl.Update(&Record{
  137. BaseModel: DB.BaseModel{ID: 500},
  138. Dept: "UPDATED",
  139. })
  140. if err != nil {
  141. fmt.Printf(" Update id=500: FAIL (%v)\n", err)
  142. } else {
  143. row, err := tbl.Find(500)
  144. if err != nil {
  145. fmt.Printf(" Update 后 Find(500): FAIL (%v)\n", err)
  146. } else if row.Dept == "UPDATED" {
  147. fmt.Printf(" Update id=500: OK (Dept=UPDATED)\n")
  148. } else {
  149. fmt.Printf(" Update id=500: DATA ERROR (Dept=%s)\n", row.Dept)
  150. }
  151. }
  152. db.Commit()
  153. fmt.Printf(" 更新+Commit 耗时: %.3fs\n", time.Since(t6).Seconds())
  154. // ---- Phase 8: 删除验证 ----
  155. fmt.Println("\n========== Phase 8: 删除验证 ==========")
  156. t7 := time.Now()
  157. err = tbl.Delete(999)
  158. if err != nil {
  159. fmt.Printf(" Delete id=999: FAIL (%v)\n", err)
  160. } else {
  161. _, err := tbl.Find(999)
  162. if err != nil {
  163. fmt.Printf(" Delete id=999: OK (已找不到)\n")
  164. } else {
  165. fmt.Printf(" Delete id=999: FAIL (仍然能找到)\n")
  166. }
  167. }
  168. db.Commit()
  169. fmt.Printf(" 删除+Commit 耗时: %.3fs\n", time.Since(t7).Seconds())
  170. // 验证 Count
  171. cnt, _ := tbl.Count()
  172. fmt.Printf(" 删除后 Count: %d (应为 %d)\n", cnt, N-1)
  173. // ---- Phase 9: 关闭重开验证 ----
  174. fmt.Println("\n========== Phase 9: 关闭重开验证 ==========")
  175. db.Close()
  176. printMemDisk("Close 后")
  177. db2, err := DB.Open(dbPath)
  178. if err != nil {
  179. panic(err)
  180. }
  181. tbl2, err := DB.UseTable[Record](db2, "test")
  182. if err != nil {
  183. panic(err)
  184. }
  185. cnt2, _ := tbl2.Count()
  186. fmt.Printf(" 重开后 Count: %d\n", cnt2)
  187. // 重开后查找
  188. row, err := tbl2.Find(1)
  189. if err != nil {
  190. fmt.Printf(" 重开后 Find(1): FAIL (%v)\n", err)
  191. } else {
  192. fmt.Printf(" 重开后 Find(1): OK (Name=%s)\n", row.Name)
  193. }
  194. row, err = tbl2.Find(1000000)
  195. if err != nil {
  196. fmt.Printf(" 重开后 Find(1000000): FAIL (%v)\n", err)
  197. } else {
  198. fmt.Printf(" 重开后 Find(1000000): OK (Name=%s)\n", row.Name)
  199. }
  200. db2.Close()
  201. // ---- Phase 10: 最终统计 ----
  202. fmt.Println("\n========== Phase 10: 最终统计 ==========")
  203. fi, _ := os.Stat(dbPath)
  204. sizeMB := float64(fi.Size()) / 1024 / 1024
  205. fmt.Printf(" 数据库文件大小: %.2f MB\n", sizeMB)
  206. fmt.Printf(" 每行平均占用: %.0f bytes\n", float64(fi.Size())/float64(N))
  207. fmt.Printf(" 理论最小行大小: ~72 bytes (32+8+8+16+8)\n")
  208. fmt.Printf(" 空间放大系数: %.1fx\n", float64(fi.Size())/float64(N)/72.0)
  209. var m runtime.MemStats
  210. runtime.ReadMemStats(&m)
  211. fmt.Printf(" Go 堆内存: %.1f MB\n", float64(m.Alloc)/1024/1024)
  212. fmt.Printf(" 总分配内存: %.1f MB\n", float64(m.TotalAlloc)/1024/1024)
  213. fmt.Println("\n==============================================")
  214. if findFail == 0 && nameErrors == 0 && ageErrors == 0 && cnt2 == int64(N-1) {
  215. fmt.Println(" ✅ 完整性测试全部通过!")
  216. } else {
  217. fmt.Printf(" ❌ 有失败项: Find=%d, Name=%d, Age=%d, Count=%d\n",
  218. findFail, nameErrors, ageErrors, int(int64(N-1)-cnt2))
  219. os.Exit(1) // 让 make test / CI 能感知失败
  220. }
  221. fmt.Println("==============================================")
  222. }
  223. func printMemDisk(label string) {
  224. fi, _ := os.Stat(dbPath)
  225. sizeMB := float64(fi.Size()) / 1024 / 1024
  226. var m runtime.MemStats
  227. runtime.ReadMemStats(&m)
  228. heapMB := float64(m.Alloc) / 1024 / 1024
  229. fmt.Printf(" [%s] 磁盘: %8.2f MB | Go堆: %6.1f MB | Sys: %6.1f MB | Goroutines: %d\n",
  230. label, sizeMB, heapMB, float64(m.Sys)/1024/1024, runtime.NumGoroutine())
  231. }