当前位置: 首页 > news >正文

Gorm(十二)乐观锁和悲观锁

在 Gorm 中,乐观锁、悲观锁用于解决并发场景下的数据一致性问题,而 Context 则用于控制数据库操作的超时和取消,三者结合可构建健壮的并发处理逻辑。以下是详细说明:

一、乐观锁(Optimistic Locking)

乐观锁假设并发操作不会频繁冲突,通过版本号(或时间戳)机制实现,仅在提交时检查数据是否被修改,适合冲突较少的场景(如读多写少)。

实现方式
  1. 模型定义:在结构体中添加 gorm:"optimistic_lock" 标签的版本字段(通常为 Version int)。
  2. 原理:更新时会自动检查版本号,若版本号与数据库一致则更新(并自增版本),否则视为数据已被修改,返回错误。
示例
type Product struct {gorm.ModelName    stringStock   intVersion int `gorm:"optimistic_lock"` // 乐观锁版本字段
}// 并发扣减库存
func deductStock(db *gorm.DB, productID, num int) error {var product Product// 1. 查询商品(获取当前版本)if err := db.First(&product, productID).Error; err != nil {return err}// 2. 检查库存if product.Stock < num {return fmt.Errorf("库存不足")}// 3. 更新库存(Gorm 自动添加 WHERE version = ? 条件)product.Stock -= numresult := db.Save(&product)if result.Error != nil {return result.Error // 版本不一致时,返回错误(如:record not found)}if result.RowsAffected == 0 {return fmt.Errorf("并发冲突,库存已被修改")}return nil
}

生成的 SQL
UPDATE products SET stock = ?, version = version + 1, updated_at = ? WHERE id = ? AND version = ?

  • 若版本匹配,更新成功,版本号自增。
  • 若版本不匹配(其他事务已修改),RowsAffected 为 0,需业务层重试或提示用户。
特点
  • 无锁阻塞:不锁定数据,并发性能高。
  • 冲突处理:冲突时需业务层手动重试(如通过循环重试)。
  • 适用场景:读多写少、冲突频率低的场景(如商品详情页库存更新)。

二、悲观锁(Pessimistic Locking)

悲观锁假设并发操作会频繁冲突,通过数据库的 FOR UPDATE 语句锁定记录,阻止其他事务修改,直到当前事务完成,适合冲突频繁的场景(如秒杀、抢票)。

实现方式

通过 Clauses(clause.Locking{Strength: "UPDATE"}) 或直接在 SQL 中添加 FOR UPDATE 实现,查询时锁定记录。

示例
// 悲观锁扣减库存
func deductStockPessimistic(db *gorm.DB, productID, num int) error {// 1. 查询并锁定商品(FOR UPDATE)var product Producttx := db.Clauses(clause.Locking{Strength: "UPDATE"}).First(&product, productID)if tx.Error != nil {return tx.Error}// 2. 检查库存if product.Stock < num {return fmt.Errorf("库存不足")}// 3. 更新库存(此时记录已被锁定,其他事务需等待)product.Stock -= numreturn db.Save(&product).Error
}

生成的 SQL
SELECT * FROM products WHERE id = ? FOR UPDATE;(锁定记录)
UPDATE products SET stock = ?, updated_at = ? WHERE id = ?;

特点
  • 阻塞性:锁定期间其他事务无法修改记录,可能导致等待超时。
  • 强一致性:确保当前事务操作时数据不被干扰。
  • 适用场景:写操作频繁、冲突高的场景(如秒杀系统库存扣减)。

三、Context 超时与取消

Context 用于传递请求的生命周期(如超时、取消信号),Gorm 支持通过 WithContext 方法将 Context 与数据库操作绑定,避免长时间阻塞或资源泄漏。

1. 超时控制

设置操作超时时间,超过时间后自动取消请求,避免数据库连接长期占用。

import ("context""time"
)// 带超时的查询
func queryWithTimeout(db *gorm.DB, userID int) (*User, error) {// 创建 2 秒超时的 Contextctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)defer cancel() // 确保资源释放var user User// 将 Context 传入 Gorm 操作result := db.WithContext(ctx).First(&user, userID)if result.Error != nil {return nil, result.Error // 超时会返回 context.DeadlineExceeded 错误}return &user, nil
}
2. 取消操作

通过 context.WithCancel 手动取消操作(如用户主动取消请求)。

// 带取消功能的更新
func updateWithCancel(db *gorm.DB, userID int, newName string) error {ctx, cancel := context.WithCancel(context.Background())// 模拟异步取消(如用户点击取消按钮)go func() {time.Sleep(1 * time.Second)cancel() // 1秒后取消操作}()result := db.WithContext(ctx).Model(&User{}).Where("id = ?", userID).Update("name", newName)return result.Error // 取消会返回 context.Canceled 错误
}
特点
  • 资源控制:防止慢查询或网络问题导致的连接泄漏。
  • 兼容性:需数据库驱动支持 Context(如 gorm.io/driver/mysql 完全支持)。
  • 事务集成:事务中也可使用 WithContext,超时或取消会导致事务回滚。

四、对比与最佳实践

机制核心逻辑并发性能适用场景注意事项
乐观锁版本号检查,无锁读多写少,冲突少(如商品详情)需处理冲突重试
悲观锁FOR UPDATE 锁定记录写多读少,冲突多(如秒杀)控制事务时长,避免死锁
Context超时/取消信号传递无影响所有场景,尤其长耗时操作及时调用 cancel 释放资源
组合使用示例

秒杀场景(高冲突+超时控制):

func seckill(db *gorm.DB, productID, userID, num int) error {// 1. 超时控制(防止阻塞过久)ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)defer cancel()// 2. 悲观锁事务(确保库存操作原子性)return db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {// 锁定商品记录var product Productif err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&product, productID).Error; err != nil {return err}// 扣减库存if product.Stock < num {return fmt.Errorf("库存不足")}product.Stock -= numreturn tx.Save(&product).Error})
}

总结

  • 乐观锁:通过版本号实现无锁并发,适合低冲突场景,需手动处理冲突。
  • 悲观锁:通过 FOR UPDATE 锁定记录,适合高冲突场景,需控制事务时长。
  • Context:控制操作超时和取消,避免资源泄漏,是所有场景的基础保障。

根据业务的冲突频率和性能需求选择锁机制,并结合 Context 确保系统的健壮性。

http://www.dtcms.com/a/532484.html

相关文章:

  • neo4j图数据库笔记
  • 网页网站设计公司有哪些网站排名有什么用
  • 泉州做网站优化哪家好微信推广平台哪里找
  • 如何制作收费网站百度收录个人网站是什么怎么做
  • VsCode + Wsl:终极开发环境搭建指南
  • 深度学习——Logistic回归中的梯度下降法
  • 中国住房和城乡建设网网站学习网站大全
  • 【Android】ViewPager2实现手/自动轮播图
  • 产品营销网站可以做英语翻译兼职的网站
  • jQuery Mobile 图标:全面解析与应用指南
  • Java(File)
  • AI 翻译入门指南:机器如何理解语言
  • 怎样上传网站程序网站数据库怎么配置
  • MySQL相关知识查询表中的内容(第三次作业)
  • h5游戏免费下载:过马路小游戏
  • 昆山建设局网站深圳企业有限公司
  • LangGraph 官方教程:聊天机器人之三
  • Anaconda的常用指令
  • 广州白云区网站开发做网站的公司叫什么名字好
  • 大兴安岭网站建设兼职有哪些网站可以做ppt
  • dfs:选数
  • 2.2.1.3 大数据方法论与实践指南-文档管理规范
  • 【智能制造工厂工业资料集】流程制造智能工厂总体架构及建设路线规划方案(PPT)
  • MFC简单入门学习
  • HD 钱包- MetaMask
  • 米拓做的网站如何改代码互联网站备案登记表
  • 【Go】--抛出和处理异常
  • Word转PDF工具,免费生成图片型文档
  • [sam2图像分割] MemoryAttentionLayer._forward_ca | 交叉注意力
  • 孝感网站建设公司电子商务平台内经营者享有公平交易的权利