您的当前位置:首页 >应用开发 >Golang 数据库事务实践 正文
时间:2025-11-05 02:52:33 来源:网络整理编辑:应用开发
Go 是一种年轻而强大的语言,专为编写小型、简单的服务而创建。但随着时间推移,越来越多复杂应用和系统也在采用 Go 进行开发,这就出现了一些问题:如何处理事务?为了深入探讨这个问题,我们假设一个简单的

Go 是数据一种年轻而强大的语言,专为编写小型、库事简单的数据服务而创建。但随着时间推移,库事越来越多复杂应用和系统也在采用 Go 进行开发,数据这就出现了一些问题:如何处理事务?库事
为了深入探讨这个问题,我们假设一个简单的数据业务场景:用户注册。
作为一个系统,库事我希望在注册时创建用户和个人资料。数据
RDBMS/DBMS 的库事现代 Go 库不像 C# 和 Java 的 Hibernate、Entity Framework 那样强大,数据因此我们必须自己处理。库事为了实现用户注册业务场景,数据我们将创建并评估几种处理事务的站群服务器库事方法。
由于每种事务处理方法都必须与 sql.DB 和 sql.Tx 配合使用,数据因此需要引入接口来封装对数据库的访问。
生成的应用有两个域实体和一个用于访问数据库的 DB 低级接口。
复制package model type User struct { Email string } type Profile struct { Name string }1.2.3.4.5.6.7.8.9. 复制package transaction type DB interface { QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error) ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) }1.2.3.4.5.6.7.8.准备工作完成后,就可以采用如下两种方法。
1. 事务感知上下文工作原理:transaction.Manager启动事务并将其放入上下文。当存储库执行查询时,助手会检查上下文中是否有事务,并使用创建的事务来执行查询,或者如果上下文为空,则不使用事务来执行查询。
为了启动事务,我们需要实体:Manager
复制package transaction type Manager interface { Run( ctx context.Context, callback func(ctx context.Context) error, ) error }1.2.3.4.5.6.7.8.transaction.Manager 实现:
复制package transaction import ( "context" "database/sql" "github.com/pkg/errors" "go.uber.org/multierr" ) type txKey string var ctxWithTx = txKey("tx") type SQLTransactionManager struct { db *sql.DB } func NewManager(db *sql.DB) *SQLTransactionManager { return &SQLTransactionManager{db: db} } func (m *SQLTransactionManager) Run( ctx context.Context, callback func(ctx context.Context) error, ) (rErr error) { tx, err := m.db.BeginTx(ctx, &sql.TxOptions{}) if err != nil { return errors.WithStack(err) } defer func() { if rErr != nil { rErr = multierr.Combine(rErr, errors.WithStack(tx.Rollback())) } }() defer func() { if rec := recover(); rec != nil { if e, ok := rec.(error); ok { rErr = e } else { rErr = errors.Errorf("%s", rec) } } }() if err = callback(putTxToContext(ctx, tx)); err != nil { return err } return errors.WithStack(tx.Commit()) } func ExtractTxFromContext(ctx context.Context) (*sql.Tx, bool) { tx := ctx.Value(ctxWithTx) if t, ok := tx.(*sql.Tx); ok { return t, true } return nil, false } func putTxToContext(ctx context.Context, tx *sql.Tx) context.Context { return context.WithValue(ctx, ctxWithTx, tx) }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.62.63.64.65.66.DB实现:
复制package storage import ( "brand/transaction/example1/transaction" "context" "database/sql" ) type DB struct { db *sql.DB } func NewDB(db *sql.DB) *DB { return &DB{db: db} } func (d *DB) QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row { tx, ok := transaction.ExtractTxFromContext(ctx) if !ok { return d.db.QueryRowContext(ctx, query, args...) } return tx.QueryRowContext(ctx, query, args...) } func (d *DB) QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error) { tx, ok := transaction.ExtractTxFromContext(ctx) if !ok { return d.db.QueryContext(ctx, query, args...) } return tx.QueryContext(ctx, query, args...) } func (d *DB) ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error) { tx, ok := transaction.ExtractTxFromContext(ctx) if !ok { return d.db.ExecContext(ctx, query, args...) } return tx.ExecContext(ctx, query, args...) } func (d *DB) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) { tx, ok := transaction.ExtractTxFromContext(ctx) if !ok { return d.db.PrepareContext(ctx, query) } return tx.PrepareContext(ctx, query) }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.RegistrationService 负责用户注册业务场景
复制package service import ( "brand/transaction/example1/model" "brand/transaction/example1/transaction" "context" ) type UserRepository interface { Create(ctx context.Context, user *model.User) error } type ProfileRepository interface { Create(ctx context.Context, user *model.Profile) error } type RegistrationData struct { Email string Name string } type RegistrationService struct { transactionManager transaction.Manager userRepository UserRepository profileRepository ProfileRepository } func NewRegistrationService( transactionManager transaction.Manager, userRepository UserRepository, profileRepository ProfileRepository, ) *RegistrationService { return &RegistrationService{ transactionManager: transactionManager, userRepository: userRepository, profileRepository: profileRepository, } } func (s *RegistrationService) Register(ctx context.Context, data RegistrationData) error { return s.transactionManager.Run(ctx, func(ctx context.Context) error { if err := s.userRepository.Create(ctx, &model.User{ Email: data.Email, }); err != nil { return err } if err := s.profileRepository.Create(ctx, &model.Profile{ Name: data.Name, }); err != nil { return err } return nil }) }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.User和ProfileRepository的亿华云实现:
复制package storage import ( "brand/transaction" "brand/transaction/example1/model" "context" ) type ProfileRepository struct { db transaction.DB } func NewProfileRepository(db transaction.DB) *ProfileRepository { return &ProfileRepository{db: db} } func (r *ProfileRepository) Create(ctx context.Context, profile *model.Profile) error { _, err := r.db.ExecContext(ctx, "INSERT ...", profile.Name) return err }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21. 复制package storage import ( "brand/transaction" "brand/transaction/example1/model" "context" ) type UserRepository struct { db transaction.DB } func NewUserRepository(db transaction.DB) *UserRepository { return &UserRepository{db: db} } func (r *UserRepository) Create(ctx context.Context, user *model.User) error { _, err := r.db.ExecContext(ctx, "INSERT ...", user.Email) return err }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.优点:
简单:存储库会自动使用由 TransactionManager 启动的事务与存储无关:客户端代码对存储类型一无所知缺点
不符合Go的使用习惯控制较少:无法防止在事务中启动事务,可能会产生意想不到的副作用,代码审查时必须考虑到这一点2. 事务感知存储库工作原理:事务管理器启动事务并将事务放入回调,存储库工厂方法使用事务创建自己。
为了启动事务,我们需要实体:Manager
复制type Manager interface { Run( ctx context.Context, callback func(ctx context.Context, tx *sql.Tx) error, ) error }1.2.3.4.5.6.transaction.Manager 实现:
复制package transaction import ( "context" "database/sql" "github.com/pkg/errors" "go.uber.org/multierr" ) type txKey string var ctxWithTx = txKey("tx") type SQLTransactionManager struct { db *sql.DB } func NewManager(db *sql.DB) *SQLTransactionManager { return &SQLTransactionManager{db: db} } func (m *SQLTransactionManager) Run( ctx context.Context, callback func(ctx context.Context, tx *sql.Tx) error, ) (rErr error) { tx, err := m.db.BeginTx(ctx, &sql.TxOptions{}) if err != nil { return errors.WithStack(err) } defer func() { if rErr != nil { rErr = multierr.Combine(rErr, errors.WithStack(tx.Rollback())) } }() defer func() { if rec := recover(); rec != nil { if e, ok := rec.(error); ok { rErr = e } else { rErr = errors.Errorf("%s", rec) } } }() if err = callback(ctx, tx); err != nil { return err } return errors.WithStack(tx.Commit()) }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.DB实现:
复制package storage import ( "context" "database/sql" ) type DB struct { db *sql.DB } func NewDB(db *sql.DB) *DB { return &DB{db: db} } func (d *DB) QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row { return d.db.QueryRowContext(ctx, query, args...) } func (d *DB) QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error) { return d.db.QueryContext(ctx, query, args...) } func (d *DB) ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error) { return d.db.ExecContext(ctx, query, args...) } func (d *DB) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) { return d.db.PrepareContext(ctx, query) }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.RegistrationService 负责用户注册业务场景
有两种方法可以创建带有事务的存储库:
存储库带有结构方法 WithTransaction(示例中使用了该方法)存储库工厂 userRepositoryFactory.CreateFromTransaction(tx) 复制package service import ( "brand/transaction/example2/model" "brand/transaction/example2/transaction" "context" "database/sql" ) type UserRepository interface { Create(ctx context.Context, user *model.User) error WithTransaction(tx *sql.Tx) UserRepository } type ProfileRepository interface { Create(ctx context.Context, user *model.Profile) error WithTransaction(tx *sql.Tx) ProfileRepository } type RegistrationData struct { Email string Name string } type RegistrationService struct { transactionManager transaction.Manager userRepository UserRepository profileRepository ProfileRepository } func NewRegistrationService( transactionManager transaction.Manager, userRepository UserRepository, profileRepository ProfileRepository, ) *RegistrationService { return &RegistrationService{ transactionManager: transactionManager, userRepository: userRepository, profileRepository: profileRepository, } } func (s *RegistrationService) Register(ctx context.Context, data RegistrationData) error { return s.transactionManager.Run(ctx, func(ctx context.Context, tx *sql.Tx) error { userRepository := s.userRepository.WithTransaction(tx) profileRepository := s.profileRepository.WithTransaction(tx) if err := userRepository.Create(ctx, &model.User{ Email: data.Email, }); err != nil { return err } if err := profileRepository.Create(ctx, &model.Profile{ Name: data.Name, }); err != nil { return err } return nil }) }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.62.User和ProfileRepository的实现:
复制package storage import ( "brand/transaction" "brand/transaction/example2/model" "brand/transaction/example2/service" "context" "database/sql" ) type ProfileRepository struct { db transaction.DB } func NewProfileRepository(db transaction.DB) *ProfileRepository { return &ProfileRepository{db: db} } func (r *ProfileRepository) Create(ctx context.Context, profile *model.Profile) error { _, err := r.db.ExecContext(ctx, "INSERT ...", profile.Name) return err } func (r *ProfileRepository) WithTransaction(tx *sql.Tx) service.ProfileRepository { return NewProfileRepository(tx) }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27. 复制package storage import ( "brand/transaction" "brand/transaction/example2/model" "brand/transaction/example2/service" "context" "database/sql" ) type UserRepository struct { db transaction.DB } func NewUserRepository(db transaction.DB) *UserRepository { return &UserRepository{db: db} } func (r *UserRepository) Create(ctx context.Context, user *model.User) error { _, err := r.db.ExecContext(ctx, "INSERT ...", user.Email) return err } func (r *UserRepository) WithTransaction(tx *sql.Tx) service.UserRepository { return NewUserRepository(tx) }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.优点:
更明确:在注册服务内部创建事务,可避免副作用缺点:
客户端代码知道存储类型客户端代码负责创建新的存储库我相信任何一种方法都能使代码更易读、更简单,但建议使用第一种方法,从而可以隐藏存储细节,使我们能够在一个项目中使用多个存储,而无需考虑实现和存储细节。
复制package storage import ( "brand/transaction" "brand/transaction/example2/model" "brand/transaction/example2/service" "context" "database/sql" ) type UserRepository struct { db transaction.DB } func NewUserRepository(db transaction.DB) *UserRepository { return &UserRepository{db: db} } func (r *UserRepository) Create(ctx context.Context, user *model.User) error { _, err := r.db.ExecContext(ctx, "INSERT ...", user.Email) return err } func (r *UserRepository) WithTransaction(tx *sql.Tx) service.UserRepository { return NewUserRepository(tx) }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.云南idc服务商如何解决在苹果电脑上安装JDK路径错误问题(正确设置JDK路径,让开发环境无忧)2025-11-05 02:44
2023年数据中心的六大趋势2025-11-05 02:06
图解网络:什么是 DNS 域名系统?2025-11-05 01:50
NVIDIA持续深耕元宇宙,为业界提供最优的软硬件整体解决方案2025-11-05 01:30
MakerBot3D打印机(发现未来制造业的无限可能性)2025-11-05 01:22
数据中心正在成为不断发展的数字经济的重要资产2025-11-05 01:07
新华三智慧计算持续进化,“一体·两中枢”赋能企业迈入数字化时代2025-11-05 01:01
数据中心冷却的绿色解决方案?2025-11-05 00:47
火影电脑系统重置教程(快速恢复系统正常运行,让电脑如新)2025-11-05 00:16
普洛斯数据中心发布DC Brain系统,科技赋能智慧化运营管理2025-11-05 00:07
用U盘快速装机,轻松解决电脑重装问题(U盘快速装机教程,操作简单高效,让电脑恢复如新)2025-11-05 02:49
戴尔科技PowerScale赋能数据存储和管理 助力企业打破数据壁垒2025-11-05 02:44
我们如何让数据中心为未来做好准备?2025-11-05 02:42
IBM发布全新一代 LinuxONE:3大优势助力企业构建“负责任的算力与绿色IT”,赋能客户现代化转型2025-11-05 02:36
如何进行云电脑设置教程(一步步教你完成云电脑的渲染设置)2025-11-05 02:11
三年十起故障灾难 数据中心如何预防应对?2025-11-05 02:10
新华三携手宁夏移动 拓宽数据西游路2025-11-05 02:02
戴尔科技推出业界首款“远边缘”服务器XR4000,充分展现其在边缘服务器领域的远见卓识2025-11-05 01:43
电脑日志错误8198的原因与解决方法(深入探究电脑日志错误8198的发生情况及解决办法)2025-11-05 01:21
欧盟计划将数据中心送入太空,云计算真的要上天了?2025-11-05 01:16