关于 golang-database
面向 Go 后端工程师和编码 Agent 的数据库开发技能,注重显式 SQL 与安全的数据访问。内容涵盖参数化查询、上下文、事务、连接池、错误处理和迁移,并建议避免 ORM 与 AI 生成的数据库模式。
主要功能
- 01参数化查询与 SQL 注入防护
- 02上下文传递与行资源处理
- 03事务、隔离级别与锁定
- 04连接池配置与批处理
- 05错误处理与可空列扫描
- 06外部迁移工具与模式审查
适用场景
- 01编写或审查 Go 仓库层查询
- 02安全处理多语句数据库更新
- 03配置连接池并批量操作数据库
- 04诊断查询错误和未找到记录的情况
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ samber/ cc-skills-golang --skill golang-database安装后在 Claude Code 中输入/golang-database确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
evals/evals.jsonreferences/performance.mdscanning.mdtesting.mdtransactions.md详细介绍
角色: 你是一名 Go 后端工程师,编写安全、明确且可观测的数据库代码。你将 SQL 视为一等语言——不使用 ORM,不依赖魔法,并在数据完整性问题的边界处发现问题,而不是等问题深入应用后才发现。
模式:
- 编写模式 — 生成新的仓储函数、查询辅助函数或事务封装:遵循技能的顺序指引;在生成新代码前,启动一个后台 Agent,在代码库中搜索现有查询模式和命名约定。
- 审查/调试模式 — 审查或调试现有数据库代码:使用子 Agent 并行扫描是否缺少
rows.Close()、是否存在未参数化查询、是否缺少上下文传递,以及是否缺少错误检查,同时阅读业务逻辑。
社区默认规则。 明确取代
samber/cc-skills-golang@golang-database技能的公司技能优先。
Go 数据库最佳实践
Go 的 database/sql 为数据库访问提供了坚实的基础。可以在其上使用 sqlx 或 pgx 提升易用性——绝不使用 ORM。
使用 sqlx 或 pgx 时,请参考库的官方文档和代码示例,了解当前的 API 签名。
最佳实践摘要
- 使用 sqlx 或 pgx,不使用 ORM —— ORM 会隐藏 SQL、生成不可预测的查询,并让调试更加困难
- 查询必须使用参数化占位符——绝不要将用户输入拼接到 SQL 字符串中
- 所有数据库操作都必须传递上下文——使用
*Context方法变体(QueryContext、ExecContext、GetContext) - 必须显式处理
sql.ErrNoRows——使用errors.Is区分“未找到”和真正的错误 - 遍历完成后必须关闭 rows——调用
QueryContext后立即执行defer rows.Close() - 对于不返回 rows 的语句,绝不要使用
db.Query——Query会返回必须关闭的*Rows;如果忘记关闭,连接就会泄漏回连接池。请改用db.Exec - 多个语句的操作要使用事务 —— 使用
BeginTxx/Commit封装相关写入 - 读取打算修改的数据时使用
SELECT ... FOR UPDATE—— 防止竞态条件 - 默认的 READ COMMITTED 隔离级别不够时,设置自定义隔离级别(例如,金融操作使用可串行化隔离级别)
- 使用指针字段(
*string、*int)或sql.NullXxx类型处理可为 NULL 的列 - 必须配置连接池——
SetMaxOpenConns、SetMaxIdleConns、SetConnMaxLifetime、SetConnMaxIdleTime - 使用外部工具执行迁移 —— 使用 golang-migrate 或 Flyway,绝不手动编写或通过 AI 生成迁移 SQL
- 以合理的批量大小执行批处理操作 —— 不要逐行处理(往返次数过多),也不要一次处理数百万行(锁和内存压力过大)
- 绝不创建或修改数据库架构 —— 在玩具数据上看似正确的架构,在真实生产负载下可能造成热点、锁竞争或缺少索引。架构设计需要了解数据量、访问模式和生产环境约束,而 AI 不具备这些知识
- 避免依赖隐藏的 SQL 功能 —— 不要在应用代码中依赖触发器、视图、物化视图、存储过程或行级安全性
库的选择
| 库 | 最适用于 | 结构体扫描 | PostgreSQL 特有功能 |
|---|---|---|---|
database/sql | 可移植性、最少依赖 | 手动 Scan | 否 |
sqlx | 多数据库项目 | StructScan | 否 |
pgx | PostgreSQL(速度快 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技能