【MySQL】 Innodb的表锁问题_auto_increment

定义:

使用auto_increment的字段可能生成唯一的标识,mysql可以保证这个字段在多进程操作时的原子性;

AUTO-INC锁是当向使用含有AUTO_INCREMENT列的表中插入数据时需要获取的一种特殊的表级锁;

使用:

可在建表时可用“AUTO_INCREMENT=n”选项来指定一个自增的初始值

可用alter table table_name AUTO_INCREMENT=n命令来重设自增的起始值

规范:

AUTO_INCREMENT适用于有唯一索引的非空正整数数据列【

前提必须将AUTO_INCREMENT列定义为索引的一部分,以便可以对表执行相当于索引的SELECT MAX(ai_col)查找以获取最大列值

innodb最为大家津津乐道的就是它实现了行锁等高级特性,相比之下,myisam的表锁显得有些弱智。不过很多人都忽视了一点,innodb在MySQL5.0里有时候的行为也是表锁:比如说当表里有一个auto_increment字段的时候,innodb会在内存里保存一个计数器用来记录auto_increment的值,当插入一个新行数据时,就会用一个表锁AUTO-INC来锁住这个计数器,直到插入结束(自增字段必须是索引第一列)。如果一行一行的插入数据则没有什么问题,但是如果大量的并发插入就废了,表锁会引起SQL堵塞,不但影响效率,而且可能会瞬间达到max_connections而崩溃。

如果你必须面对大量并发插入的情况,要么听天由命,要么便只能抛弃auto_increment,转而使用自定义主键的方式,但必须小心选择,否则可能会陷入到另一个陷阱当中,比如说,有的人会通过使用UUID主键的方式来回避auto_increment表锁的问题,但是这可能会让应用陷入更严重的IO瓶颈问题之中去,原因可以参考我以前写的文章

还有一个方法就是使用MySQL5.1,在这个版本里,出现了一个新的配置选项:innodb_autoinc_lock_mode,它是专门用来在使用auto_increment的情况下调整锁策略的,目前有三种选择:

为了方便说明,先介绍几个术语:

1.“INSERT-like”:

INSERT, INSERT … SELECT, REPLACE, REPLACE … SELECT, and LOAD DATA, INSERT … VALUES(),VALUES()
所有可以向表中增加行的语句,包括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

回到三种选择:

//所有的insert语句在语句开始时得到一个表级的auto_inc锁,语句结束的时释放锁,一个一个分配值
innodb_autoinc_lock_mode = 0 (“traditional” lock mode) 

//对simple insert做了优化,一次性插入值的个数可以立马得到确定,所以mysql可以一次生成几个连续的值,用于这个insert语句,
//保证了基于语句复制的安全,这是mysql的默认模式,好处是auto_inc锁不需要保持到语句的结束,只要语句得到了相应的值后就可以提前释放锁,可能会造成id不连续出现1,3,2的情况 
   1)“Simple inserts”:直接通过分析语句,获得要插入的数量,然后一次性分配足够的auto_increment id,只会将整个分配的过程锁住

   2)“Bulk inserts”:因为不能确定插入的数量,因此使用和以前的模式相同的表级锁定
   3)“Mixed-mode inserts”:直接分析语句,获得最坏情况下需要插入的数量,然后一次性分配足够的auto_increment id,只会将整个分配的过程锁住
innodb_autoinc_lock_mode = 1 (“consecutive” lock mode) 

//没有了auto_inc锁,性能最优,但会造成基于statement的复制出问题,主要会造成从库的主键冲突;
  1)来一个分配一个,而不会锁表,只会锁住分配id的过程
  2)当并发执行时,“Bulk inserts”在分配的时会同时向其他的INSERT分配,会出现主从不一致
innodb_autoinc_lock_mode = 2 (“interleaved” lock mode) 

对于1:每次根据已经插入了N条数据、预申请N-1个id,insert执行完成后,会特别将这些预留id空出(特意将预申请后的当前最大id回写到表中)

实际insert行

自增id增加值

2、3

3

4、5、6、7

7

8~15

15

总结:1 innodb row复制时,可将innodb_autoinc_lock_mode设置为2,这时可在所有insert情况下表获得最大并发度 
2 innodb statement复制时,可将innodb_autoinc_lock_mode设置为1,保证复制安全的同时,获得简单insert语句的最大并发度 
3 myisam引擎情况下,无论什么样自增id锁都是表级锁,设置innodb_autoinc_lock_mode参数无效(测试略) 
4 实际上提问者说到的在innodb引擎下自增id值作为主键的情况下,相比uuid或者自定义的主键,是可以提到插入速度的,因为innodb是主键聚集索引,实际的主键值必须按照主键顺序存取,那么自增id本身就是升序的,那么在插入数据时,底层就不必再做额外的排序操作,也减少了索引页分裂的次数,从而大大增加insert速度(除非其他方案也能保证主键完全自增)【

小结:

主键自增

innodb_autoinc_lock_mode:0 锁表  1 预留多个  2 来一个配一个

谢分享:

https://blog.csdn.net/adparking/article/details/8857857

https://www.jianshu.com/p/054cf6c10116

https://www.jianshu.com/p/68b581481831

https://blog.csdn.net/ashic/article/details/53810319

https://www.jianshu.com/p/68b581481831

猜你喜欢

转载自blog.csdn.net/ma15732625261/article/details/81587023