在服务开发中使用自增锁

举报
码乐 发表于 2025/02/06 09:09:51 2025/02/06
【摘要】 1 简介AUTO-INC 锁 是 MySQL 在插入数据时使用的一种锁,特别是对于具有自增主键(AUTO_INCREMENT)的表。为了避免并发插入操作时导致冲突,MySQL 会加锁来保证自增字段的唯一性。当多个事务插入数据时,AUTO-INC 锁确保自增字段的顺序性,避免多个事务之间因自增值冲突而发生死锁。AUTO-INC 锁(自增锁)详解 2 AUTO-INC 锁的设计模式(1) 什么...

1 简介

AUTO-INC 锁 是 MySQL 在插入数据时使用的一种锁,特别是对于具有自增主键(AUTO_INCREMENT)的表。
为了避免并发插入操作时导致冲突,MySQL 会加锁来保证自增字段的唯一性。

当多个事务插入数据时,AUTO-INC 锁确保自增字段的顺序性,避免多个事务之间因自增值冲突而发生死锁。

AUTO-INC 锁(自增锁)详解

2 AUTO-INC 锁的设计模式

  • (1) 什么是 AUTO-INC 锁?

AUTO-INC 锁(Auto-Increment Lock)是 MySQL 在插入数据到带有 AUTO_INCREMENT 自增主键的表时使用的锁,它保证并发插入时,自增列的值不会发生冲突。

  • (2) 设计目的

保证唯一性:确保多个并发插入操作时,自增字段值不会重复。
防止跳号:在某些存储引擎模式下,避免事务回滚后自增 ID 产生不连续的情况。
优化性能:减少锁的粒度,提高并发插入的吞吐量。

  • (3) 主要特性

作用范围:

仅对 AUTO_INCREMENT 的列加锁,不影响其他列或其他表。
作用于单个表,不会影响整个数据库。

表级锁(Table-Level Lock):

MySQL 的 AUTO-INC 锁会锁住整个表,但仅限于当前正在插入的会话,不会影响读取操作。

隐式释放:

当 INSERT 语句执行完成后,锁自动释放,无需手动 UNLOCK。
不同存储引擎的实现:

MyISAM:

采用 表级锁,在 INSERT 操作时锁定整个表,避免多个线程同时获取相同的 AUTO_INCREMENT 值。

InnoDB(默认):

默认使用 innodb_autoinc_lock_mode = 1(连续模式),只锁住 当前分配的 ID,提高并发性能。
可以调整 innodb_autoinc_lock_mode = 0(传统模式),对所有 INSERT 语句加锁,保证严格的 ID 顺序(性能较差)。

3. AUTO-INC 锁的使用场景

  • (1) 并发插入

当多个事务同时往同一张带 AUTO_INCREMENT 的表插入数据时,MySQL 需要确保自增 ID 的唯一性。

  • (2) 主键自增列

如果表的主键是 AUTO_INCREMENT,那么 INSERT 语句在执行时需要锁定这个自增字段,以防止 ID 冲突。

  • (3) 批量插入
    大量并发 INSERT 时,AUTO-INC 锁可以减少自增 ID 分配冲突,提高事务吞吐量。

4. MySQL 中 AUTO-INC 锁的使用示例

(1) 创建一个带 AUTO_INCREMENT 的表

    CREATE TABLE users (
        id INT AUTO_INCREMENT PRIMARY KEY,
        name VARCHAR(100) NOT NULL
    ) ENGINE=InnoDB;

id 列是自增主键,插入数据时 AUTO-INC 锁会确保 id 唯一且顺序递增。

(2) 插入数据

    INSERT INTO users (name) VALUES ('Alice');
    INSERT INTO users (name) VALUES ('Bob');

MySQL 采用 AUTO-INC 锁 保证 Alice 和 Bob 获得不同的 ID,避免并发插入时 ID 冲突。

(3) 并发测试

假设两个事务同时执行:

    -- 事务 1
    START TRANSACTION;
    INSERT INTO users (name) VALUES ('Charlie');

    -- 事务 2
    START TRANSACTION;
    INSERT INTO users (name) VALUES ('David');

MySQL 通过 AUTO-INC 锁确保事务 1 分配的 ID 和事务 2 不冲突。
事务 1 提交后,事务 2 继续执行,获取下一个自增 ID。

5. 在web服务中使用 AUTO-INC 锁

(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)
    }

    // 插入用户,并观察 AUTO_INCREMENT 行为
    func insertUser(c *gin.Context) {
        name := c.Query("name")

        // 执行插入
        result, err := db.Exec("INSERT INTO users (name) VALUES (?)", name)
        if err != nil {
            c.JSON(http.StatusInternalServerError, gin.H{"error": "插入失败"})
            return
        }

        // 获取自增 ID
        id, _ := result.LastInsertId()
        c.JSON(http.StatusOK, gin.H{"message": "用户插入成功", "id": id})
    }

    func main() {
        r := gin.Default()
        r.GET("/insert", insertUser)
        r.Run(":8080")
    }

(2) API 测试

发送多个并发请求:

    http://localhost:8080/insert?name=Eve
    http://localhost:8080/insert?name=Frank

结果示例:

    {"message": "用户插入成功", "id": 1}
    {"message": "用户插入成功", "id": 2}

即使多个请求同时插入,ID 仍然是唯一且递增的。

6. 小结

AUTO-INC 锁 vs. 全局锁

  项 				AUTO-INC 锁(自增锁) 		全局锁(Global Lock)
  作用范围    		仅影响 AUTO_INCREMENT 列    	影响整个 MySQL 实例
  影响范围    		仅当前插入的表 				 所有表的写入操作
  加锁粒度    		表级锁(仅影响 ID 生成)  	  整个 MySQL 服务器
  适用场景    		并发插入,确保自增 ID 唯一性    备份、迁移数据库
  影响操作  		INSERT  							INSERT, UPDATE, DELETE 等所有写操作

AUTO-INC 锁的作用

主要用于保证 AUTO_INCREMENT 主键列的唯一性。
不会影响读取操作,仅影响插入操作。
锁是隐式的,在 INSERT 结束后自动释放。

与全局锁的区别

AUTO-INC 锁:仅影响 某个表的自增 ID,确保 ID 唯一性,不影响其他表或查询操作。
全局锁:影响整个数据库实例,阻止所有写操作,通常用于备份或迁移。

通过 INSERT INTO 语句,可以观察到 AUTO-INC 锁的作用,确保多个 API 请求分配的 ID 是唯一且递增的。

AUTO-INC 锁是MySQL 在插入自增列数据时的关键机制,在高并发插入时起着保证 ID 唯一性、提高事务吞吐量的重要作用。
它的影响范围仅限于表级别,而不会像全局锁那样影响整个数据库实例的操作

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

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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