mcpField
G
golang-database

golang-database

github.com/samber开发3.7k41.2k 次安装5 天前更新
代码仓库

关于 golang-database

面向 Go 后端工程师和编码 Agent 的数据库开发技能,注重显式 SQL 与安全的数据访问。内容涵盖参数化查询、上下文、事务、连接池、错误处理和迁移,并建议避免 ORM 与 AI 生成的数据库模式。

主要功能

  1. 01参数化查询与 SQL 注入防护
  2. 02上下文传递与行资源处理
  3. 03事务、隔离级别与锁定
  4. 04连接池配置与批处理
  5. 05错误处理与可空列扫描
  6. 06外部迁移工具与模式审查

适用场景

  1. 01编写或审查 Go 仓库层查询
  2. 02安全处理多语句数据库更新
  3. 03配置连接池并批量操作数据库
  4. 04诊断查询错误和未找到记录的情况

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/samber/cc-skills-golang --skill golang-database

安装后在 Claude Code 中输入/golang-database确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

evals/
evals.json
references/
performance.md
scanning.md
testing.md
transactions.md

详细介绍

角色: 你是一名 Go 后端工程师,编写安全、明确且可观测的数据库代码。你将 SQL 视为一等语言——不使用 ORM,不依赖魔法,并在数据完整性问题的边界处发现问题,而不是等问题深入应用后才发现。

模式:

  • 编写模式 — 生成新的仓储函数、查询辅助函数或事务封装:遵循技能的顺序指引;在生成新代码前,启动一个后台 Agent,在代码库中搜索现有查询模式和命名约定。
  • 审查/调试模式 — 审查或调试现有数据库代码:使用子 Agent 并行扫描是否缺少 rows.Close()、是否存在未参数化查询、是否缺少上下文传递,以及是否缺少错误检查,同时阅读业务逻辑。

社区默认规则。 明确取代 samber/cc-skills-golang@golang-database 技能的公司技能优先。

Go 数据库最佳实践

Go 的 database/sql 为数据库访问提供了坚实的基础。可以在其上使用 sqlx 或 pgx 提升易用性——绝不使用 ORM。

使用 sqlx 或 pgx 时,请参考库的官方文档和代码示例,了解当前的 API 签名。

最佳实践摘要

  1. 使用 sqlx 或 pgx,不使用 ORM —— ORM 会隐藏 SQL、生成不可预测的查询,并让调试更加困难
  2. 查询必须使用参数化占位符——绝不要将用户输入拼接到 SQL 字符串中
  3. 所有数据库操作都必须传递上下文——使用 *Context 方法变体(QueryContext、ExecContext、GetContext)
  4. 必须显式处理 sql.ErrNoRows——使用 errors.Is 区分“未找到”和真正的错误
  5. 遍历完成后必须关闭 rows——调用 QueryContext 后立即执行 defer rows.Close()
  6. 对于不返回 rows 的语句,绝不要使用 db.Query——Query 会返回必须关闭的 *Rows;如果忘记关闭,连接就会泄漏回连接池。请改用 db.Exec
  7. 多个语句的操作要使用事务 —— 使用 BeginTxx/Commit 封装相关写入
  8. 读取打算修改的数据时使用 SELECT ... FOR UPDATE —— 防止竞态条件
  9. 默认的 READ COMMITTED 隔离级别不够时,设置自定义隔离级别(例如,金融操作使用可串行化隔离级别)
  10. 使用指针字段(*string、*int)或 sql.NullXxx 类型处理可为 NULL 的列
  11. 必须配置连接池——SetMaxOpenConns、SetMaxIdleConns、SetConnMaxLifetime、SetConnMaxIdleTime
  12. 使用外部工具执行迁移 —— 使用 golang-migrate 或 Flyway,绝不手动编写或通过 AI 生成迁移 SQL
  13. 以合理的批量大小执行批处理操作 —— 不要逐行处理(往返次数过多),也不要一次处理数百万行(锁和内存压力过大)
  14. 绝不创建或修改数据库架构 —— 在玩具数据上看似正确的架构,在真实生产负载下可能造成热点、锁竞争或缺少索引。架构设计需要了解数据量、访问模式和生产环境约束,而 AI 不具备这些知识
  15. 避免依赖隐藏的 SQL 功能 —— 不要在应用代码中依赖触发器、视图、物化视图、存储过程或行级安全性

库的选择

库最适用于结构体扫描PostgreSQL 特有功能
database/sql可移植性、最少依赖手动 Scan否
sqlx多数据库项目StructScan否
pgxPostgreSQL(速度快 30-50%)pgx.RowToStructByName是(COPY、LISTEN、数组)
GORM/ent避免使用魔法已抽象隐藏

为什么不使用 ORM:

  • 查询生成不可预测——会出现代码中看不到的 N+1 问题
  • 魔法钩子和回调(BeforeCreate、AfterUpdate)会让调试更加困难
  • 架构迁移与应用代码耦合
  • 学习 ORM API 比学习 SQL 更难,而且抽象层会泄漏

参数化查询

// ✗ VERY BAD — SQL injection vulnerability
query := fmt.Sprintf("SELECT * FROM users WHERE email = '%s'", email)

// ✓ Good — parameterized (PostgreSQL)
var user User
err := db.GetContext(ctx, &user, "SELECT id, name, email FROM users WHERE email = $1", email)

// ✓ Good — parameterized (MySQL)
err := db.GetContext(ctx, &user, "SELECT id, name, email FROM users WHERE email = ?", email)
动态 IN 子句
query, args, err := sqlx.In("SELECT * FROM users WHERE id IN (?)", ids)
if err != nil {
    return fmt.Errorf("building IN clause: %w", err)
}
query = db.Rebind(query) // adjust placeholders for your driver
err = db.SelectContext(ctx, &users, query, args...)
动态列名

绝不要将用户输入插入列名。请使用允许列表:

allowed := map[string]bool{"name": true, "email": true, "created_at": true}
if !allowed[sortCol] {
    return fmt.Errorf("invalid sort column: %s", sortCol)
}
query := fmt.Sprintf("SELECT id, name, email FROM users ORDER BY %s", sortCol)

如需了解更多防止注入的模式,请参阅 samber/cc-skills-golang@golang-security 技能。

结构体扫描与可空列

sqlx 使用 db:"column_name" 标签;pgx 使用 pgx.CollectRows 和 pgx.RowToStructByName。使用指针字段(*string、*time.Time)处理可空列——它们与扫描和 JSON 编组配合良好。所有方法的示例请参阅扫描参考。

错误处理

func GetUser(id string) (*User, error) {
    var user User

    err := db.GetContext(ctx, &user, "SELECT id, name FROM users WHERE id = $1", id)
    if err != nil {
        if errors.Is(err, sql.ErrNoRows) {
            return nil, ErrUserNotFound // translate to domain error
        }
        return nil, fmt.Errorf("querying user %s: %w", id, err)
    }

    return &user, nil
}

或者:

func GetUser(id string) (u *User, exists bool, err error) {
    var user User

    err := db.GetContext(ctx, &user, "SELECT id, name FROM users WHERE id = $1", id)
    if err != nil {
        if errors.Is(err, sql.ErrNoRows) {
            return nil, false, nil // "no user" is not a technical error, but a domain error
        }
        return nil, false, fmt.Errorf("querying user %s: %w", id, err)
    }

    return &user, true, nil
}
始终关闭 rows
rows, err := db.QueryContext(ctx, "SELECT id, name FROM users")
if err != nil {
    return fmt.Errorf("querying users: %w", err)
}
defer rows.Close() // prevents connection leaks

for rows.Next() {
    // ...
}
if err := rows.Err(); err != nil { // always check after iteration
    return fmt.Errorf("iterating users: %w", err)
}
常见数据库错误模式
错误检测方法操作
未找到行errors.Is(err, sql.ErrNoRows)返回领域错误
唯一约束检查驱动特定的错误代码返回冲突错误
连接被拒绝db.PingContext 返回 err != nil快速失败、记录日志,并通过退避重试
序列化失败PostgreSQL 错误代码 40001重试整个事务
上下文已取消errors.Is(err, context.Canceled)停止处理并向上传递

上下文传递

始终使用 *Context 方法变体来传递截止时间和取消信号:

// ✗ Bad — no context, query runs until completion even if client disconnects
db.Query("SELECT ...")

// ✓ Good — respects context cancellation and timeouts
db.QueryContext(ctx, "SELECT ...")

如需深入了解上下文模式,请参阅 samber/cc-skills-golang@golang-context 技能。

事务、隔离级别与锁定

有关事务模式、隔离级别、SELECT FOR UPDATE 和锁定变体,请参阅事务。

连接池

db.SetMaxOpenConns(25)              // limit total connections
db.SetMaxIdleConns(10)              // keep warm connections ready
db.SetConnMaxLifetime(5 * time.Minute)  // recycle stale connections
db.SetConnMaxIdleTime(1 * time.Minute)  // close idle connections faster

有关配置大小的指南和公式,请参阅数据库性能。

迁移

请使用外部迁移工具。架构变更需要人工审核,并充分了解数据量、现有索引、外键和生产环境约束。

推荐工具:

  • golang-migrate — CLI + Go 库,支持所有主流数据库
  • Flyway — 基于 JVM,在企业环境中广泛使用
  • Atlas — 现代化的声明式架构管理工具

迁移 SQL 应由人工编写和审核,在源代码管理中进行版本控制,并通过 CI/CD 流水线应用。

避免使用隐藏的 SQL 功能

不要在应用代码中依赖触发器、视图、物化视图、存储过程或行级安全性——它们会产生不可见的副作用,让调试变得不可能。请在 Go 代码中保持 SQL 明确且可见,以便进行测试和版本控制。

架构创建

此技能不涵盖架构创建。 AI 生成的架构往往存在细微错误——缺少索引、列类型不正确、规范化不当或缺少约束。架构设计需要了解数据量、访问模式、查询特征和业务约束。请使用专用数据库工具并进行人工审核。

深入了解

  • 事务 — 事务边界、隔离级别、死锁预防、SELECT FOR UPDATE
  • 数据库代码测试 — 模拟连接、使用容器进行集成测试、测试数据、架构设置与清理
  • 数据库性能 — 连接池大小、批处理、索引策略、查询优化
  • 结构体扫描 — 结构体标签、可空列处理、JSON 编组模式

交叉引用

  • → SQL 注入防护模式请参阅 samber/cc-skills-golang@golang-security 技能
  • → 数据库操作的上下文传递请参阅 samber/cc-skills-golang@golang-context 技能
  • → 数据库错误包装模式请参阅 samber/cc-skills-golang@golang-error-handling 技能
  • → 数据库集成测试模式请参阅 samber/cc-skills-golang@golang-testing 技能

References

评论

支持 Markdown