Golang SQLC事务处理最佳实践
在使用Golang的sqlc进行数据库操作时,如何正确实现事务处理?特别是在复杂业务场景下,有哪些需要注意的最佳实践?比如错误处理、事务隔离级别选择、性能优化等方面,希望能分享一些实际项目中的经验总结。
2 回复
在Golang中使用SQLC处理事务时,以下是最佳实践:
1. 使用事务上下文
func TransferMoney(ctx context.Context, db *sql.DB, from, to string, amount int64) error {
tx, err := db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer tx.Rollback()
queries := db.New(tx)
// 执行事务操作
err = queries.DeductBalance(ctx, from, amount)
if err != nil {
return err
}
err = queries.AddBalance(ctx, to, amount)
if err != nil {
return err
}
return tx.Commit()
}
2. 使用事务封装
type Store struct {
*sql.Queries
db *sql.DB
}
func (store *Store) execTx(ctx context.Context, fn func(*sql.Queries) error) error {
tx, err := store.db.BeginTx(ctx, nil)
if err != nil {
return err
}
q := store.New(tx)
err = fn(q)
if err != nil {
if rbErr := tx.Rollback(); rbErr != nil {
return fmt.Errorf("tx err: %v, rb err: %v", err, rbErr)
}
return err
}
return tx.Commit()
}
3. 设置事务隔离级别
tx, err := db.BeginTx(ctx, &sql.TxOptions{
Isolation: sql.LevelSerializable,
ReadOnly: false,
})
4. 错误处理和回滚
func (store *Store) TransferTx(ctx context.Context, arg TransferTxParams) error {
return store.execTx(ctx, func(q *sql.Queries) error {
// 业务逻辑
if err := q.UpdateAccount(ctx, arg.FromAccountID, -arg.Amount); err != nil {
return err
}
if err := q.UpdateAccount(ctx, arg.ToAccountID, arg.Amount); err != nil {
return err
}
return nil
})
}
5. 超时控制
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
err := store.TransferTx(ctx, params)
关键要点:
- 始终使用defer tx.Rollback() - 确保事务在出错时回滚
- 合理设置隔离级别 - 根据业务需求选择
- 使用上下文 - 支持取消和超时
- 保持事务简短 - 减少锁竞争
- 错误处理要完整 - 包括回滚错误
这些实践能确保事务的原子性、一致性和可靠性。


