答案
import (
"database/sql"
"fmt"
)
// ... 数据库连接配置 ...
func DeductStock(productId int, quantity int) (bool, error) {
// 开启数据库事务
tx, err := db.Begin()
if err != nil {
return false, fmt.Errorf("开始事务失败: %v", err)
}
defer tx.Rollback() // 保证事务最终被回滚
// 查询商品信息
var currentStock int
err = tx.QueryRow("SELECT stock FROM products WHERE id = ? FOR UPDATE", productId).Scan(¤tStock)
if err != nil {
return false, fmt.Errorf("查询商品信息失败: %v", err)
}
// 检查库存是否足够
if currentStock < quantity {
return false, fmt.Errorf("库存不足")
}
// 扣减库存
result, err := tx.Exec("UPDATE products SET stock = stock - ? WHERE id = ? AND stock >= ?", quantity, productId, quantity)
if err != nil {
return false, fmt.Errorf("更新库存失败: %v", err)
}
// 检查更新的行数
rowsAffected, err := result.RowsAffected()
if err != nil {
return false, fmt.Errorf("检查更新行数失败: %v", err)
}
if rowsAffected == 0 {
// 乐观锁:库存可能已被其他请求修改,扣减失败
return false, fmt.Errorf("库存已发生变化,请重试")
}
// 提交事务
err = tx.Commit()
if err != nil {
return false, fmt.Errorf("提交事务失败: %v", err)
}
return true, nil
}
代码逻辑解释:
数据库事务: 使用事务保证数据一致性。 乐观锁:
使用 FOR UPDATE
语句在查询商品信息时锁定该行数据,防止其他并发请求修改。在 UPDATE
语句中添加stock >= ?
条件,确保只有当库存足够时才进行扣减。检查更新的行数,如果为 0,说明库存已被其他请求修改,扣减失败。
考点:
Golang 数据库操作 MySQL 事务处理 乐观锁机制 并发控制