从整体角度理解间隙锁

举报
码乐 发表于 2025/02/17 10:15:14 2025/02/17
【摘要】 1 简介从整体角度理解 MySQL 的间隙锁(Gap Lock),在理解 MySQL 间隙锁(Gap Lock) 及其与 FOR UPDATE 的对比时,可以从整体性、层次性、动态性和关联性这些角度进行分析。 2 整体性(Holism):间隙锁在数据库系统中的作用在 MySQL InnoDB 存储引擎中,锁的作用是确保数据一致性和事务隔离性。间隙锁(Gap Lock) 主要用于 防止幻读,...

1 简介

从整体角度理解 MySQL 的间隙锁(Gap Lock),在理解 MySQL 间隙锁(Gap Lock) 及其与 FOR UPDATE 的对比时,可以从整体性、层次性、动态性和关联性这些角度进行分析。

2 整体性(Holism):间隙锁在数据库系统中的作用

在 MySQL InnoDB 存储引擎中,锁的作用是确保数据一致性和事务隔离性。
间隙锁(Gap Lock) 主要用于 防止幻读,属于 Next-Key Lock(行锁 + 间隙锁) 机制的一部分。

行锁(FOR UPDATE) vs. 间隙锁(Gap Lock)

	 锁类型 			作用  				适用场景    	并发影响
  行锁(FOR UPDATE)  锁定已存在的行    更新、扣库存  	高并发友好(只锁住已查询行)
  间隙锁(Gap Lock)  锁定行之间的间隙   防止新插入     低并发友好度(阻止新插入)
									  范围查询(WHERE col > X

间隙锁的特点:

适用于 REPEATABLE READ 及更严格的隔离级别,锁住查询范围内不存在的行,防止幻读,仅在 SELECT … FOR UPDATE 或 SELECT … LOCK IN SHARE MODE 的范围查询时触发。

3. 层次性(Hierarchy):锁的范围

MySQL InnoDB 存储引擎的锁分为 表级 → 行级 → 间隙级:

表锁(Table Lock):影响整个表,几乎禁止并发(如 LOCK TABLES)。
行锁(Row Lock,FOR UPDATE):基于索引锁定特定行(如 SELECT … FOR UPDATE)。

间隙锁(Gap Lock):

锁定 查询范围内不存在的行,防止其他事务插入新数据
例如 SELECT * FROM users WHERE age > 30 FOR UPDATE,会锁住 所有 age > 30 的行及间隙。

间隙锁影响并发性能,可能导致阻塞。

4 动态性(Dynamism):事务执行中的锁行为

在事务执行过程中,间隙锁的作用是防止新的数据插入,确保可重复读。

示例 1:FOR UPDATE 仅锁定行

    BEGIN;
    SELECT * FROM users WHERE id = 5 FOR UPDATE;

仅锁定 id=5 这一行
不会影响 id=6 的插入
高并发友好

示例 2:间隙锁锁住范围

  BEGIN;
  SELECT * FROM users WHERE age > 30 FOR UPDATE;

锁住所有 age > 30 的行 和 age > 30 的空隙
其他事务无法插入 age=35 的新用户
可能导致阻塞,降低并发能力

5. 关联性(Interrelation):间隙锁的业务应用

间隙锁适用于防止幻读的场景,如:

防止用户在同一事务中插入新的订单,导致重复计算
金融系统防止某个时间范围内新增交易
多表关联查询,确保一致性
但间隙锁可能降低并发,适用场景有限!

使用 GORM 实现间隙锁(Gap Lock)
我们用 GORM 设计一个用户年龄查询与插入控制系统:

使用 FOR UPDATE 只锁定行
使用间隙锁防止新的用户插入

  1. 初始化数据库

       CREATE TABLE users (
           id INT PRIMARY KEY AUTO_INCREMENT,
           name VARCHAR(255) NOT NULL,
           age INT NOT NULL
       );
    
     	INSERT INTO users (name, age) VALUES ('Alice', 28), ('Bob', 35);
    
  2. 服务器 GORM 代码

     // User 模型
     type User struct {
         ID   uint   `gorm:"primaryKey"`
         Name string `gorm:"type:varchar(255)"`
         Age  int
     }
    
     var db *gorm.DB
    
     func main() {
         // 连接数据库
         dsn := "root:password@tcp(127.0.0.1:3306)/testdb?charset=utf8mb4&parseTime=True&loc=Local"
         var err error
         db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
         if err != nil {
             log.Fatal("数据库连接失败:", err)
         }
    
         r := gin.Default()
         r.POST("/lock/:age", lockUsersByAge)  // 查询大于某年龄的用户并加锁
         r.POST("/add_user", addUser) // 新增用户
    
         r.Run(":8080") // 启动服务
     }
    
  3. 使用 GAP LOCK 查询大于某年龄的用户,并防止新插入

    	func lockUsersByAge(c *gin.Context) {
        var users []User
        age := c.Param("age")
    
        tx := db.Begin()
        if err := tx.Error; err != nil {
            c.JSON(http.StatusInternalServerError, gin.H{"error": "无法开启事务"})
            return
        }
    
        // 关键点:使用 FOR UPDATE + 范围查询,会触发间隙锁
        if err := tx.Raw("SELECT * FROM users WHERE age > ? FOR UPDATE", age).Scan(&users).Error; err != nil {
            tx.Rollback()
            c.JSON(http.StatusInternalServerError, gin.H{"error": "查询失败"})
            return
        }
    
        // 模拟长时间事务,观察锁行为
        // time.Sleep(10 * time.Second)
    
        tx.Commit()
    
        c.JSON(http.StatusOK, gin.H{"message": "锁定成功", "users": users})
    }
    
  4. 新增用户,可能因间隙锁失败

    func addUser(c *gin.Context) {
        var user User
        if err := c.ShouldBindJSON(&user); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数错误"})
            return
        }
    
        // 直接插入,可能因间隙锁导致阻塞
        if err := db.Create(&user).Error; err != nil {
            c.JSON(http.StatusInternalServerError, gin.H{"error": "插入失败"})
            return
        }
    
        c.JSON(http.StatusOK, gin.H{"message": "用户新增成功"})
    }
    

6 总结

系统思维维度 FOR UPDATE GAP LOCK
整体性 事务锁定已存在的数据,确保一致性 防止数据插入,确保事务隔离
层次性 只锁定查询到的行 锁住查询范围的“空隙”
动态性 事务提交或回滚释放锁 事务提交前,新数据无法插入
关联性 适用于扣库存、转账、订单处理 适用于防止幻读,如订单查询

使用场景

减少间隙锁的使用,避免影响并发
必要时使用 innodb_locks_unsafe_for_binlog=1 禁用间隙锁
使用 FOR UPDATE 代替 GAP LOCK,提高系统吞吐量
通过这几个方面的比较我们可以更深入理解 MySQL 间隙锁的影响,并在服务开发应用中合理使用锁机制,提升系统稳定性与并发能力.

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

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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