列锁在电商服务中的示例

举报
码乐 发表于 2025/01/28 09:35:56 2025/01/28
【摘要】 1 简介列锁的用途与解锁,列锁(Column-level lock) 是指在数据库中只对特定列施加锁,而不是对整行或整表进行加锁。列锁的主要用途是:避免过多的锁竞争:相较于行锁和表锁,列锁仅限制对特定列的访问,从而减少对其他列的阻塞。适用于部分数据更新:当仅对表中的某个列进行操作时,使用列锁可以保证操作的粒度更细,减少其他列的锁竞争。提高并发性能:因为列锁的作用范围较小,它通常能够提供比行...

1 简介

列锁的用途与解锁,列锁(Column-level lock) 是指在数据库中只对特定列施加锁,而不是对整行或整表进行加锁。

列锁的主要用途是:

避免过多的锁竞争:相较于行锁和表锁,列锁仅限制对特定列的访问,从而减少对其他列的阻塞。
适用于部分数据更新:当仅对表中的某个列进行操作时,使用列锁可以保证操作的粒度更细,减少其他列的锁竞争。
提高并发性能:因为列锁的作用范围较小,它通常能够提供比行锁和表锁更高的并发性能。

但是,列锁并不常见,因为大多数数据库系统更倾向于使用行锁(特别是通过 SELECT FOR UPDATE 来实现)。列锁通常由数据库引擎自动管理,开发人员较少直接使用。

2 操作实例

在下面的例子中,我们会模拟一个电商平台的订单操作,其中使用列锁对 products 表的 stock 列进行加锁,确保在库存更新时不会有其他事务修改库存数据。

数据库表结构,假设有以下两个表:

orders:存储订单信息。
products:存储商品信息。

    CREATE TABLE orders (
        order_id INT AUTO_INCREMENT PRIMARY KEY,
        user_id INT NOT NULL,
        product_id INT NOT NULL,
        quantity INT NOT NULL,
        status VARCHAR(255) NOT NULL,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    );

    CREATE TABLE products (
        product_id INT AUTO_INCREMENT PRIMARY KEY,
        product_name VARCHAR(255) NOT NULL,
        stock INT NOT NULL
    );

使用 Gin 实现列锁操作

    1. 安装依赖

首先确保你的项目包含了以下依赖:

    go get github.com/gin-gonic/gin
    go get github.com/jinzhu/gorm
    go get github.com/go-sql-driver/mysql
    1. 初始化数据库连接

      var db *gorm.DB

      // 初始化数据库连接
      func initDB() {
      var err error
      dsn := “root:password@tcp(localhost:3306)/ecommerce?charset=utf8mb4&parseTime=True&loc=Local”
      db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
      if err != nil {
      log.Fatal(“Failed to connect to the database:”, err)
      }
      fmt.Println(“Connected to the database successfully.”)
      }

    1. 订单操作代码(使用列锁)

在这个实现中,我们通过列锁来确保在库存更新时,其他事务无法修改 products 表的 stock 列。

    type Order struct {
        OrderID   int    `json:"order_id"`
        UserID    int    `json:"user_id"`
        ProductID int    `json:"product_id"`
        Quantity  int    `json:"quantity"`
        Status    string `json:"status"`
    }

    type Product struct {
        ProductID   int    `json:"product_id"`
        ProductName string `json:"product_name"`
        Stock       int    `json:"stock"`
    }

下单操作,使用列锁来保证库存更新时的安全性

  func placeOrder(c *gin.Context) {
      var order Order
      if err := c.ShouldBindJSON(&order); err != nil {
          c.JSON(400, gin.H{"error": err.Error()})
          return
      }

      // 开始一个事务
      tx := db.Begin()

      // 使用列锁来锁住 `products` 表的 `stock` 列,防止其他事务修改库存
      if err := tx.Exec("SELECT stock FROM products WHERE product_id = ? FOR UPDATE", order.ProductID).Error; err != nil {
          tx.Rollback()
          c.JSON(500, gin.H{"error": "Failed to lock product stock column"})
          return
      }

      // 查询商品库存是否足够
      var product Product
      if err := tx.First(&product, "product_id = ?", order.ProductID).Error; err != nil {
          tx.Rollback()
          c.JSON(400, gin.H{"error": "Product not found"})
          return
      }

      if product.Stock < order.Quantity {
          tx.Rollback()
          c.JSON(400, gin.H{"error": "Insufficient stock"})
          return
      }

      // 创建订单
      order.Status = "Pending"
      if err := tx.Create(&order).Error; err != nil {
          tx.Rollback()
          c.JSON(500, gin.H{"error": "Failed to place order"})
          return
      }

      // 更新商品库存
      product.Stock -= order.Quantity
      if err := tx.Save(&product).Error; err != nil {
          tx.Rollback()
          c.JSON(500, gin.H{"error": "Failed to update product stock"})
          return
      }

      // 提交事务
      tx.Commit()

      c.JSON(200, gin.H{"message": "Order placed successfully"})
  }
    1. 启动服务

      func main() {
      initDB()

       r := gin.Default()
      
       // 定义下单接口
       r.POST("/place_order", placeOrder)
      
       // 启动 Gin 服务器
       r.Run(":8080")
      

      }

3 代码解读

列锁的使用:

在 placeOrder 函数中,我们通过 FOR UPDATE 子句来对 products 表的 stock 列进行加锁。这个操作会确保当前事务在执行期间,其他事务不能对该列进行修改。

具体实现是如下方式来实现列锁。

tx.Exec("SELECT stock FROM products WHERE product_id = ? FOR UPDATE", order.ProductID) 

库存检查和更新:

查询商品库存是否足够。如果库存不足,则回滚事务并返回错误。如果库存足够,则创建订单并更新库存。

提交事务:

如果所有操作成功,事务提交,将订单和更新的库存数据保存到数据库中。

列锁的用途与解锁

列锁的用途:

列锁主要用于保证对特定列的操作不受其他事务的干扰。在本例中,列锁确保了在订单下单时,其他事务无法修改该商品的库存,避免了并发更新库存时的冲突。

列锁的解锁:

在数据库中,列锁通常是由数据库引擎自动管理的。当事务提交或回滚时,数据库会自动解锁。我们无需手动解锁列,但事务的提交和回滚会触发数据库自动解锁操作。

4 小结

列锁通常用于细粒度的锁定操作,只锁定需要修改的列,避免阻塞其他列的操作。
在本例中,使用了列锁来保证在下单时,商品库存列的安全性,避免其他事务修改库存导致的冲突。

列锁的解锁是由数据库自动完成的,通过事务的提交或回滚来完成。
列锁在实际应用中较为少见,但在某些特定场景下,它能提供更高的并发性能。
相比于表锁和行锁,列锁的作用范围更小,能够有效减少锁竞争。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。