← 返回 Skills 市场
lynnss-ai

Gorm Expert

作者 xuebing · GitHub ↗ · v1.6.0 · MIT-0
cross-platform ✓ 安全检测通过
200
总下载
0
收藏
0
当前安装
1
版本数
在 OpenClaw 中安装
/install gorm-expert-skill
功能描述
GORM v2 最佳实践与性能优化。适用于:代码审查、慢查询优化、N+1、连接池、 事务管理、分库分表、Prometheus/OTel监控、Session安全、Clause/Upsert、 缓存集成、BaseModel脚手架、SQL→struct生成、多租户隔离。 触发词:GORM、数据库慢、加索引、写struc...
使用说明 (SKILL.md)

GORM 使用与性能优化 Skill

脚本工具(优先用脚本,减少 token 消耗)

用户提供代码/SQL/参数时,先跑脚本,只输出脚本结果 + 针对性说明。 所有脚本仅依赖 Python 3.8+,不调用任何外部 API 或凭证。

场景 脚本 用法示例
Go 代码审查 scripts/analyze_gorm.py python3 scripts/analyze_gorm.py - \x3C\x3C\x3C "代码"(R1–R27;CI 用 --format json
CREATE TABLE → struct scripts/gen_model.py echo "CREATE TABLE..." | python3 scripts/gen_model.py -(PG 加 --dialect pg
连接池计算 scripts/pool_advisor.py python3 scripts/pool_advisor.py --qps 500 --avg-latency-ms 20 --app-instances 4
SQL 性能分析 scripts/query_explain.py python3 scripts/query_explain.py "SELECT * FROM ..."
迁移 SQL 生成 scripts/migration_gen.py python3 scripts/migration_gen.py old.go new.go --table users
Scope 生成 scripts/scope_gen.py python3 scripts/scope_gen.py model.go --tenant --paginate
Benchmark 模板 scripts/bench_template.py 默认 stdout;写文件加 --output bench_test.go
项目初始化 scripts/init_project.py --dry-run 预览,再 --output ./internal/dbcore 写入

核心原则

  1. 禁止物理外键,必须使用逻辑外键 — 所有关联字段只建索引,不建 FK CONSTRAINT;gorm.Config 必须设置 DisableForeignKeyConstraintWhenMigrating: true;若已有 foreignKey tag 必须加 constraint:false
  2. 先测量,再优化db.Debug() 或自定义 Logger 定位慢 SQL
  3. 最小数据传输 — 只 Select 需要的字段,只查需要的行
  4. 减少 Round-trip — 批量操作、Preload vs 懒加载权衡
  5. 连接复用 — 正确配置连接池,避免频繁开关连接

1. 初始化与连接池

db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{
    SkipDefaultTransaction:                   true,  // 写性能 +30%
    PrepareStmt:                              true,  // SQL 编译缓存
    DisableForeignKeyConstraintWhenMigrating: true,  // 禁物理 FK
    Logger: logger.New(writer, logger.Config{
        SlowThreshold: 200 * time.Millisecond,
        LogLevel:      logger.Warn,
    }),
})
sqlDB, _ := db.DB()
sqlDB.SetMaxOpenConns(100)
sqlDB.SetMaxIdleConns(20)            // MaxOpen 的 20%
sqlDB.SetConnMaxLifetime(time.Hour)
sqlDB.SetConnMaxIdleTime(10 * time.Minute)

连接池参数建议:python3 scripts/pool_advisor.py --qps 500 --avg-latency-ms 20

ID 生成策略(使用 dbcore 脚手架时配置):

// Snowflake(默认,零依赖)
dbcore.SetIDGenerator(dbcore.NewSnowflakeGenerator(nodeID))
// Leaf-Segment(高并发推荐,严格递增,需 MySQL 号段表)
dbcore.SetIDGenerator(dbcore.NewLeafSegmentGenerator(db, "order"))
// UUID(无需协调,不可排序)
dbcore.SetIDGenerator(dbcore.NewUUIDGenerator())

策略选型、时钟回拨保护、K8s nodeID 管理详见 references/id-generation.md

多租户配置(按需开启,默认关闭):

dbcore.SetConfig(dbcore.Config{
    TenantEnabled: true,            // 开启多租户隔离
    TenantField:   "tenant_id",     // 租户字段名
    TenantStrict:  true,            // 严格模式:无租户时拒绝查询
    TenantExtractor: func(ctx context.Context) string {
        if tid, ok := ctx.Value("tenant_id").(string); ok { return tid }
        return ""
    },
})
// 开启后所有 BaseModel CRUD 自动注入 tenant_id 条件,无需手动拼接
// 不需要多租户时,不调用 SetConfig 即可(TenantEnabled 默认 false)

脚手架初始化

python3 scripts/init_project.py --output ./internal/dbcore          # 写入
python3 scripts/init_project.py --output ./internal/dbcore --dry-run # 仅预览
python3 scripts/init_project.py --output ./internal/dbcore --example # 含示例

2. 查询优化

2.1 只查需要的字段

// ❌ SELECT *
db.Find(&users)
// ✅ SELECT id, name, email
db.Select("id", "name", "email").Find(&users)
// ✅ 投影到小 struct
db.Model(&User{}).Select("id", "name").Scan(&dtos)

2.2 避免 N+1 查询

// ❌ N+1:循环内查 DB
for _, u := range users { db.Where("user_id=?", u.ID).Find(&u.Orders) }
// ✅ Preload:两次查询
db.Preload("Orders").Find(&users)
// ✅ Joins:一次 JOIN,按关联字段过滤
db.Joins("JOIN orders ON orders.user_id=users.id AND orders.status=?","paid").Find(&users)
// ✅ 带条件 Preload
db.Preload("Orders", "status=?", "paid").Preload("Orders.Items").Find(&users)

Preload vs Joins 选型详见 references/association.md

2.3 分批处理大数据集

// ✅ FindInBatches:每批 500 条
db.Model(&User{}).FindInBatches(&batch, 500, func(tx *gorm.DB, n int) error {
    process(batch); return nil
})
// ✅ 游标分页(大表推荐,避免 OFFSET 退化)
var lastID uint
for {
    var users []User
    db.Where("id > ?", lastID).Order("id").Limit(500).Find(&users)
    if len(users) == 0 { break }
    lastID = users[len(users)-1].ID
}

2.4 聚合与子查询

// 分组统计
db.Model(&Order{}).Select("user_id, SUM(amount) as total").
    Group("user_id").Having("SUM(amount) > ?", 1000).Scan(&results)
// 去重
db.Distinct("status").Model(&Order{}).Pluck("status", &statuses)
// 子查询
subQuery := db.Model(&Order{}).Select("user_id").Where("amount > ?", 1000)
db.Where("id IN (?)", subQuery).Find(&users)

2.5 流式读取与索引提示

// 流式读取(超大数据集,逐行处理)
rows, _ := db.Model(&User{}).Where("status = ?", "active").Rows()
defer rows.Close()
for rows.Next() { var u User; db.ScanRows(rows, &u); process(u) }

// 索引提示
db.Clauses(hints.UseIndex("idx_user_name")).Find(&users)
db.Clauses(hints.ForceIndex("idx_created_at").ForOrderBy()).Find(&orders)

3. 写操作优化

3.1 批量插入

// ❌ 逐条 INSERT(N 次 Round-trip)
for _, u := range users { db.Create(&u) }
// ✅ 批量插入
db.CreateInBatches(&users, 200)

3.2 按需更新,不更新零值

// ❌ struct Updates 忽略零值(int=0, bool=false)
db.Model(&user).Updates(User{Name: "new", Age: 0}) // Age=0 被忽略!
// ✅ map 明确指定字段
db.Model(&user).Updates(map[string]any{"name": "new", "age": 0})
// ✅ Select 限制 / Omit 排除
db.Model(&user).Select("name", "email").Updates(&user)
db.Model(&user).Omit("password").Updates(&user)

3.3 检查 RowsAffected

result := db.Model(&User{}).Where("id = ?", id).Update("status", "banned")
if result.Error != nil { return result.Error }
if result.RowsAffected == 0 { return fmt.Errorf("user %d not found", id) }

3.4 Upsert

db.Clauses(clause.OnConflict{
    Columns:   []clause.Column{{Name: "email"}},
    DoUpdates: clause.AssignmentColumns([]string{"name", "updated_at"}),
}).Create(&user)
db.Clauses(clause.OnConflict{DoNothing: true}).Create(&users) // 忽略冲突

4. 事务管理

// 标准事务
err := db.Transaction(func(tx *gorm.DB) error {
    if err := tx.Create(&order).Error; err != nil { return err }
    return tx.Model(&stock).Update("qty", gorm.Expr("qty - ?", 1)).Error
})
// 嵌套事务(GORM 自动 SavePoint)
tx.Transaction(func(tx2 *gorm.DB) error { return tx2.Create(&log).Error })
// 悲观锁(FOR UPDATE)
db.Clauses(clause.Locking{Strength: "UPDATE"}).Where("id=?", id).First(&stock)

乐观锁、CAS、Savepoint、事务陷阱详见 references/concurrency.md


5. 读写分离

db.Use(dbresolver.Register(dbresolver.Config{
    Sources:  []gorm.Dialector{mysql.Open(writeDSN)},
    Replicas: []gorm.Dialector{mysql.Open(read1DSN), mysql.Open(read2DSN)},
    Policy:   dbresolver.RandomPolicy{},
}).SetMaxOpenConns(50).SetMaxIdleConns(10))
// GORM 自动路由:Find/First → Replica;Create/Update/Delete → Source
db.Clauses(dbresolver.Write).Find(&user) // 强制走主库

6. Scopes 与多租户

// 可复用 Scope
func ActiveUser(db *gorm.DB) *gorm.DB { return db.Where("status = ?", "active") }
func AgeOver(age int) func(*gorm.DB) *gorm.DB {
    return func(db *gorm.DB) *gorm.DB { return db.Where("age > ?", age) }
}
db.Scopes(ActiveUser, AgeOver(18)).Find(&users)

多租户隔离(两种方式,按需选择):

// 方式 1(推荐):通过 dbcore.Config 全局开关,BaseModel 自动注入
// 开启:dbcore.SetConfig(dbcore.Config{TenantEnabled: true, ...})
// 关闭:不调用 SetConfig 或 TenantEnabled: false(默认)
// 详见 §1 初始化配置

// 方式 2:手动 Scope(不使用 dbcore 时适用)
func TenantScope(ctx context.Context) func(*gorm.DB) *gorm.DB {
    return func(db *gorm.DB) *gorm.DB {
        if tid, ok := ctx.Value("tenant_id").(string); ok && tid != "" {
            return db.Where("tenant_id = ?", tid)
        }
        return db.Where("1 = 0")
    }
}

自动生成:python3 scripts/scope_gen.py model.go --tenant --paginate 完整示例详见 references/scopes.md


7. Model 设计规范

type Order struct {
    gorm.Model
    UserID uint   `gorm:"not null;index"`        // 逻辑外键:只加索引,不建 FK
    Status string `gorm:"type:varchar(20);index"`
    Amount int64  `gorm:"not null;default:0"`
}

逻辑外键规范(核心原则 #1):

// ❌ 物理外键:会在 AutoMigrate 时创建 FK CONSTRAINT
User User `gorm:"foreignKey:UserID"`
// ❌ 有 references 但没有 constraint:false
User User `gorm:"foreignKey:UserID;references:ID"`

// ✅ 逻辑外键:显式禁止约束
User User `gorm:"foreignKey:UserID;constraint:false"`
// ✅ 全局禁止(必须写在 gorm.Config 中)
db, _ := gorm.Open(dsn, &gorm.Config{
    DisableForeignKeyConstraintWhenMigrating: true,
})

物理外键的问题:分库分表不兼容、级联不可控、高并发性能瓶颈、导入迁移困难。 详见 references/base-model-pattern.md


8. 调试与性能分析

db.Debug().Find(&users)  // 单次打印 SQL
// ToSQL(不执行,只生成 SQL)
sql := db.ToSQL(func(tx *gorm.DB) *gorm.DB {
    return tx.Where("id > ?", 100).Limit(10).Find(&users)
})

pprof / Benchmark 详见 references/observability.md


9. 常见坑与反模式

问题 错误写法 正确做法
忘记传 Context db.Find(&u) db.WithContext(ctx).Find(&u)
struct Updates 丢零值 db.Updates(User{Age:0}) db.Updates(map[string]any{"age":0})
大 OFFSET 分页 db.Offset(100000).Limit(20) 游标分页 WHERE id > lastID
前导通配 Like LIKE '%foo%' 前缀匹配 LIKE 'foo%' 或全文索引
事务内耗时操作 事务 + HTTP 调用 HTTP 移到事务外
未检查 Error db.Find(&u); use u if err := db.Find(&u).Error; err != nil
物理外键 未加 constraint:false constraint:false 或全局禁止
Save 全量更新 db.Save(&user) db.Model(&u).Updates(map[string]any{...})
忽略 RowsAffected 不检查影响行数 if result.RowsAffected == 0 { ... }

10. 缓存集成(Cache-Aside)

func (r *UserRepo) GetUser(ctx context.Context, id uint) (*User, error) {
    key := fmt.Sprintf("user:%d", id)
    if val, err := r.rdb.Get(ctx, key).Bytes(); err == nil {
        var u User; json.Unmarshal(val, &u); return &u, nil
    }
    // singleflight 防击穿
    res, err, _ := r.group.Do(key, func() (any, error) {
        var u User
        if err := r.db.WithContext(ctx).First(&u, id).Error; err != nil {
            if errors.Is(err, gorm.ErrRecordNotFound) {
                r.rdb.Set(ctx, key, "null", time.Minute) // 防穿透
            }
            return nil, err
        }
        ttl := 30*time.Minute + time.Duration(rand.Int63n(int64(6*time.Minute))) // 抖动防雪崩
        data, _ := json.Marshal(u)
        r.rdb.Set(ctx, key, data, ttl)
        return &u, nil
    })
    // 写操作:更新 DB → 删缓存(不更新缓存)
}

布隆过滤器防穿透、延迟双删、列表缓存详见 references/caching.md


11. 分库分表(Sharding)

db.Use(sharding.Register(sharding.Config{
    ShardingKey: "user_id", NumberOfShards: 64,
    PrimaryKeyGenerator: sharding.PKSnowflake,
}, "orders"))
db.Where("user_id = ?", userID).Find(&orders) // 携带分片键 → 自动路由

分片算法、双写迁移、跨分片查询详见 references/sharding.md


12. 监控与可观测性

db.Use(prometheus.New(prometheus.Config{DBName: "myapp", RefreshInterval: 15}))
db.Use(otelgorm.NewPlugin(otelgorm.WithDBName("myapp"))) // OpenTelemetry

Prometheus 告警、Grafana 仪表盘、pprof 详见 references/observability.md


13. GORM v2 核心机制

13.1 Session 与 goroutine 安全

// ❌ 条件累积 + goroutine 数据竞争
base := db.Where("tenant_id = ?", tid)
go func() { base.Find(&list1) }() // 危险!
// ✅ Session 隔离
go func() { base.Session(&gorm.Session{NewDB: true}).Find(&list1) }()

Session 配置项、8 种陷阱详见 references/session.md

13.2 Clause 系统

db.Clauses(clause.Locking{Strength: "UPDATE"}).First(&stock) // FOR UPDATE
db.Clauses(clause.Locking{Strength: "UPDATE", Options: "SKIP LOCKED"}).Find(&tasks) // 任务抢占
db.Clauses(clause.Returning{}).Create(&user) // RETURNING(PostgreSQL)

完整 Clause 用法、自定义表达式详见 references/clause.md

13.3 Association 关联操作

db.Preload("Orders", "status = ?", "paid").Find(&users)
db.Preload(clause.Associations).Find(&users)         // 预加载所有关联
db.Omit(clause.Associations).Create(&user)           // 跳过关联写入
db.Model(&user).Association("Orders").Append(&order)  // 添加关联

Preload vs Joins、级联控制、多对多详见 references/association.md

13.4 Serializer 与自定义类型

type User struct {
    Tags  []string `gorm:"type:json;serializer:json"` // JSON 自动序列化
    Phone string   `gorm:"serializer:encrypted"`      // 自定义加密
}

完整实现、GormDataType 接口详见 references/serializer.md

13.5 Error 处理规范(v2)

errors.Is(err, gorm.ErrRecordNotFound) // ✅ v2 正确写法
// ❌ gorm.IsRecordNotFoundError(err) — v1 API,v2 已移除
// 注意:Find 不触发 ErrRecordNotFound;First/Take/Last 触发

14. 进阶参考

详细专题见 references/ 目录(按需加载,不要全量读入):

文件 内容 触发时机
base-model-pattern.md BaseModel Bug 修复、游标分页、多租户隔离 BaseModel、QueryBuilder、分页
session.md Session 机制、goroutine 安全、条件累积 Session、db 复用、DryRun
clause.md Clause 系统(Upsert/FOR UPDATE/RETURNING) FOR UPDATE、Upsert
association.md Preload/Joins、级联控制 关联加载、多对多
serializer.md Serializer、自定义类型(枚举/Money/加密) 字段序列化
hooks.md Hook 执行顺序、性能陷阱 Hook 使用
raw-sql.md Raw SQL / Scan / Rows 原生 SQL
indexing.md 索引设计、EXPLAIN 验证 索引、慢查询
concurrency.md 乐观锁、悲观锁、CAS 并发冲突、超卖
testing.md sqlmock、SQLite 集成测试 GORM 测试
migration.md golang-migrate、大表在线 DDL 数据库迁移
sharding.md 分库分表、分片算法 水平拆分
observability.md Prometheus、OTel、Grafana 监控
scopes.md Scope、分页、多租户 Scope 用法
caching.md Cache-Aside、防击穿/雪崩/穿透 Redis 缓存
soft-delete.md 软删除、唯一约束兼容、归档清理 软删除
id-generation.md Snowflake/Leaf-Segment/UUID、时钟回拨 分布式 ID
安全使用建议
This skill appears to be what it claims: a local GORM assist toolset and a Go dbcore library. Before installing/using: 1) Inspect the Python scripts locally (they claim not to call external APIs), and prefer --dry-run or --format json in CI to avoid accidental writes. 2) When using init_project.py, bench_template.py, or any --output/--force flags, run in a sandbox or non-production checkout to avoid overwriting files. 3) The Go assets contain code that can auto-migrate DB tables (LeafSegmentGenerator auto-creates leaf_alloc, NewOrderModel may AutoMigrate when isMigrate=true). Do not run those parts against a production database without backups and review. 4) If you will run analyze_gorm.py in CI, validate its rule set and consider running it on a sample repo first. Overall the skill is coherent and doesn't request unrelated secrets — treat DB-migration and file-write operations with the usual operational caution.
功能分析
Type: OpenClaw Skill Name: gorm-expert-skill Version: 1.6.0 The gorm-expert-skill bundle is a comprehensive toolkit for Go developers using GORM v2, focusing on best practices, performance optimization, and security. It contains well-structured Go source code for database abstractions (BaseModel, QueryBuilder) and several Python utility scripts for static analysis (analyze_gorm.py), SQL-to-struct generation (gen_model.py), and performance advising. The code demonstrates strong security awareness, such as implementing input validation in query_builder.go (safeField) to prevent SQL injection and providing multi-tenancy isolation logic. No evidence of data exfiltration, malicious execution, or harmful prompt injection was found; all scripts operate locally on user-provided data or specified output directories.
能力评估
Purpose & Capability
Name/description (GORM best practices, performance, multi-tenant, scaffolding, SQL→struct, etc.) match the included artifacts: Go assets (dbcore), Python helper scripts (analyze, gen_model, pool_advisor, etc.), and extensive references. There are no unexpected environment variables, binaries, or remote services declared that would be inconsistent with the stated purpose.
Instruction Scope
SKILL.md instructs the agent to prefer running local Python scripts on user-provided code/SQL and to return script outputs plus commentary. Scripts are described as Python 3.8+ only and claim not to call external APIs or require credentials. This scope is appropriate, but note: several scripts (init_project.py, bench_template.py) can write files when invoked with explicit flags (--output, --force). The SKILL.md does not instruct reading system secrets or unrelated files. Review scripts before running and prefer --dry-run when available.
Install Mechanism
There is no install spec and no network download/install in the manifest — the skill is instruction + local source files. All code is bundled in the skill (Go sources and Python scripts). No external URLs, package installations, or archive extraction steps are declared.
Credentials
The skill declares no required environment variables or credentials. Comments in the Go code mention optional patterns (e.g., reading SNOWFLAKE_NODE_ID from env) which are normal implementation notes but not required by the skill. Nothing in requires.env or primary credential is requested that would be disproportionate to a DB tooling / scaffolding skill.
Persistence & Privilege
The skill is not always-on and does not request elevated platform privileges. However, functionality that writes to disk exists and is gated behind explicit flags (init_project.py --output / --force). Also, some Go library code (e.g., NewLeafSegmentGenerator) will AutoMigrate a leaf_alloc table when invoked in a running application — that is expected for a DB scaffolding/ID generator library but has real side effects if run against a production DB. Treat these operations as potentially destructive and run them intentionally and in safe environments.
如何使用
  1. 确保已安装 OpenClaw(本地或 Docker 部署)
  2. 在对话框中输入安装命令:/install gorm-expert-skill
  3. 安装完成后,直接呼叫该 Skill 的名称或使用 /gorm-expert-skill 触发
  4. 根据 Skill 的参数说明提供必要输入,即可获得结构化输出
版本历史
v1.6.0
gorm-expert-skill v1.6.0 - 提供了丰富的最佳实践和性能优化指南,涵盖代码审查、慢查询、N+1 问题、连接池、事务管理、多租户隔离等常见场景 - 内置各类脚本工具,支持 Python 一键辅助结构体生成、连接池计算、SQL 性能分析、批量数据处理等 - 明确流程与配置:禁用物理外键、连接池推荐值、多租户启用与关闭方式、ID 生成策略 - 丰富代码片段与脚本推荐,覆盖查询优化、批量写入、事务、读写分离及 Scope 复用等细节 - 新增多租户两种隔离方式,脚手架初始化及常用场景自动脚本调用说明
元数据
Slug gorm-expert-skill
版本 1.6.0
许可证 MIT-0
累计安装 0
当前安装数 0
历史版本数 1
常见问题

Gorm Expert 是什么?

GORM v2 最佳实践与性能优化。适用于:代码审查、慢查询优化、N+1、连接池、 事务管理、分库分表、Prometheus/OTel监控、Session安全、Clause/Upsert、 缓存集成、BaseModel脚手架、SQL→struct生成、多租户隔离。 触发词:GORM、数据库慢、加索引、写struc... 它是一个面向 Claude Code / OpenClaw 的 AI Agent Skill 插件,目前累计下载 200 次。

如何安装 Gorm Expert?

在 OpenClaw 或 Claude Code 对话框中运行命令「/install gorm-expert-skill」即可一键安装,无需额外配置。

Gorm Expert 是免费的吗?

是的,Gorm Expert 完全免费,采用 MIT-0 许可证,可自由下载、安装和使用。

Gorm Expert 支持哪些平台?

Gorm Expert 跨平台运行,可在任意部署了 OpenClaw / Claude Code 的环境中使用(cross-platform)。

谁开发了 Gorm Expert?

由 xuebing(@lynnss-ai)开发并维护,当前版本 v1.6.0。

💬 留言讨论