MySQL AutoIncrement--自增锁模式

自增锁模式

在MYSQL 5.1.22版本前,自增列使用AUTO_INC Locking方式来实现,即采用一种特殊的表锁机制来保证并发插入下自增操作依然是串行操作,为提高插入效率,该锁会在插入语句完成后立即释放,而不是插入语句所在事务提交时释放。该设计并发性能太差,尤其在大批量数据在一条语句中插入时(INSERT SELECT ), 会导致该语句长时间持有这个“表锁”,从而阻塞其他事务的插入操作。

在MYSQL 5.1.22版本开始,InnoDB存储引使用一种轻量级互斥锁(Mutex)来控制自增列增长,并提供innodb_autoinc_lock_mode参数来控制。

自增长方式可分为下面四类:

1.INSERT-LIKE:指所有的插入语句,比如 INSERT、REPLACE、INSERT…SELECT、REPLACE…SELECT,LOAD DATA等。
2.Simple insert:指在插入前就能确定插入行数的语句,包括INSERT、REPLACE,不包含INSERT…ON DUPLICATE KEY UPDATE这类语句。
3.Bulk inserts:指在插入前不能确定得到插入行的语句。如INSERT…SELECT,REPLACE…SELECT,LOAD DATA.
4.Mixed-mode inserts:指其中一部分是子增长的,有一部分是确定的。

innodb_autoinc_lock_mode参数取值

1.innodb_autoinc_lock_mode=0 传统锁定模式, 5.1.22之前的方式,也就是所有INSERT-LIKE操作都用AUTO-inc locking。
2.innodb_autoinc_lock_mode=1 连续锁定模式,这个参数是5.1.22之后出现的也是之后的默认值,对于SIMPLE INSERT,使用轻量级互斥锁,对于BULK INSERT,使用AUTO-inc locking。
3.innodb_autoinc_lock_mode=2 交错锁定模式,指不管什么情况都使用轻量级互斥的锁,效率最高,但是复制只能使用row-basereplication,因为statement-base replication会出现问题。

当innodb_autoinc_lock_mode=1时,"Simple insert"操作能在插入前知道插入的记录数量,因此无需在整个插入操作过程中持有表级别的AUTO-INC锁,MySQL通过轻量级互斥锁来控制INSERT操作获取自增值的过程,并在INSERT操作获取到自增值后快速释放互斥锁,通过降低锁颗粒度和锁持续周期,实现"Simple insert"操作并发执行。当其他事物对表持有AUTO-INC锁时,"Simple insert"操作也会升级使用AUTO-INC锁并被阻塞。

当innodb_autoinc_lock_mode=1时,在语句复制格式下(BINLOG_FORMAT=STATEMENT),BINLOG中没有记录主库执行过程中获取到的所有自增值及其对应行的信息,要保证"Bulk insert"操作主从复制数据一致就必须保证语句在主库和从库执行时获取到相同自增值,而因此只能通过控制“获取连续自增值”的方式来实现,同时为避免受其他事务插入操作影响,就必须在表级别加锁且保证持有锁至语句结束。

在行复制格式下(BINLOG_FORMAT=ROW),主库BINLOG中保存有记录的所有列信息包括自增列值,因此无需通过AUTO-INC锁来保证主从数据一致。

在MySQL 8.0版本前,参数BINLOG_FORMAT的默认值为STATEMENT,参数innodb_autoinc_lock_mode的默认值为1。

在MySQL 8.0版本后,参数BINLOG_FORMAT的默认值被调整为ROW格式,参数innodb_autoinc_lock_mode的默认值为2。

自增值间隙

在MySQL中,获取自增值的操作是非事务性,获取自增值的操作产生的锁在语句执行过程中或执行完成里便被释放而不会持续到事务提交和回滚,获取到自增值也不会随事务回滚而回滚,因此不能依赖MySQL自增列来实现表中列值连续无间隙。

表中自增列作为代理键,只能用来标识和定位记录,而不应该承载业务逻辑,因此也不建议对自增列值进行显式更新。

AUTO_INC锁导致的死锁

在业务繁忙的表上通过pt-osc进行DDL操作变更时,可能会出现死锁,死锁场景如下:

pt-osc工具执行对tb001执行DDL变更操作,创建临时表_tb001_new来执行变更并拷贝数据,并在tb001上创建增删改三种触发器。

步骤1、事务1执行业务操作对表tb001进行数据更新,对更新记录加排他锁(X)

步骤2、pt-osc的拷贝线程拷贝数据,事务2执行INSERT INTO _tb001_new SELECT FROM tb001 WHERE ... LOCK IN SHARE MODE的操作,先执行INSERT INTO操作申请_tb001_new表上AUTO-INC锁,申请成功

步骤3、事务1更新操作触发表tb0011上update触发器执行中执行REPLACE INTO操作,向表_tb001_new更新插入数据,申请_tb001_new表上AUTO-INC锁,由于事务2持有_tb001_new表上AUTO-INC锁,因此事务1被阻塞。

步骤4、事务2获取_tb001_new表上AUTO-INC锁后,再执行SELECT FROM tb001 WHERE ... LOCK IN SHARE MODE操作,申请某个范围内所有记录上的共享锁(S),由于事务1持有其中一条或多条记录的排他锁(X),导致事务2被阻塞。

步骤5、事务2被阻塞,触发死锁检测,发现事务1和事务2相互等待锁资源形成死锁,挑选事务1或事务2进行事务回滚。

上述死锁解决办法:

1、在行复制格式下(BINLOG_FORMAT=ROW),可以考虑调整参数innodb_autoinc_lock_mode的值为2(非动态参数,需重启服务)

2、通过降低锁冲突概率来降低死锁发生概率:

1、在业务低峰期进行DDL变更操作
2、优化业务SQL降低单个事务修改记录数
3、调整pt-osc单次拷贝操作的数据量

参考资料:

https://dev.mysql.com/doc/refman/8.0/en/innodb-parameters.html#sysvar_innodb_autoinc_lock_mode

原文地址:https://www.cnblogs.com/gaogao67/p/11123772.html

时间: 2024-10-10 23:46:04

MySQL AutoIncrement--自增锁模式的相关文章

自增锁

自增锁,在提交前释放,并发插入高 s,x等锁,在COMMIT扣释放,并发插入需要等待 不能回滚到前面的值 insert -like: simple-insert:插入前就能确定插入行数语句 bulk insert :插入前不确定插入行数的语句 replace ... select mixed-mode inserts:insert into t1(c1,c2) values(1,"a"),(null,"b"),(4,"c"),(null,&quo

mysql主主互备模式配置

本文为南非蚂蚁的书籍<循序渐进linux-第二版>-8.3.5的读笔记 mysql双主互备架构图 mysql主主互备模式配置 环境: DB1:主服务器  centos6.6  mysql5.1.73 IP:10.24.24.111 DB2:从服务器  centos6.6  mysql5.1.73 IP:10.24.24.112 mysql VIP:10.24.24.112 ---------------------------------------- centos6.x安装mysql # y

【mysql】关于悲观锁

关于mysql中的锁 在并发环境下,有可能会出现脏读(Dirty Read).不可重复读(Unrepeatable Read). 幻读(Phantom Read).更新丢失(Lost update)等情况,所以mysql引入了很多锁的概念 MySQL InnoDB对数据行的锁定类型一共有四种:共享锁(读锁,S锁).排他锁(写锁,X锁).意向共享锁(IS锁)和意向排他锁(IX锁),支持三种行锁定方式: 行锁(Record Lock):锁直接加在索引记录上面. 间隙锁(Gap Lock):锁加在不存

深入理解MYSQL的MDL元数据锁

1 前言 2 MDL锁与实现 3 MDL锁的性能与并发改进 4 MDL锁的诊断 前言 好久没更新,主要是因为Inside君最近沉迷于一部动画片——<新葫芦娃兄弟>.终于抽得闲,完成了本篇关于MySQL MDL锁的深入分析与介绍.虽然之前有很多小伙伴分析过,但总感觉少了点什么,故花了点时间翻看了下源码.Inside君或许不是最牛掰的内核开发人员,但自认为应该是业界最会讲故事的码农,希望本篇能做到通俗易懂,因为MDL锁其实并不好理解.如果同学们还有问题,也可以直接看源码文件mdl.cc. MDL锁

自增锁引发的悲剧

背景 先描述下故障吧 step0: 环境介绍 1. MySQL5.6.27 2. InnoDB 3. Centos 基本介绍完毕,应该跟大部分公司的实例一样 CREATETABLE`new_table`( `id` int(11) NOT NULL AUTO_INCREMENT, `x` varchar(200) DEFAULT '', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5908151 DEFAULT CHARSET=utf8 C

mysql 数据库自增id 的总结

有一个表StuInfo,里面只有两列 StuID,StuName其中StuID是int型,主键,自增列.现在我要插入数据,让他自动的向上增长,insert into StuInfo(StuID,StuName) values(????) 如何写? INSERT INTO StuInfo(StuID,StuName) VALUES (NULL, `字符`)或者INSERT INTO StuInfo(StuName) VALUES (`字符`) INSERT和REPLACE语句的功能都是向表中插入新

MySQL分表自增ID解决方案(转)

当我们对MySQL进行分表操作后,将不能依赖MySQL的自动增量来产生唯一ID了,因为数据已经分散到多个表中. 应尽量避免使用自增IP来做为主键,为数据库分表操作带来极大的不便. 在postgreSQL.oracle.db2数据库中有一个特殊的特性---sequence. 任何时候数据库可以根据当前表中的记录数大小和步长来获取到该表下一条记录数.然而,MySQL是没有这种序列对象的. 可以通过下面的方法来实现sequence特性产生唯一ID: 1. 通过MySQL表生成ID 在<关于MySQL分

mysql中的乐观锁和悲观锁

mysql中的乐观锁和悲观锁的简介以及如何简单运用. 关于mysql中的乐观锁和悲观锁面试的时候被问到的概率还是比较大的. mysql的悲观锁: 其实理解起来非常简单,当数据被外界修改持保守态度,包括自身系统当前的其他事务,以及来自外部系统的事务处理,因此,在整个数据处理过程中,将数据处于锁定状态.悲观锁的实现,往往依靠数据库提供的锁机制,但是也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在自身系统中实现了加锁机制,也无法保证外部系统不会修改数据. 来点实际的,当我们使用悲观

Spring事务管理实现原理及MySQL InnoBD引擎行锁概述

Spring实现事务管理的机制 Spring事务管理是基于AOP编程思想实现,Spring框架被广泛使用的原因之一,就是提供了强大的事务管理机制. AOP是什么?我们常说的AOP并不是指一种开发技术,而是一种编程思想,AOP的核心概念就是面向切面编程,实现可插拔,降低程序之前的耦合性,提高重用性. Spring AOP 基于动态代理实现,一种是JDK动态代理,另一种是CGLIB动态代理. spring2.5之前声明式事务可以这样实现: ?<!-- 声明使用的spring事务管理器--> <