MySQL自增主键那些事

Posted 码邦德

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL自增主键那些事相关的知识,希望对你有一定的参考价值。

数据准备:


MySQL版本:5.7.31,INNODB引擎,

autocommit=1,开启事务自动提交

auto_increment_offset =1,设置自增长初始值1

auto_increment_increment=1,设置自增长步长1

 create table mbond_test( -> id int(10) auto_increment, -> name varchar(30), -> primary key (id),    -> index(name)    -> ) engine =innodb charset=utf8;

分别执行如下语句

//语句1,隐式事务inser into mbond_test (name) values("a");//语句2,显式事务begin;inser into mbond_test (name) values("b");rollback;//语句3,隐式事务inser into mbond_test (name) values("c");//语句4,隐式事务delete from mbond_test where name = "c";//重启mysql后执行语句5,,隐式事务inser into mbond_test (name) values("d");

Q:语句3执行时插入的id值是多少?语句5又是多少、?

A:经过实操,答案分别是3和2。

分析:在MySQL8版本之前,Innodb引擎下的自增值是保存在内存中的,MySQL8开始提供持久化,记录在redo log中。以此结论简单分析下问题答案,语句2执行insert后事务回滚了,但自增值并没有回滚,此时内存中的自增值已经+1(从2变成3),因此语句3对应的id就是3,语句4删除了id=3的数据,重启mysql,内存数据会丢失,这个时候自增值的初始如下:

select max(id)+1 from mbond_test;

也就是重启后的自增值变成了2,因此语句5对应的id就是2。

那么在MySQL8版本下答案就是3和4了,也就是说MySQL8后的版本自增主键才是严格递增的。


指定自增主键对自增值的影响

//语句6inser into mbond_test (id,name) values(6,"e");//语句7inser into mbond_test (name) values("f");

Q:语句7执行时插入的id值是多少?

A:经过实操,答案是7.

分析:从结果来看,指定的id=6改变了自增值的值。这个时候表的数据如下:


因此自增列的值规则:

  • 如果自增列指定0、null、或者未指定值,则使用内存中的自增值

  • 如果指定值,则使用指定值

自增值的规则:

  • 如果指定值>=自增值,新的自增值=指定值+1

  • 如果指定值<自增值,新的自增值不变


关于自增值的妙用


场景:主备库需要同时支持写入数据。

这个时候主备库的两个参数设置如下

主:

auto_increment_offset =1

auto_increment_increment=2

备:

auto_increment_offset =2

auto_increment_increment=2

这样主库id都是奇数,备库id都是偶数,数据合并时不会造成主键冲突。



自增主键递增但不连续


在例子1中我们做了一个事务回滚的操作,发现事务回滚时自增值并不会回滚,从而造成了自增主键不连续,这也是不连续的原因之一。

试想下自增值为什么不能回滚

获取自增值时会有一个自增锁,保证同一时刻只有一个事务能取值并更新,为了提高并发性能,这个锁在获取到值后就释放了,而不是语句所在事务提交后释放,因此在事务并发时,如果某个事务回滚后把自增值回滚了,那么下一个事务就会出现主键冲突问题。

关于自增锁的规则可以通过参数 innodb_autoinc_lock_mode设置,默认是1:

  • 0,语句执行后释放

  • 1,普通insert,获取到自增值后就释放,批量insert,语句执行后释放

  • 2,全部都是获取后就释放

为什么默认是1?

还是因为事务并发的问题,如果有多个事务批量往同一张表插入数据,那么当binlog_format=statement时,备份数据时就会出现数据不一致的问题,因为id不连续的原因。所以默认设置1能保证批量插入时id是连续的,但我们知道binlog_format还有一种格式是row,备份数据时是不需要获取自增值得,因此实际产线上经常会有如下配置组合:

innodb_autoinc_lock_mode=2;

binlog_format=row;




自增主键和无主键


自增主键的自增值是有上限的,比如上面建的自增id最大值就是2的31次方-1,这个值不算很大,还是很有可能出现用完的情况,那么达到上限后再插入数据会出现什么情况尼?我们可以试下。

insert into mbond_test(id,name) values(2147483647,"a");

执行后我们发现正常插入,此时再执行如下sql:

insert into mbond_test(namevalues("a");

当达到最大值后,自增值就不变了,下次再插入就会报主键冲突,所以我们一般会设置主键id为unsigned bigint,8字节,最大值是2的64次方-1。


当一个表没有设置主键时,系统会为其额外增加一个主键row_id,6字节,最大值为2的48次方-1。当达到最大值后,下一次插入时不会报错,而是从0开始,会把之前的行覆盖,所以建议建表时一定要设置主键。



以上是关于MySQL自增主键那些事的主要内容,如果未能解决你的问题,请参考以下文章

mysql自增主键怎么用

mysql自增主键怎么用

mysql主键设置了自增以后,怎么修改初始值?

JAVA中如何保证线程安全以及主键自增有序

java面试一日一题:mysql中的自增主键

怎么设置主键自增