main.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. package main
  2. import (
  3. "git.ycl.pub/yl32940/TsDB2/DB"
  4. "fmt"
  5. "os"
  6. "runtime"
  7. "strings"
  8. "time"
  9. )
  10. // ========== 数据模型 ==========
  11. type Employee struct {
  12. DB.BaseModel
  13. Name string `vrtx:"maxlen:32"`
  14. Age int64 `vrtx:"index:idx_age"`
  15. Score int64 `vrtx:"index:idx_score"`
  16. Dept string `vrtx:"maxlen:16,index:idx_dept"`
  17. }
  18. type Order struct {
  19. DB.BaseModel
  20. UserID int64 `vrtx:"index:idx_userid"`
  21. Amount int64
  22. Status int64 `vrtx:"index:idx_status"`
  23. }
  24. const (
  25. dbPath = "stress_test.db"
  26. EMPLOYEE_N = 500_000
  27. ORDER_N = 100_000
  28. )
  29. var db *DB.DB
  30. var passed, failed int
  31. func memStats() string {
  32. var m runtime.MemStats
  33. runtime.ReadMemStats(&m)
  34. return fmt.Sprintf("Go堆=%.1fMB Sys=%.1fMB", float64(m.HeapInuse)/1024/1024, float64(m.Sys)/1024/1024)
  35. }
  36. func fileSize() string {
  37. fi, err := os.Stat(dbPath)
  38. if err != nil {
  39. return "N/A"
  40. }
  41. return fmt.Sprintf("%.1fMB", float64(fi.Size())/1024/1024)
  42. }
  43. func check(msg string, ok bool) {
  44. if ok {
  45. passed++
  46. fmt.Printf(" ✅ %s\n", msg)
  47. } else {
  48. failed++
  49. fmt.Printf(" ❌ %s\n", msg)
  50. }
  51. }
  52. func section(title string) {
  53. fmt.Printf("\n%s %s\n", strings.Repeat("=", 50), title)
  54. }
  55. func main() {
  56. os.Remove(dbPath)
  57. os.Remove(dbPath + ".wal")
  58. fmt.Println(strings.Repeat("=", 60))
  59. fmt.Printf(" TsDB2 大数据压力测试 (%d万行 Employee + %d万行 Order)\n", EMPLOYEE_N/10000, ORDER_N/10000)
  60. fmt.Println(" 覆盖: 批量插入/多索引/事务/WAL/条件查询/排序/聚合/持久化")
  61. fmt.Println(strings.Repeat("=", 60))
  62. // ============================================================
  63. // 1. 打开数据库
  64. // ============================================================
  65. section("1. 打开数据库")
  66. var err error
  67. db, err = DB.Open(dbPath)
  68. check("OpenDB", err == nil)
  69. db.SetAutoCommitDelay(0) // 禁用自动提交, 手动控制
  70. db.SetBufferPoolCapacity(2000)
  71. db.SetDirtyThreshold(500)
  72. fmt.Printf(" 配置: BufferPool=2000页(32MB), DirtyThreshold=500页\n")
  73. fmt.Printf(" [初始状态] 磁盘: %s | %s\n", fileSize(), memStats())
  74. // ============================================================
  75. // 2. 建表 + 多索引
  76. // ============================================================
  77. section("2. 建表 + 多索引")
  78. empTbl, err := DB.UseTable[Employee](db, "employee")
  79. check("UseTable[Employee]", err == nil)
  80. // 索引已通过 vrtx 标签自动创建: idx_age, idx_dept, idx_score
  81. fmt.Println(" ✅ 索引: pk_employee + idx_age + idx_dept + idx_score (标签自动创建)")
  82. passed++
  83. orderTbl, err := DB.UseTable[Order](db, "order")
  84. check("UseTable[Order]", err == nil)
  85. // 索引已通过 vrtx 标签自动创建: idx_status, idx_userid
  86. fmt.Println(" ✅ 索引: pk_order + idx_status + idx_userid (标签自动创建)")
  87. passed++
  88. // ============================================================
  89. // 3. 批量插入 Employee (50万行)
  90. // ============================================================
  91. section("3. 批量插入 Employee (50万行)")
  92. depts := []string{"Engineering", "Sales", "HR", "Finance", "Operations", "Marketing", "Support", "Legal"}
  93. t0 := time.Now()
  94. for i := 1; i <= EMPLOYEE_N; i++ {
  95. err := empTbl.Insert(&Employee{
  96. Name: fmt.Sprintf("Emp%07d", i),
  97. Age: int64(20 + (i % 50)),
  98. Score: int64(i % 1000),
  99. Dept: depts[i%len(depts)],
  100. })
  101. if err != nil {
  102. fmt.Printf(" ❌ Insert(%d): %v\n", i, err)
  103. failed++
  104. break
  105. }
  106. if i%100000 == 0 {
  107. fmt.Printf(" [%d行] 耗时: %.1fs | 磁盘: %s | %s\n", i, time.Since(t0).Seconds(), fileSize(), memStats())
  108. }
  109. }
  110. insertTime := time.Since(t0)
  111. fmt.Printf(" ✅ 插入 %d 行耗时: %.2fs (%.0f rows/s)\n", EMPLOYEE_N, insertTime.Seconds(), float64(EMPLOYEE_N)/insertTime.Seconds())
  112. passed++
  113. // ============================================================
  114. // 4. Commit (WAL 持久化)
  115. // ============================================================
  116. section("4. Commit (WAL 持久化)")
  117. t1 := time.Now()
  118. err = db.Commit()
  119. commitTime := time.Since(t1)
  120. check("DB.Commit()", err == nil)
  121. fmt.Printf(" Commit 耗时: %.3fs | 磁盘: %s | %s\n", commitTime.Seconds(), fileSize(), memStats())
  122. // ============================================================
  123. // 5. 批量插入 Order (10万行)
  124. // ============================================================
  125. section("5. 批量插入 Order (10万行)")
  126. t2 := time.Now()
  127. for i := 1; i <= ORDER_N; i++ {
  128. orderTbl.Insert(&Order{
  129. UserID: int64(1 + i%EMPLOYEE_N),
  130. Amount: int64(100 + i*10),
  131. Status: int64(i % 5),
  132. })
  133. }
  134. fmt.Printf(" ✅ 插入 %d 行 Order 耗时: %.2fs (%.0f rows/s)\n", ORDER_N, time.Since(t2).Seconds(), float64(ORDER_N)/time.Since(t2).Seconds())
  135. passed++
  136. err = db.Commit()
  137. check("Order Commit", err == nil)
  138. fmt.Printf(" 磁盘: %s | %s\n", fileSize(), memStats())
  139. // ============================================================
  140. // 6. 主键精确查找 (性能)
  141. // ============================================================
  142. section("6. 主键精确查找 (性能)")
  143. samples := []uint64{1, 1000, 50000, 100000, 250000, 499999}
  144. t3 := time.Now()
  145. for _, id := range samples {
  146. u, err := empTbl.Find(id)
  147. if err != nil || u == nil {
  148. fmt.Printf(" ❌ Find(%d) 失败\n", id)
  149. failed++
  150. }
  151. }
  152. findTime := time.Since(t3)
  153. fmt.Printf(" ✅ %d 次主键查找 耗时: %.4fs (%.0f finds/s)\n", len(samples), findTime.Seconds(), float64(len(samples))/findTime.Seconds())
  154. passed++
  155. // ============================================================
  156. // 7. 条件查询 (二级索引)
  157. // ============================================================
  158. section("7. 条件查询 (二级索引)")
  159. // Age = 25
  160. t4 := time.Now()
  161. users, err := empTbl.Where("Age", "=", int64(25)).Find()
  162. q1Time := time.Since(t4)
  163. if err == nil {
  164. fmt.Printf(" ✅ WHERE Age=25: %d 行, 耗时: %.3fs\n", len(users), q1Time.Seconds())
  165. passed++
  166. } else {
  167. fmt.Printf(" ❌ WHERE Age=25: %v\n", err)
  168. failed++
  169. }
  170. // Dept = "Engineering"
  171. t5 := time.Now()
  172. users, err = empTbl.Where("Dept", "=", "Engineering").Find()
  173. q2Time := time.Since(t5)
  174. if err == nil {
  175. fmt.Printf(" ✅ WHERE Dept='Engineering': %d 行, 耗时: %.3fs\n", len(users), q2Time.Seconds())
  176. passed++
  177. } else {
  178. fmt.Printf(" ❌ WHERE Dept: %v\n", err)
  179. failed++
  180. }
  181. // Score > 900
  182. t6 := time.Now()
  183. users, err = empTbl.Where("Score", ">", int64(900)).Find()
  184. q3Time := time.Since(t6)
  185. if err == nil {
  186. fmt.Printf(" ✅ WHERE Score>900: %d 行, 耗时: %.3fs\n", len(users), q3Time.Seconds())
  187. passed++
  188. } else {
  189. fmt.Printf(" ❌ WHERE Score>900: %v\n", err)
  190. failed++
  191. }
  192. // Age >= 30 AND Age <= 40
  193. t7 := time.Now()
  194. users, err = empTbl.Where("Age", ">=", int64(30)).Where("Age", "<=", int64(40)).Find()
  195. q4Time := time.Since(t7)
  196. if err == nil {
  197. fmt.Printf(" ✅ WHERE Age>=30 AND Age<=40: %d 行, 耗时: %.3fs\n", len(users), q4Time.Seconds())
  198. passed++
  199. } else {
  200. fmt.Printf(" ❌ WHERE Age range: %v\n", err)
  201. failed++
  202. }
  203. // ============================================================
  204. // 8. 排序 + 分页 (大数据量)
  205. // ============================================================
  206. section("8. 排序 + 分页")
  207. t8 := time.Now()
  208. top10, err := empTbl.OrderBy("Score", true).Limit(10).Find()
  209. sortTime := time.Since(t8)
  210. if err == nil && len(top10) == 10 {
  211. fmt.Printf(" ✅ ORDER BY Score DESC LIMIT 10, 耗时: %.3fs\n", sortTime.Seconds())
  212. fmt.Printf(" Top3: Score=%d, %d, %d\n", top10[0].Score, top10[1].Score, top10[2].Score)
  213. passed++
  214. } else {
  215. fmt.Printf(" ❌ 排序: err=%v rows=%d\n", err, len(top10))
  216. failed++
  217. }
  218. // 分页: OFFSET 10000 LIMIT 100
  219. t9 := time.Now()
  220. page, err := empTbl.OrderBy("Age", false).Offset(10000).Limit(100).Find()
  221. pageTime := time.Since(t9)
  222. if err == nil && len(page) == 100 {
  223. fmt.Printf(" ✅ ORDER BY Age ASC OFFSET 10000 LIMIT 100, 耗时: %.3fs\n", pageTime.Seconds())
  224. passed++
  225. } else {
  226. fmt.Printf(" ❌ 分页: err=%v rows=%d\n", err, len(page))
  227. failed++
  228. }
  229. // ============================================================
  230. // 9. 聚合查询
  231. // ============================================================
  232. section("9. 聚合查询")
  233. t10 := time.Now()
  234. cnt, err := empTbl.Query().Count()
  235. countTime := time.Since(t10)
  236. if err == nil {
  237. fmt.Printf(" ✅ COUNT(*): %d, 耗时: %.3fs\n", cnt, countTime.Seconds())
  238. passed++
  239. } else {
  240. fmt.Printf(" ❌ COUNT: %v\n", err)
  241. failed++
  242. }
  243. // ============================================================
  244. // 10. 事务: 大批量操作
  245. // ============================================================
  246. section("10. 事务: 批量插入 + 提交")
  247. txn := db.BeginTransaction()
  248. txnN := 10000
  249. t11 := time.Now()
  250. for i := EMPLOYEE_N + 1; i <= EMPLOYEE_N+txnN; i++ {
  251. empTbl.Insert(&Employee{
  252. Name: fmt.Sprintf("Txn%07d", i),
  253. Age: int64(25 + i%20),
  254. Score: int64(500 + i%500),
  255. Dept: depts[i%len(depts)],
  256. })
  257. }
  258. err = txn.Commit()
  259. txnTime := time.Since(t11)
  260. check(fmt.Sprintf("事务插入 %d 行 + Commit, 耗时: %.2fs", txnN, txnTime.Seconds()), err == nil)
  261. // 验证 (查找事务范围内的行, 验证事务数据持久化)
  262. u, _ := empTbl.Find(uint64(EMPLOYEE_N + 1))
  263. if u != nil {
  264. fmt.Printf(" ✅ 验证: ID=%d 存在 (Name=%s)\n", EMPLOYEE_N+1, u.Name)
  265. passed++
  266. } else {
  267. fmt.Println(" ❌ 验证: 事务数据不存在")
  268. failed++
  269. }
  270. // ============================================================
  271. // 11. 事务: 回滚
  272. // ============================================================
  273. section("11. 事务: 回滚")
  274. txn2 := db.BeginTransaction()
  275. rbN := 5000
  276. for i := EMPLOYEE_N + txnN + 1; i <= EMPLOYEE_N+txnN+rbN; i++ {
  277. empTbl.Insert(&Employee{
  278. Name: fmt.Sprintf("Rb%07d", i),
  279. Age: int64(30),
  280. Score: int64(0),
  281. Dept: "ROLLBACK",
  282. })
  283. }
  284. fmt.Printf(" 事务内插入 %d 行\n", rbN)
  285. err = txn2.Rollback()
  286. check("Transaction.Rollback()", err == nil)
  287. // ============================================================
  288. // 12. 更新 + 删除 (大批量 Scan + Update/Delete)
  289. // ============================================================
  290. section("12. 更新 + 删除 (大批量)")
  291. // 更新: Score > 950 的设为 9999
  292. t12 := time.Now()
  293. updateCount := 0
  294. var updatedIDs []uint64
  295. empTbl.Where("Score", ">", int64(950)).Scan(func(u *Employee) bool {
  296. updatedIDs = append(updatedIDs, u.ID)
  297. u.Score = 9999
  298. empTbl.Update(u)
  299. updateCount++
  300. return true
  301. })
  302. updateTime := time.Since(t12)
  303. fmt.Printf(" ✅ 更新 Score>950 → 9999: %d 行, 耗时: %.3fs\n", updateCount, updateTime.Seconds())
  304. passed++
  305. // 删除: Dept = "Legal" 的前100行
  306. deleteCount := 0
  307. var firstDeletedID uint64
  308. deletedSet := make(map[uint64]bool)
  309. t13 := time.Now()
  310. empTbl.Where("Dept", "=", "Legal").Limit(100).Scan(func(u *Employee) bool {
  311. deletedSet[u.ID] = true
  312. if firstDeletedID == 0 {
  313. firstDeletedID = u.ID
  314. }
  315. empTbl.Delete(u.ID)
  316. deleteCount++
  317. return true
  318. })
  319. deleteTime := time.Since(t13)
  320. fmt.Printf(" ✅ 删除 Dept='Legal' 前100行: %d 行, 耗时: %.3fs\n", deleteCount, deleteTime.Seconds())
  321. passed++
  322. // 选取一个"确实被更新且未被删除"的行, 用于验证更新持久化
  323. var verifyUpdatedID uint64
  324. for _, id := range updatedIDs {
  325. if !deletedSet[id] {
  326. verifyUpdatedID = id
  327. break
  328. }
  329. }
  330. // 验证删除 (检查确实被删除的那一行已不存在)
  331. _, err = empTbl.Find(firstDeletedID)
  332. check(fmt.Sprintf("验证 Delete(%d) 成功", firstDeletedID), err != nil)
  333. err = db.Commit()
  334. check("更新+删除后 Commit", err == nil)
  335. // ============================================================
  336. // 13. 关闭 + 重开验证持久化
  337. // ============================================================
  338. section("13. 关闭 + 重开验证持久化")
  339. fmt.Printf(" 关闭前磁盘: %s\n", fileSize())
  340. err = db.Close()
  341. check("DB.Close()", err == nil)
  342. db, err = DB.Open(dbPath)
  343. check("重新打开数据库", err == nil)
  344. empTbl, _ = DB.UseTable[Employee](db, "employee")
  345. // 验证数据完整性:
  346. // 基础 50万行 + 事务提交 1万行 - 删除 deleteCount 行
  347. // (回滚的 5000 行不应存在, 事务行必须持久化)
  348. expected := int64(EMPLOYEE_N + txnN - deleteCount)
  349. cnt, _ = empTbl.Query().Count()
  350. if cnt == expected {
  351. fmt.Printf(" ✅ 重开后 COUNT: %d (期望 %d, 事务已持久化)\n", cnt, expected)
  352. passed++
  353. } else {
  354. fmt.Printf(" ❌ 重开后 COUNT: %d (期望 %d)\n", cnt, expected)
  355. failed++
  356. }
  357. // 验证更新持久化 (使用确实被更新且未被删除的行)
  358. u, _ = empTbl.Find(verifyUpdatedID)
  359. if verifyUpdatedID != 0 && u != nil && u.Score == 9999 {
  360. fmt.Printf(" ✅ 重开后 Find(%d): Score=%d (更新持久化)\n", verifyUpdatedID, u.Score)
  361. passed++
  362. } else if u == nil {
  363. fmt.Printf(" ❌ 重开后 Find(%d) 不存在\n", verifyUpdatedID)
  364. failed++
  365. } else {
  366. fmt.Printf(" ❌ 重开后 Find(%d): Score=%d (期望 9999)\n", verifyUpdatedID, u.Score)
  367. failed++
  368. }
  369. // 验证回滚数据不存在
  370. _, err = empTbl.Find(uint64(EMPLOYEE_N + txnN + 1))
  371. check("重开后回滚数据不存在", err != nil)
  372. fmt.Printf(" 重开后磁盘: %s | %s\n", fileSize(), memStats())
  373. // ============================================================
  374. // 14. 全量扫描验证
  375. // ============================================================
  376. section("14. 全量扫描验证")
  377. scanCount := 0
  378. t14 := time.Now()
  379. err = empTbl.Query().Scan(func(u *Employee) bool {
  380. scanCount++
  381. return true
  382. })
  383. scanTime := time.Since(t14)
  384. if err == nil {
  385. fmt.Printf(" ✅ 全量扫描: %d 行, 耗时: %.2fs (%.0f rows/s)\n",
  386. scanCount, scanTime.Seconds(), float64(scanCount)/scanTime.Seconds())
  387. passed++
  388. } else {
  389. fmt.Printf(" ❌ 全量扫描: %v\n", err)
  390. failed++
  391. }
  392. // ============================================================
  393. // 15. Order 表验证
  394. // ============================================================
  395. section("15. Order 表验证")
  396. orderTbl, _ = DB.UseTable[Order](db, "order")
  397. cnt, _ = orderTbl.Query().Count()
  398. if cnt == int64(ORDER_N) {
  399. fmt.Printf(" ✅ Order COUNT: %d\n", cnt)
  400. passed++
  401. } else {
  402. fmt.Printf(" ❌ Order COUNT: %d (期望 %d)\n", cnt, ORDER_N)
  403. failed++
  404. }
  405. orders, _ := orderTbl.Where("Status", "=", int64(0)).Find()
  406. fmt.Printf(" ✅ Order WHERE Status=0: %d 行\n", len(orders))
  407. passed++
  408. // ============================================================
  409. // 最终汇总
  410. // ============================================================
  411. fmt.Println()
  412. fmt.Println(strings.Repeat("=", 60))
  413. fmt.Printf(" 测试结果: %d 通过, %d 失败\n", passed, failed)
  414. fmt.Printf(" 数据量: Employee=%d + Order=%d\n", EMPLOYEE_N, ORDER_N)
  415. fmt.Printf(" 磁盘大小: %s\n", fileSize())
  416. fmt.Printf(" %s\n", memStats())
  417. if failed == 0 {
  418. fmt.Println(" 🎉 全部通过!")
  419. } else {
  420. fmt.Printf(" ⚠️ %d 项失败\n", failed)
  421. }
  422. fmt.Println(strings.Repeat("=", 60))
  423. db.Close()
  424. // [KEEP-DB] 保留数据库文件供 Analyzer 分析, 屏蔽运行结束后的清理删除
  425. // os.Remove(dbPath)
  426. // os.Remove(dbPath + ".wal")
  427. }