logo
0
0
WeChat Login

NutsDB-ORM

基于 NutsDB 的泛型优先 Go ORM 框架

Go Version License


📖 目录


✨ 特性亮点

核心特性

  • 🎯 泛型优先设计 - 基于 Go 1.24+ 泛型特性,编译时类型安全
  • 🔗 流畅链式 API - 类似 Laravel/ThinkPHP 的优雅查询构建器
  • 高性能索引 - 唯一索引查询 3μs,普通索引 23μs
  • 🔄 原生迭代器 - 支持 range over func (Go 1.24+)
  • 📊 五种数据结构 - BTree(KV)、List、Set、ZSet、TimeSeries 完整支持
  • 🗑️ 软删除支持 - 内置软删除、恢复、强制删除功能
  • 自动时间戳 - 自动维护 CreatedAt/UpdatedAt 字段
  • 📦 有界集合 - List/ZSet 支持最大容量限制和自动淘汰

开发体验

  • 🔐 自动索引维护 - 唯一索引和普通索引自动同步
  • 🎨 多编解码器 - JSON、MsgPack、GOB 可选
  • 🛠️ Functional Options - 灵活的配置模式
  • 高性能模式 - 一键启用性能优化配置

性能数据

操作延迟吞吐量
唯一索引查询3μs330K op/s
普通索引查询23μs43K op/s
批量写入2.5ms/条400 op/s
并发查询45μs22K QPS

🚀 快速开始

安装

go get cnb.cool/svn/pkg/nutsdb-orm@latest

要求: Go 1.24+ (需要泛型和 iter.Seq 支持)

5 分钟入门

1. 定义模型

package main import ( "cnb.cool/svn/pkg/nutsdb-orm" "time" ) type User struct { ID int `nutsorm:"id,increment"` // 主键,自增 Email string `nutsorm:"unique"` // 唯一索引 Name string `nutsorm:"index"` // 普通索引 Age int Status int CreatedAt time.Time } // 或使用内置模型基类(推荐) type User struct { nutsorm.TimestampModel // 自动维护 CreatedAt/UpdatedAt ID int `nutsorm:"id,increment"` Email string `nutsorm:"unique"` Name string `nutsorm:"index"` } // 完整模型(时间戳 + 软删除) type Article struct { nutsorm.FullModel // CreatedAt + UpdatedAt + DeletedAt ID int `nutsorm:"id,increment"` Title string `nutsorm:"unique"` }

2. 初始化数据库

func main() { // 打开数据库(高性能模式) db, err := nutsorm.Open("/data/kvdb", nutsorm.WithHighPerformance()) if err != nil { panic(err) } defer db.Close() // 初始化模型(创建 bucket 和索引) db.Init(&User{}) }

3. 基本操作

// 创建记录 user := &User{ Email: "alice@example.com", Name: "Alice", Age: 25, Status: 1, CreatedAt: time.Now(), } db.Save(user) // user.ID 自动填充为 1 // 查询记录 found, _ := nutsorm.Query[User](db). Eq("Email", "alice@example.com"). First() fmt.Println(found.Name) // Alice // 更新记录 found.Age = 26 db.Update(&found) // 删除记录 db.Delete(&found)

🎯 核心功能

1. ORM 操作

基础 CRUD

// Save - 保存记录(自动填充自增 ID) user := &User{Email: "test@example.com", Name: "Test", Age: 25} db.Save(user) // Update - 更新记录 user.Name = "Updated" db.Update(user) // UpdateFields - 部分字段更新 user.Age = 30 db.UpdateFields(user, "Age") // 只更新 Age 字段 // Delete - 删除记录 db.Delete(user) // DeleteByID - 按 ID 删除 db.DeleteByID(&User{}, 1)

批量操作

// SaveAll - 批量保存 users := []*User{ {Email: "a@test.com", Name: "A"}, {Email: "b@test.com", Name: "B"}, } db.SaveAll(users) // UpdateAll - 批量更新 db.UpdateAll(users) // UpsertAll - 批量 Upsert(存在则更新,不存在则插入) db.UpsertAll(users) // DeleteByIDs - 批量删除 db.DeleteByIDs(&User{}, []int{1, 2, 3}) // FindByIDs - 批量查询 var found []User db.FindByIDs([]int{1, 2, 3}, &found)

查询方法

// One - 按索引字段查询单条 var user User db.One("Email", "test@example.com", &user) // FindByID - 按主键查询 db.FindByID(1, &user) // All - 查询所有记录 var users []User db.All(&users) // Count - 统计数量 count, _ := db.Count(&User{}) // Exists - 检查是否存在 exists, _ := db.Exists("Email", "test@example.com", &User{})

2. 链式查询

基础查询

// 查询多条记录 users, err := nutsorm.Query[User](db). Eq("Status", 1). // 状态等于 1 Gt("Age", 18). // 年龄大于 18 Limit(10). // 限制 10 条 Find() // 查询第一条 user, err := nutsorm.Query[User](db). Eq("Email", "test@example.com"). First() // 统计数量 count, err := nutsorm.Query[User](db). Eq("Status", 1). Count() // 删除匹配记录 deleted, err := nutsorm.Query[User](db). Lt("Age", 18). Delete()

查询条件

// 比较条件 Eq("Status", 1) // 等于 Neq("Status", 0) // 不等于 Gt("Age", 18) // 大于 Gte("Age", 18) // 大于等于 Lt("Age", 60) // 小于 Lte("Age", 60) // 小于等于 // 范围条件 Between("Age", 18, 60) // 范围内 NotBetween("Age", 0, 17) // 范围外 In("Status", 1, 2, 3) // 在集合中 NotIn("Status", 0, -1) // 不在集合中 // 字符串条件 Like("Name", "Admin") // 前缀匹配 NotLike("Name", "Test") // 前缀不匹配 // 空值条件 IsNull("DeletedAt") // 为空 IsNotNull("Email") // 非空

复杂条件组合

import "cnb.cool/svn/pkg/nutsdb-orm/q" // AND 组合 users, _ := nutsorm.Query[User](db). Where(q.And( q.Eq("Status", 1), q.Gt("Age", 18), )). Find() // OR 组合 users, _ := nutsorm.Query[User](db). Where(q.Or( q.Eq("Status", 1), q.Eq("Status", 2), )). Find() // 嵌套组合 users, _ := nutsorm.Query[User](db). Where(q.And( q.Eq("Status", 1), q.Or( q.Gt("Age", 30), q.Like("Name", "Admin"), ), )). Find()

分页与迭代

// 分页查询 result, _ := nutsorm.Query[User](db). Eq("Status", 1). Paginate(1, 20) // 第 1 页,每页 20 条 fmt.Printf("总数: %d, 总页数: %d\n", result.Total, result.TotalPages) for _, user := range result.Data { fmt.Println(user.Name) } // 迭代器查询(Go 1.24+) for user, err := range nutsorm.Query[User](db).Eq("Status", 1).Iter() { if err != nil { break } fmt.Println(user.Name) } // 分批处理 nutsorm.Query[User](db).Chunk(100, func(users []User) bool { for _, u := range users { process(u) } return true // 返回 false 可提前停止 }) // 随机获取 randomUsers, _ := nutsorm.Query[User](db).Random(5)

3. 高级功能

FirstOrCreate - 查询或创建

user, created, err := nutsorm.Query[User](db). Eq("Email", "test@example.com"). FirstOrCreate(&User{ Email: "test@example.com", Name: "New User", Age: 25, }) if created { fmt.Println("创建了新用户") } else { fmt.Println("找到已有用户") }

UpdateOrCreate - 更新或创建

user, created, err := nutsorm.Query[User](db). Eq("Email", "test@example.com"). UpdateOrCreate( &User{Email: "test@example.com", Name: "Default"}, func(existing *User) { existing.LoginCount++ existing.LastLoginAt = time.Now() }, )

Pluck - 提取字段值

// 提取所有 Email emails, _ := nutsorm.Pluck[string]( nutsorm.Query[User](db).Eq("Status", 1), "Email", ) // 提取唯一值 uniqueAges, _ := nutsorm.PluckUnique[int]( nutsorm.Query[User](db), "Age", )

4. 软删除

启用软删除

// 嵌入 SoftDeleteModel 启用软删除 type Article struct { nutsorm.SoftDeleteModel // 包含 DeletedAt 字段 ID int `nutsorm:"id,increment"` Title string `nutsorm:"unique"` Content string }

软删除操作

// 软删除(标记删除) db.SoftDelete(&article) // 通过 ID 软删除 db.SoftDeleteByID(&Article{}, 1) // 恢复软删除 db.Restore(&article) // 通过 ID 恢复 db.RestoreByID(&Article{}, 1) // 强制物理删除 db.ForceDelete(&article)

软删除查询

// 默认查询:自动过滤已删除记录 articles, _ := nutsorm.Query[Article](db).Find() // 包含已删除记录 articles, _ := nutsorm.Query[Article](db).WithTrashed().Find() // 只查询已删除记录 deleted, _ := nutsorm.Query[Article](db).OnlyTrashed().Find() // Count 也支持软删除过滤 activeCount, _ := nutsorm.Query[Article](db).Count() allCount, _ := nutsorm.Query[Article](db).WithTrashed().Count() deletedCount, _ := nutsorm.Query[Article](db).OnlyTrashed().Count()

5. 自动时间戳

启用时间戳

// 方式一:嵌入 TimestampModel type User struct { nutsorm.TimestampModel // 包含 CreatedAt, UpdatedAt ID int `nutsorm:"id,increment"` Email string `nutsorm:"unique"` } // 方式二:嵌入 FullModel(时间戳 + 软删除) type Article struct { nutsorm.FullModel // CreatedAt + UpdatedAt + DeletedAt ID int `nutsorm:"id,increment"` Title string `nutsorm:"unique"` } // 方式三:手动定义字段(自动识别) type Order struct { ID int `nutsorm:"id,increment"` CreatedAt time.Time // 自动设置 UpdatedAt time.Time // 自动更新 }

时间戳行为

user := &User{Email: "test@example.com"} // Save 时自动设置 CreatedAt 和 UpdatedAt db.Save(user) fmt.Println(user.CreatedAt) // 2024-01-01 12:00:00 fmt.Println(user.UpdatedAt) // 2024-01-01 12:00:00 // Update 时只更新 UpdatedAt user.Email = "new@example.com" db.Update(user) fmt.Println(user.CreatedAt) // 2024-01-01 12:00:00(不变) fmt.Println(user.UpdatedAt) // 2024-01-01 12:05:00(更新) // Upsert 智能判断 db.Upsert(user) // 新记录设置 CreatedAt,已存在只更新 UpdatedAt // 手动设置的值会被保留 user.CreatedAt = time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC) db.Save(user) // CreatedAt 保持 2020-01-01

6. 事务管理

// 开启写事务 tx, err := db.Begin(true) if err != nil { return err } defer tx.Rollback() // 确保异常时回滚 // 在事务中执行操作 tx.Save(&user1) tx.Save(&user2) tx.Update(&user3) // 事务中使用查询构建器 users, _ := nutsorm.Query[User](tx).Eq("Status", 1).Find() // 提交事务 if err := tx.Commit(); err != nil { return err }

7. Raw KV API

直接访问 NutsDB 的 BTree 键值存储。

// 获取 Raw API 实例 raw := db.Bucket("cache").Raw() // Put - 写入键值 raw.Put([]byte("key"), []byte("value"), 0) // TTL=0 永不过期 raw.Put([]byte("temp"), []byte("data"), 3600) // 1 小时后过期 // Get - 读取键值 value, err := raw.Get([]byte("key")) // Delete - 删除键 raw.Delete([]byte("key")) // RangeScan - 范围扫描 values, _ := raw.RangeScan([]byte("user:000"), []byte("user:100")) // PrefixScan - 前缀扫描 values, _ := raw.PrefixScan([]byte("session:"), 0, 100)

8. 数据结构

List - 列表

list := nutsorm.List[Task](db, "queue") list.Push(Task{Name: "job1"}) // 尾部添加 list.LPush(Task{Name: "job0"}) // 头部添加 task, _ := list.Pop() // 尾部弹出 task, _ := list.LPop() // 头部弹出 tasks, _ := list.Range(0, 9) // 获取范围 count, _ := list.Len() // 获取长度

有界队列 - 自动淘汰超限元素:

import "cnb.cool/svn/pkg/nutsdb-orm/ds" // 创建最大容量为 1000 的有界队列(FIFO 淘汰最旧的) queue := nutsorm.List[Task](db, "jobs").WithMaxSize(1000) // 使用 LIFO 策略(淘汰最新的) stack := nutsorm.List[Task](db, "jobs").WithMaxSize(100, ds.EvictNewest) queue.Push(task) // 超过 1000 时自动淘汰最旧的

Set - 集合

tags := nutsorm.Set[string](db, "tags") tags.Add("go", "rust", "python") // 添加成员 tags.Remove("python") // 删除成员 exists, _ := tags.Contains("go") // 检查存在 members, _ := tags.Members() // 获取所有成员 // 随机操作 popped, _ := tags.Pop() // 随机弹出并删除一个 random, _ := tags.RandomMembers(5) // 随机获取 5 个(不删除)

ZSet - 有序集合

leaderboard := nutsorm.ZSet[Player](db, "scores") leaderboard.Add(100.5, Player{Name: "Alice"}) // 添加(按分数排序) top10, _ := leaderboard.RangeByRank(0, 10) // 前 10 名 rank, _ := leaderboard.Rank(player) // 获取排名 score, _ := leaderboard.ZScore(player) // 获取分数 // 带分数查询 members, _ := leaderboard.RangeByScoreWithScores(0, 100) for _, m := range members { fmt.Printf("%v: %.2f\n", m.Value, m.Score) } top3, _ := leaderboard.RangeByRankWithScores(0, 3) for i, m := range top3 { fmt.Printf("#%d %v: %.2f\n", i+1, m.Value, m.Score) }

有界排行榜 - 只保留 Top N:

// 创建只保留前 100 名的排行榜 leaderboard := nutsorm.ZSet[Player](db, "scores").WithMaxSize(100) leaderboard.Add(50, player) // 超过 100 时自动淘汰分数最低的

TimeSeries - 时序数据

import "cnb.cool/svn/pkg/nutsdb-orm/ts" // 创建时序数据集 metrics := nutsorm.TimeSeries[float64](db, "cpu_usage"). WithRetention(24 * time.Hour). // 保留 24 小时 WithMaxPoints(10000) // 最多 1 万条 // 写入数据 metrics.Write(75.5) // 自动使用当前时间 metrics.WriteAt(time.Now(), 80.0) // 指定时间 // 时间范围查询 points, _ := metrics.Range(start, end).Find() // 时间范围 points, _ := metrics.Last(time.Hour).Find() // 最近 1 小时 points, _ := metrics.LastN(100).Find() // 最近 100 条 points, _ := metrics.First(10).Find() // 最早 10 条 points, _ := metrics.All().Find() // 所有数据 // 聚合计算 sum, _ := ts.Sum(metrics.Last(time.Hour)) // 求和 avg, _ := ts.Avg(metrics.Last(time.Hour)) // 平均值 min, _ := ts.Min(metrics.Last(time.Hour)) // 最小值 max, _ := ts.Max(metrics.Last(time.Hour)) // 最大值 count, _ := metrics.Last(time.Hour).Count() // 统计数量 // 完整统计信息 stats, _ := ts.Statistics(metrics.All()) fmt.Printf("Count: %d, Sum: %.2f, Avg: %.2f, Min: %.2f, Max: %.2f\n", stats.Count, stats.Sum, stats.Avg, stats.Min, stats.Max) // 获取最新/最早数据点 latest, _ := metrics.Latest() oldest, _ := metrics.Oldest() // 数据维护 metrics.Clear() // 清空所有数据 metrics.Trim(start, end) // 只保留指定范围

时序数据配置

// WithRetention - 数据保留期(自动清理过期数据) metrics := nutsorm.TimeSeries[float64](db, "metrics"). WithRetention(7 * 24 * time.Hour) // 保留 7 天 // WithMaxPoints - 最大数据点数量(自动淘汰最旧数据) metrics := nutsorm.TimeSeries[float64](db, "metrics"). WithMaxPoints(100000) // 最多 10 万条

📚 API 参考

查询条件方法

方法说明示例
Eq等于Eq("Status", 1)
Neq不等于Neq("Status", 0)
Gt大于Gt("Age", 18)
Gte大于等于Gte("Age", 18)
Lt小于Lt("Age", 60)
Lte小于等于Lte("Age", 60)
Like前缀匹配Like("Name", "Admin")
Between范围内Between("Age", 18, 60)
In在集合中In("Status", 1, 2, 3)
IsNull为空IsNull("DeletedAt")
IsNotNull非空IsNotNull("Email")

查询执行方法

方法返回值说明
Find([]T, error)查询多条记录
First(T, error)查询第一条
Count(int, error)统计数量
Delete(int, error)删除匹配记录
Paginate(PaginateResult, error)分页查询
Random([]T, error)随机获取
Iteriter.Seq2[T, error]迭代器
Chunkerror分批处理
FirstOrCreate(T, bool, error)查询或创建
UpdateOrCreate(T, bool, error)更新或创建

⚙️ 配置与优化

Struct Tag 标签

Tag说明示例
id标记为主键字段nutsorm:"id"
increment自增主键(仅 int 类型)nutsorm:"id,increment"
unique唯一索引nutsorm:"unique"
index普通索引(一对多)nutsorm:"index"
bucket:name自定义 bucket 名称nutsorm:"bucket:users"

性能配置选项

// 高性能模式(推荐生产环境使用) db, _ := nutsorm.Open("/data/kvdb", nutsorm.WithHighPerformance()) // 自定义配置 db, _ := nutsorm.Open("/data/kvdb", nutsorm.WithHintFile(true), // 启用 Hint 文件(加速启动) nutsorm.WithCacheSize(10000), // LRU 缓存大小 nutsorm.WithCommitBuffer(16*1024*1024), // 提交缓冲区 16MB nutsorm.WithSyncWrite(false), // 异步写入(高性能) nutsorm.WithAutoMerge(time.Hour), // 自动合并间隔 )
配置选项说明默认值推荐值
WithHighPerformance一键高性能模式-生产环境
WithHintFile启用 Hint 文件加速启动falsetrue
WithCacheSizeLRU 缓存条目数010000
WithCommitBuffer提交缓冲区大小4MB16MB
WithSyncWrite同步写入(数据安全)true按需
WithAutoMerge自动合并间隔2h1-6h

编解码器

import "cnb.cool/svn/pkg/nutsdb-orm/codec/json" import "cnb.cool/svn/pkg/nutsdb-orm/codec/msgpack" import "cnb.cool/svn/pkg/nutsdb-orm/codec/gob" // JSON(默认,推荐开发环境) db, _ := nutsorm.Open(path, nutsorm.WithCodec(json.Codec)) // MsgPack(推荐生产环境,性能更高) db, _ := nutsorm.Open(path, nutsorm.WithCodec(msgpack.Codec)) // GOB(Go 专用) db, _ := nutsorm.Open(path, nutsorm.WithCodec(gob.Codec))

📊 性能基准

测试环境

  • CPU: AMD EPYC 9K65 192-Core
  • Go: 1.24+
  • NutsDB: v1.1.0

基准测试结果

BenchmarkQueryByUniqueIndex-8 363649 3009 ns/op 1617 B/op 37 allocs/op BenchmarkQueryByIndex-8 50203 23244 ns/op 7904 B/op 103 allocs/op BenchmarkSaveBatch-8 490 2485946 ns/op 4953 B/op 83 allocs/op BenchmarkUpdateByID-8 1130 1287517 ns/op 4385 B/op 81 allocs/op BenchmarkTransactionCommit-8 1132 1381037 ns/op 4256 B/op 76 allocs/op

性能总结

操作延迟内存
唯一索引查询3μs1.6KB
普通索引查询23μs7.9KB
全表扫描 (3K条)2.5ms849KB
单条保存2.5ms5KB
单条更新1.3ms4.4KB

索引 vs 全表扫描

查询方式延迟性能差距
唯一索引3μs基准
普通索引23μs8x
全表扫描2.5ms833x

结论: 高频查询字段必须添加索引!


💡 最佳实践

1. 索引设计

// ✅ 好的设计 type User struct { ID int `nutsorm:"id,increment"` Email string `nutsorm:"unique"` // 高频精确查询 → 唯一索引 Status int `nutsorm:"index"` // 高频范围查询 → 普通索引 Name string // 低频查询 → 无索引 } // ❌ 过度索引 type User struct { ID int `nutsorm:"id,increment"` Email string `nutsorm:"unique"` Name string `nutsorm:"index"` // 很少单独查询 Avatar string `nutsorm:"index"` // 展示字段,不需要索引 }

2. 批量操作

// ❌ 低效:逐条保存 for _, user := range users { db.Save(&user) } // ✅ 高效:批量保存 db.SaveAll(users) // ✅ 或使用事务 tx, _ := db.Begin(true) for _, user := range users { tx.Save(&user) } tx.Commit()

3. 大数据处理

// ❌ 一次性加载(OOM 风险) users, _ := nutsorm.Query[User](db).Find() // ✅ 分批处理 nutsorm.Query[User](db).Chunk(1000, func(users []User) bool { process(users) return true }) // ✅ 流式迭代 for user, err := range nutsorm.Query[User](db).Iter() { process(user) }

🔧 故障排除

Q1: 索引查询没有加速?

多条件查询无法使用索引,会降级为全表扫描:

// ❌ 无法使用索引 users, _ := nutsorm.Query[User](db). Eq("Status", 1). Gt("Age", 18). Find() // ✅ 可以使用索引 user, _ := nutsorm.Query[User](db). Eq("Email", "test@example.com"). First()

Q2: 保存时报 ErrAlreadyExists

唯一索引冲突,检查是否已存在相同值的记录。

Q3: 事务提交后数据未生效?

确保调用了 tx.Commit()

tx, _ := db.Begin(true) defer tx.Rollback() tx.Save(&user) tx.Commit() // 必须调用

📄 许可证

MIT License

Copyright (c) 2024 nutsdb-orm contributors


⭐ 如果这个项目对你有帮助,请给个 Star ⭐

About

基于 NutsDB 的泛型优先 Go ORM 框架

Language
Go100%