| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486 |
- package main
- import (
- "git.ycl.pub/yl32940/TsDB2/DB"
- "fmt"
- "os"
- "runtime"
- "strings"
- "time"
- )
- // ========== 数据模型 ==========
- type Employee struct {
- DB.BaseModel
- Name string `vrtx:"maxlen:32"`
- Age int64 `vrtx:"index:idx_age"`
- Score int64 `vrtx:"index:idx_score"`
- Dept string `vrtx:"maxlen:16,index:idx_dept"`
- }
- type Order struct {
- DB.BaseModel
- UserID int64 `vrtx:"index:idx_userid"`
- Amount int64
- Status int64 `vrtx:"index:idx_status"`
- }
- const (
- dbPath = "stress_test.db"
- EMPLOYEE_N = 500_000
- ORDER_N = 100_000
- )
- var db *DB.DB
- var passed, failed int
- func memStats() string {
- var m runtime.MemStats
- runtime.ReadMemStats(&m)
- return fmt.Sprintf("Go堆=%.1fMB Sys=%.1fMB", float64(m.HeapInuse)/1024/1024, float64(m.Sys)/1024/1024)
- }
- func fileSize() string {
- fi, err := os.Stat(dbPath)
- if err != nil {
- return "N/A"
- }
- return fmt.Sprintf("%.1fMB", float64(fi.Size())/1024/1024)
- }
- func check(msg string, ok bool) {
- if ok {
- passed++
- fmt.Printf(" ✅ %s\n", msg)
- } else {
- failed++
- fmt.Printf(" ❌ %s\n", msg)
- }
- }
- func section(title string) {
- fmt.Printf("\n%s %s\n", strings.Repeat("=", 50), title)
- }
- func main() {
- os.Remove(dbPath)
- os.Remove(dbPath + ".wal")
- fmt.Println(strings.Repeat("=", 60))
- fmt.Printf(" TsDB2 大数据压力测试 (%d万行 Employee + %d万行 Order)\n", EMPLOYEE_N/10000, ORDER_N/10000)
- fmt.Println(" 覆盖: 批量插入/多索引/事务/WAL/条件查询/排序/聚合/持久化")
- fmt.Println(strings.Repeat("=", 60))
- // ============================================================
- // 1. 打开数据库
- // ============================================================
- section("1. 打开数据库")
- var err error
- db, err = DB.Open(dbPath)
- check("OpenDB", err == nil)
- db.SetAutoCommitDelay(0) // 禁用自动提交, 手动控制
- db.SetBufferPoolCapacity(2000)
- db.SetDirtyThreshold(500)
- fmt.Printf(" 配置: BufferPool=2000页(32MB), DirtyThreshold=500页\n")
- fmt.Printf(" [初始状态] 磁盘: %s | %s\n", fileSize(), memStats())
- // ============================================================
- // 2. 建表 + 多索引
- // ============================================================
- section("2. 建表 + 多索引")
- empTbl, err := DB.UseTable[Employee](db, "employee")
- check("UseTable[Employee]", err == nil)
- // 索引已通过 vrtx 标签自动创建: idx_age, idx_dept, idx_score
- fmt.Println(" ✅ 索引: pk_employee + idx_age + idx_dept + idx_score (标签自动创建)")
- passed++
- orderTbl, err := DB.UseTable[Order](db, "order")
- check("UseTable[Order]", err == nil)
- // 索引已通过 vrtx 标签自动创建: idx_status, idx_userid
- fmt.Println(" ✅ 索引: pk_order + idx_status + idx_userid (标签自动创建)")
- passed++
- // ============================================================
- // 3. 批量插入 Employee (50万行)
- // ============================================================
- section("3. 批量插入 Employee (50万行)")
- depts := []string{"Engineering", "Sales", "HR", "Finance", "Operations", "Marketing", "Support", "Legal"}
- t0 := time.Now()
- for i := 1; i <= EMPLOYEE_N; i++ {
- err := empTbl.Insert(&Employee{
- Name: fmt.Sprintf("Emp%07d", i),
- Age: int64(20 + (i % 50)),
- Score: int64(i % 1000),
- Dept: depts[i%len(depts)],
- })
- if err != nil {
- fmt.Printf(" ❌ Insert(%d): %v\n", i, err)
- failed++
- break
- }
- if i%100000 == 0 {
- fmt.Printf(" [%d行] 耗时: %.1fs | 磁盘: %s | %s\n", i, time.Since(t0).Seconds(), fileSize(), memStats())
- }
- }
- insertTime := time.Since(t0)
- fmt.Printf(" ✅ 插入 %d 行耗时: %.2fs (%.0f rows/s)\n", EMPLOYEE_N, insertTime.Seconds(), float64(EMPLOYEE_N)/insertTime.Seconds())
- passed++
- // ============================================================
- // 4. Commit (WAL 持久化)
- // ============================================================
- section("4. Commit (WAL 持久化)")
- t1 := time.Now()
- err = db.Commit()
- commitTime := time.Since(t1)
- check("DB.Commit()", err == nil)
- fmt.Printf(" Commit 耗时: %.3fs | 磁盘: %s | %s\n", commitTime.Seconds(), fileSize(), memStats())
- // ============================================================
- // 5. 批量插入 Order (10万行)
- // ============================================================
- section("5. 批量插入 Order (10万行)")
- t2 := time.Now()
- for i := 1; i <= ORDER_N; i++ {
- orderTbl.Insert(&Order{
- UserID: int64(1 + i%EMPLOYEE_N),
- Amount: int64(100 + i*10),
- Status: int64(i % 5),
- })
- }
- fmt.Printf(" ✅ 插入 %d 行 Order 耗时: %.2fs (%.0f rows/s)\n", ORDER_N, time.Since(t2).Seconds(), float64(ORDER_N)/time.Since(t2).Seconds())
- passed++
- err = db.Commit()
- check("Order Commit", err == nil)
- fmt.Printf(" 磁盘: %s | %s\n", fileSize(), memStats())
- // ============================================================
- // 6. 主键精确查找 (性能)
- // ============================================================
- section("6. 主键精确查找 (性能)")
- samples := []uint64{1, 1000, 50000, 100000, 250000, 499999}
- t3 := time.Now()
- for _, id := range samples {
- u, err := empTbl.Find(id)
- if err != nil || u == nil {
- fmt.Printf(" ❌ Find(%d) 失败\n", id)
- failed++
- }
- }
- findTime := time.Since(t3)
- fmt.Printf(" ✅ %d 次主键查找 耗时: %.4fs (%.0f finds/s)\n", len(samples), findTime.Seconds(), float64(len(samples))/findTime.Seconds())
- passed++
- // ============================================================
- // 7. 条件查询 (二级索引)
- // ============================================================
- section("7. 条件查询 (二级索引)")
- // Age = 25
- t4 := time.Now()
- users, err := empTbl.Where("Age", "=", int64(25)).Find()
- q1Time := time.Since(t4)
- if err == nil {
- fmt.Printf(" ✅ WHERE Age=25: %d 行, 耗时: %.3fs\n", len(users), q1Time.Seconds())
- passed++
- } else {
- fmt.Printf(" ❌ WHERE Age=25: %v\n", err)
- failed++
- }
- // Dept = "Engineering"
- t5 := time.Now()
- users, err = empTbl.Where("Dept", "=", "Engineering").Find()
- q2Time := time.Since(t5)
- if err == nil {
- fmt.Printf(" ✅ WHERE Dept='Engineering': %d 行, 耗时: %.3fs\n", len(users), q2Time.Seconds())
- passed++
- } else {
- fmt.Printf(" ❌ WHERE Dept: %v\n", err)
- failed++
- }
- // Score > 900
- t6 := time.Now()
- users, err = empTbl.Where("Score", ">", int64(900)).Find()
- q3Time := time.Since(t6)
- if err == nil {
- fmt.Printf(" ✅ WHERE Score>900: %d 行, 耗时: %.3fs\n", len(users), q3Time.Seconds())
- passed++
- } else {
- fmt.Printf(" ❌ WHERE Score>900: %v\n", err)
- failed++
- }
- // Age >= 30 AND Age <= 40
- t7 := time.Now()
- users, err = empTbl.Where("Age", ">=", int64(30)).Where("Age", "<=", int64(40)).Find()
- q4Time := time.Since(t7)
- if err == nil {
- fmt.Printf(" ✅ WHERE Age>=30 AND Age<=40: %d 行, 耗时: %.3fs\n", len(users), q4Time.Seconds())
- passed++
- } else {
- fmt.Printf(" ❌ WHERE Age range: %v\n", err)
- failed++
- }
- // ============================================================
- // 8. 排序 + 分页 (大数据量)
- // ============================================================
- section("8. 排序 + 分页")
- t8 := time.Now()
- top10, err := empTbl.OrderBy("Score", true).Limit(10).Find()
- sortTime := time.Since(t8)
- if err == nil && len(top10) == 10 {
- fmt.Printf(" ✅ ORDER BY Score DESC LIMIT 10, 耗时: %.3fs\n", sortTime.Seconds())
- fmt.Printf(" Top3: Score=%d, %d, %d\n", top10[0].Score, top10[1].Score, top10[2].Score)
- passed++
- } else {
- fmt.Printf(" ❌ 排序: err=%v rows=%d\n", err, len(top10))
- failed++
- }
- // 分页: OFFSET 10000 LIMIT 100
- t9 := time.Now()
- page, err := empTbl.OrderBy("Age", false).Offset(10000).Limit(100).Find()
- pageTime := time.Since(t9)
- if err == nil && len(page) == 100 {
- fmt.Printf(" ✅ ORDER BY Age ASC OFFSET 10000 LIMIT 100, 耗时: %.3fs\n", pageTime.Seconds())
- passed++
- } else {
- fmt.Printf(" ❌ 分页: err=%v rows=%d\n", err, len(page))
- failed++
- }
- // ============================================================
- // 9. 聚合查询
- // ============================================================
- section("9. 聚合查询")
- t10 := time.Now()
- cnt, err := empTbl.Query().Count()
- countTime := time.Since(t10)
- if err == nil {
- fmt.Printf(" ✅ COUNT(*): %d, 耗时: %.3fs\n", cnt, countTime.Seconds())
- passed++
- } else {
- fmt.Printf(" ❌ COUNT: %v\n", err)
- failed++
- }
- // ============================================================
- // 10. 事务: 大批量操作
- // ============================================================
- section("10. 事务: 批量插入 + 提交")
- txn := db.BeginTransaction()
- txnN := 10000
- t11 := time.Now()
- for i := EMPLOYEE_N + 1; i <= EMPLOYEE_N+txnN; i++ {
- empTbl.Insert(&Employee{
- Name: fmt.Sprintf("Txn%07d", i),
- Age: int64(25 + i%20),
- Score: int64(500 + i%500),
- Dept: depts[i%len(depts)],
- })
- }
- err = txn.Commit()
- txnTime := time.Since(t11)
- check(fmt.Sprintf("事务插入 %d 行 + Commit, 耗时: %.2fs", txnN, txnTime.Seconds()), err == nil)
- // 验证 (查找事务范围内的行, 验证事务数据持久化)
- u, _ := empTbl.Find(uint64(EMPLOYEE_N + 1))
- if u != nil {
- fmt.Printf(" ✅ 验证: ID=%d 存在 (Name=%s)\n", EMPLOYEE_N+1, u.Name)
- passed++
- } else {
- fmt.Println(" ❌ 验证: 事务数据不存在")
- failed++
- }
- // ============================================================
- // 11. 事务: 回滚
- // ============================================================
- section("11. 事务: 回滚")
- txn2 := db.BeginTransaction()
- rbN := 5000
- for i := EMPLOYEE_N + txnN + 1; i <= EMPLOYEE_N+txnN+rbN; i++ {
- empTbl.Insert(&Employee{
- Name: fmt.Sprintf("Rb%07d", i),
- Age: int64(30),
- Score: int64(0),
- Dept: "ROLLBACK",
- })
- }
- fmt.Printf(" 事务内插入 %d 行\n", rbN)
- err = txn2.Rollback()
- check("Transaction.Rollback()", err == nil)
- // ============================================================
- // 12. 更新 + 删除 (大批量 Scan + Update/Delete)
- // ============================================================
- section("12. 更新 + 删除 (大批量)")
- // 更新: Score > 950 的设为 9999
- t12 := time.Now()
- updateCount := 0
- var updatedIDs []uint64
- empTbl.Where("Score", ">", int64(950)).Scan(func(u *Employee) bool {
- updatedIDs = append(updatedIDs, u.ID)
- u.Score = 9999
- empTbl.Update(u)
- updateCount++
- return true
- })
- updateTime := time.Since(t12)
- fmt.Printf(" ✅ 更新 Score>950 → 9999: %d 行, 耗时: %.3fs\n", updateCount, updateTime.Seconds())
- passed++
- // 删除: Dept = "Legal" 的前100行
- deleteCount := 0
- var firstDeletedID uint64
- deletedSet := make(map[uint64]bool)
- t13 := time.Now()
- empTbl.Where("Dept", "=", "Legal").Limit(100).Scan(func(u *Employee) bool {
- deletedSet[u.ID] = true
- if firstDeletedID == 0 {
- firstDeletedID = u.ID
- }
- empTbl.Delete(u.ID)
- deleteCount++
- return true
- })
- deleteTime := time.Since(t13)
- fmt.Printf(" ✅ 删除 Dept='Legal' 前100行: %d 行, 耗时: %.3fs\n", deleteCount, deleteTime.Seconds())
- passed++
- // 选取一个"确实被更新且未被删除"的行, 用于验证更新持久化
- var verifyUpdatedID uint64
- for _, id := range updatedIDs {
- if !deletedSet[id] {
- verifyUpdatedID = id
- break
- }
- }
- // 验证删除 (检查确实被删除的那一行已不存在)
- _, err = empTbl.Find(firstDeletedID)
- check(fmt.Sprintf("验证 Delete(%d) 成功", firstDeletedID), err != nil)
- err = db.Commit()
- check("更新+删除后 Commit", err == nil)
- // ============================================================
- // 13. 关闭 + 重开验证持久化
- // ============================================================
- section("13. 关闭 + 重开验证持久化")
- fmt.Printf(" 关闭前磁盘: %s\n", fileSize())
- err = db.Close()
- check("DB.Close()", err == nil)
- db, err = DB.Open(dbPath)
- check("重新打开数据库", err == nil)
- empTbl, _ = DB.UseTable[Employee](db, "employee")
- // 验证数据完整性:
- // 基础 50万行 + 事务提交 1万行 - 删除 deleteCount 行
- // (回滚的 5000 行不应存在, 事务行必须持久化)
- expected := int64(EMPLOYEE_N + txnN - deleteCount)
- cnt, _ = empTbl.Query().Count()
- if cnt == expected {
- fmt.Printf(" ✅ 重开后 COUNT: %d (期望 %d, 事务已持久化)\n", cnt, expected)
- passed++
- } else {
- fmt.Printf(" ❌ 重开后 COUNT: %d (期望 %d)\n", cnt, expected)
- failed++
- }
- // 验证更新持久化 (使用确实被更新且未被删除的行)
- u, _ = empTbl.Find(verifyUpdatedID)
- if verifyUpdatedID != 0 && u != nil && u.Score == 9999 {
- fmt.Printf(" ✅ 重开后 Find(%d): Score=%d (更新持久化)\n", verifyUpdatedID, u.Score)
- passed++
- } else if u == nil {
- fmt.Printf(" ❌ 重开后 Find(%d) 不存在\n", verifyUpdatedID)
- failed++
- } else {
- fmt.Printf(" ❌ 重开后 Find(%d): Score=%d (期望 9999)\n", verifyUpdatedID, u.Score)
- failed++
- }
- // 验证回滚数据不存在
- _, err = empTbl.Find(uint64(EMPLOYEE_N + txnN + 1))
- check("重开后回滚数据不存在", err != nil)
- fmt.Printf(" 重开后磁盘: %s | %s\n", fileSize(), memStats())
- // ============================================================
- // 14. 全量扫描验证
- // ============================================================
- section("14. 全量扫描验证")
- scanCount := 0
- t14 := time.Now()
- err = empTbl.Query().Scan(func(u *Employee) bool {
- scanCount++
- return true
- })
- scanTime := time.Since(t14)
- if err == nil {
- fmt.Printf(" ✅ 全量扫描: %d 行, 耗时: %.2fs (%.0f rows/s)\n",
- scanCount, scanTime.Seconds(), float64(scanCount)/scanTime.Seconds())
- passed++
- } else {
- fmt.Printf(" ❌ 全量扫描: %v\n", err)
- failed++
- }
- // ============================================================
- // 15. Order 表验证
- // ============================================================
- section("15. Order 表验证")
- orderTbl, _ = DB.UseTable[Order](db, "order")
- cnt, _ = orderTbl.Query().Count()
- if cnt == int64(ORDER_N) {
- fmt.Printf(" ✅ Order COUNT: %d\n", cnt)
- passed++
- } else {
- fmt.Printf(" ❌ Order COUNT: %d (期望 %d)\n", cnt, ORDER_N)
- failed++
- }
- orders, _ := orderTbl.Where("Status", "=", int64(0)).Find()
- fmt.Printf(" ✅ Order WHERE Status=0: %d 行\n", len(orders))
- passed++
- // ============================================================
- // 最终汇总
- // ============================================================
- fmt.Println()
- fmt.Println(strings.Repeat("=", 60))
- fmt.Printf(" 测试结果: %d 通过, %d 失败\n", passed, failed)
- fmt.Printf(" 数据量: Employee=%d + Order=%d\n", EMPLOYEE_N, ORDER_N)
- fmt.Printf(" 磁盘大小: %s\n", fileSize())
- fmt.Printf(" %s\n", memStats())
- if failed == 0 {
- fmt.Println(" 🎉 全部通过!")
- } else {
- fmt.Printf(" ⚠️ %d 项失败\n", failed)
- }
- fmt.Println(strings.Repeat("=", 60))
- db.Close()
- // [KEEP-DB] 保留数据库文件供 Analyzer 分析, 屏蔽运行结束后的清理删除
- // os.Remove(dbPath)
- // os.Remove(dbPath + ".wal")
- }
|