MySQL数据库自增锁问题

73 阅读2分钟

背景

执行数据库表归档任务,把【table_1】表数据导入到【table_1_backup】表中,使用多线程并发执行【insert ... select】语句


INSERT table_1_backup 

SELECT * FROM table_1 

WHERE id in (?, ?, ?, ?, ?, ?, ?, ? ...)

问题

部分任务线程发生数据库异常,数据库死锁,插入执行失败

异常信息:


MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction

INSERT种类 与 自增锁关系

insert种类

(1) INSERT-like

所有可以向表中增加行的语句,包括INSERT, INSERT ... SELECT, REPLACE, REPLACE ... SELECT, and LOAD DATA。

包括“simple-inserts”, “bulk-inserts”, and “mixed-mode inserts”.

(2) Simple inserts

就是通过分析insert语句可以确定插入数量的insert语句, INSERT, INSERT … VALUES(),VALUES(),

没有嵌套子查询的单行和多行INSERT和REPLACE语句,但不包括INSERT ... ON DUPLICATE KEY UPDATE

(3) Bulk inserts

就是通过分析insert语句不能确定插入数量的insert语句, INSERT … SELECT, REPLACE … SELECT, LOAD DATA,不包括纯INSERT。

InnoDB在处理每行时一次为AUTO_INCREMENT列分配一个新值。 

(4) Mixed-mode inserts (存疑)

下面两种,不确定是否需要分配auto_increment id
INSERT INTO t1 (c1,c2) VALUES (1,’a'), (NULL,’b'), (5,’c'), (NULL,’d');
INSERT … ON DUPLICATE KEY UPDATE

自增锁 (auto_inc锁)

如果存在自增字段,MySQL会维护一个自增锁,和自增锁相关的一个参数为innodb_autoinc_lock_mode,可以设定3个值,0,1,2。

show variables like '%autoinc_lock_mode%'  

innodb_autoinc_lock_mode=0

所有的insert语句在语句开始时得到一个表级的自增锁,语句结束的时释放锁,一个一个分配值(MyIsam引擎的默认值)

innodb_autoinc_lock_mode=1 轻量锁

批量插入时,会加一个自增锁,分配一段连续的id值,只要语句得到了响应的值后就可以提前释放锁,不需要保持到语句结束,可能会造成id不连续的情况(1,3,2)

innodb_autoinc_lock_mode=2 无锁

无自增锁,性能最优

  • 来一个分配一个,而不会锁表,只会锁住分配id的过程;

  • 适用于row复制(RBR)

  • 但会造成基于statement的复制(SBR)出问题,主要会造成从库的主键冲突;

解决方式

(1)innodb row复制时,可将innodb_autoinc_lock_mode设置为2,这时可在所有insert情况下表获得最大并发度 

(2)另一种方式:去除mcc_customer_consume_archive主键的auto_increment特性

其他:

innodb statement复制时,可将innodb_autoinc_lock_mode设置为1,保证复制安全的同时,获得简单insert语句的最大并发度 
myisam引擎情况下,无论什么样自增id锁都是表级锁,设置innodb_autoinc_lock_mode参数无效