range over func (Go 1.24+)| 操作 | 延迟 | 吞吐量 |
|---|---|---|
| 唯一索引查询 | 3μs | 330K op/s |
| 普通索引查询 | 23μs | 43K op/s |
| 批量写入 | 2.5ms/条 | 400 op/s |
| 并发查询 | 45μs | 22K QPS |
go get cnb.cool/svn/pkg/nutsdb-orm@latest
要求: Go 1.24+ (需要泛型和 iter.Seq 支持)
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"`
}
func main() {
// 打开数据库(高性能模式)
db, err := nutsorm.Open("/data/kvdb", nutsorm.WithHighPerformance())
if err != nil {
panic(err)
}
defer db.Close()
// 初始化模型(创建 bucket 和索引)
db.Init(&User{})
}
// 创建记录
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)
// 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{})
// 查询多条记录
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)
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("找到已有用户")
}
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()
},
)
// 提取所有 Email
emails, _ := nutsorm.Pluck[string](
nutsorm.Query[User](db).Eq("Status", 1),
"Email",
)
// 提取唯一值
uniqueAges, _ := nutsorm.PluckUnique[int](
nutsorm.Query[User](db),
"Age",
)
// 嵌入 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()
// 方式一:嵌入 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
// 开启写事务
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
}
直接访问 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)
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 时自动淘汰最旧的
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 个(不删除)
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 时自动淘汰分数最低的
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 万条
| 方法 | 说明 | 示例 |
|---|---|---|
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) | 随机获取 |
Iter | iter.Seq2[T, error] | 迭代器 |
Chunk | error | 分批处理 |
FirstOrCreate | (T, bool, error) | 查询或创建 |
UpdateOrCreate | (T, bool, error) | 更新或创建 |
| 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 文件加速启动 | false | true |
WithCacheSize | LRU 缓存条目数 | 0 | 10000 |
WithCommitBuffer | 提交缓冲区大小 | 4MB | 16MB |
WithSyncWrite | 同步写入(数据安全) | true | 按需 |
WithAutoMerge | 自动合并间隔 | 2h | 1-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))
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μs | 1.6KB |
| 普通索引查询 | 23μs | 7.9KB |
| 全表扫描 (3K条) | 2.5ms | 849KB |
| 单条保存 | 2.5ms | 5KB |
| 单条更新 | 1.3ms | 4.4KB |
| 查询方式 | 延迟 | 性能差距 |
|---|---|---|
| 唯一索引 | 3μs | 基准 |
| 普通索引 | 23μs | 8x |
| 全表扫描 | 2.5ms | 833x |
结论: 高频查询字段必须添加索引!
// ✅ 好的设计
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"` // 展示字段,不需要索引
}
// ❌ 低效:逐条保存
for _, user := range users {
db.Save(&user)
}
// ✅ 高效:批量保存
db.SaveAll(users)
// ✅ 或使用事务
tx, _ := db.Begin(true)
for _, user := range users {
tx.Save(&user)
}
tx.Commit()
// ❌ 一次性加载(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)
}
多条件查询无法使用索引,会降级为全表扫描:
// ❌ 无法使用索引
users, _ := nutsorm.Query[User](db).
Eq("Status", 1).
Gt("Age", 18).
Find()
// ✅ 可以使用索引
user, _ := nutsorm.Query[User](db).
Eq("Email", "test@example.com").
First()
唯一索引冲突,检查是否已存在相同值的记录。
确保调用了 tx.Commit():
tx, _ := db.Begin(true)
defer tx.Rollback()
tx.Save(&user)
tx.Commit() // 必须调用
MIT License
Copyright (c) 2024 nutsdb-orm contributors
⭐ 如果这个项目对你有帮助,请给个 Star ⭐