在服务开发中使用NEXT-KEY锁

举报
码乐 发表于 2025/02/08 09:41:28 2025/02/08
【摘要】 1 简介本文解释Next-Key 锁 是结合了行锁和间隙锁的锁定机制。在 InnoDB 存储引擎中,Next-Key 锁 会锁住一个索引记录以及它前后的间隙。它既能锁定某一行数据,也能防止插入新数据行进入该范围。使用Next-Key 锁用于防止事务插入新的行数据到锁定范围内,以避免幻读的发生。 2 详解 Next-Key 锁(1) 定义Next-Key 锁是 MySQL InnoDB 存储...

1 简介

本文解释Next-Key 锁 是结合了行锁和间隙锁的锁定机制。
在 InnoDB 存储引擎中,Next-Key 锁 会锁住一个索引记录以及它前后的间隙。
它既能锁定某一行数据,也能防止插入新数据行进入该范围。

使用Next-Key 锁用于防止事务插入新的行数据到锁定范围内,以避免幻读的发生。

2 详解 Next-Key 锁

  • (1) 定义

Next-Key 锁是 MySQL InnoDB 存储引擎 在可重复读(REPEATABLE READ)隔离级别下,用于 防止幻读(Phantom Read) 的一种锁定机制。

它结合了:

行锁(Record Lock):锁住当前索引的具体行。
Gap 锁(间隙锁):锁住索引范围内的“空隙”。
最终,Next-Key 锁 锁定了索引记录及其前后的间隙,防止:

其他事务修改该行(行锁)
其他事务在该范围内插入新行(Gap 锁)

  • (2) 设计目的
    防止幻读:在 REPEATABLE READ 隔离级别下,防止其他事务在某个范围插入新记录,导致事务两次查询结果不同。
    确保范围查询的一致性:避免在事务过程中,范围内的记录发生变化。

3 Next-Key 锁 vs. 全局锁

  对比项 		Next-Key 锁  						全局锁(Global Lock)
  作用范围    某个索引的具体行和其前后的间隙   	整个 MySQL 实例
  影响范围    行级别和范围内的插入    			所有数据库的写操作
  锁定类型    索引级锁    							服务器级锁
  适用场景    事务操作,防止幻读   					全库备份、迁移
  影响SQL     SELECT ... FOR UPDATE,				  所有写操作.
  				INSERTUPDATEDELETE
  • Next-Key 锁的使用场景

(1) 防止幻读

事务 1 先执行 SELECT … FOR UPDATE,如果没有 Next-Key 锁,事务 2 可能会在该范围插入新数据,导致事务 1 读出的数据发生变化(幻读)。

(2) 确保范围查询的一致性

当执行范围查询(如 WHERE age > 25 FOR UPDATE),Next-Key 锁锁定索引记录及其前后的间隙,防止其他事务插入 age=26, 27, … 的新记录。

5 MySQL 中 Next-Key 锁的使用示例

(1) 创建测试表

      CREATE TABLE employees (
          id INT AUTO_INCREMENT PRIMARY KEY,
          name VARCHAR(50) NOT NULL,
          age INT NOT NULL,
          INDEX idx_age (age)  -- 创建索引
      ) ENGINE=InnoDB;

(2) 插入初始数据

		INSERT INTO employees (name, age) VALUES ('Alice', 25), ('Bob', 30), ('Charlie', 35);

(3) 测试 Next-Key 锁

事务 1:执行范围查询

  START TRANSACTION;
  SELECT * FROM employees WHERE age BETWEEN 25 AND 35 FOR UPDATE;

这条 SQL 语句会:

锁住已有数据(age = 25, 30, 35)(行锁)。
锁住 age ∈ (25, 35) 这个范围的“空隙”(Gap 锁)。
最终形成 Next-Key 锁,阻止其他事务插入或修改该范围的任何数据。

事务 2:尝试插入数据

		INSERT INTO employees (name, age) VALUES ('David', 28);

这将被阻塞!
因为 28 落在 25-35 的范围,Next-Key 锁阻止插入,直到事务 1 提交或回滚。

事务 1 提交

		COMMIT;

事务 2 现在可以继续执行插入。

6. 在web服务中使用 Next-Key 锁

(1) 代码示例

    var db *sql.DB

    func init() {
        var err error
        dsn := "root:password@tcp(127.0.0.1:3306)/testdb?parseTime=true"
        db, err = sql.Open("mysql", dsn)
        if err != nil {
            log.Fatal(err)
        }
        db.SetMaxOpenConns(10)
        db.SetMaxIdleConns(5)
    }

    // 事务 1: 查询数据并加 Next-Key 锁
    func transaction1(c *gin.Context) {
        tx, err := db.Begin()
        if err != nil {
            c.JSON(http.StatusInternalServerError, gin.H{"error": "事务开启失败"})
            return
        }

        // 执行查询并加锁
        _, err = tx.Exec("SELECT * FROM employees WHERE age BETWEEN 25 AND 35 FOR UPDATE")
        if 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": "事务 1 执行完成"})
    }

    // 事务 2: 插入数据
    func transaction2(c *gin.Context) {
        _, err := db.Exec("INSERT INTO employees (name, age) VALUES ('David', 28)")
        if err != nil {
            c.JSON(http.StatusInternalServerError, gin.H{"error": "插入失败,可能被 Next-Key 锁阻止"})
            return
        }

        c.JSON(http.StatusOK, gin.H{"message": "插入成功"})
    }

    func main() {
        r := gin.Default()
        r.GET("/tx1", transaction1) // 执行事务 1(锁定范围)
        r.GET("/tx2", transaction2) // 执行事务 2(尝试插入)
        r.Run(":8080")
    }

7 小结

(1) Next-Key 锁的作用

既锁住索引行,又锁住范围,防止其他事务修改或插入新数据。
防止幻读,确保事务的一致性。

(2) Next-Key 锁 vs. 全局锁

		对比项 		Next-Key 锁  					全局锁(Global Lock)
    作用范围    索引记录和其前后的间隙   			整个 MySQL 服务器
    影响操作    INSERTUPDATEDELETE(范围锁定)  所有写操作
    适用场景    防止幻读,事务一致性  				备份、迁移
    加锁方式    行级锁和Gap 锁 						服务器级锁

Next-Key 锁 主要用于 防止并发事务中的幻读,锁住索引行及其前后的间隙,防止数据变动。
全局锁 则是 影响整个 MySQL 实例,用于备份、数据迁移等场景。

服务 API 结合 Next-Key 锁 可以 模拟事务的加锁机制,确保范围内的数据一致性。
当你的业务需要 严格控制事务范围内的数据一致性(如订单管理、库存系统),Next-Key 锁是非常关键的锁机制!

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

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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