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后的版本自增主键才是严格递增的。
指定自增主键对自增值的影响
//语句6
inser into mbond_test (id,name) values(6,"e");
//语句7
inser 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(name) values("a");
当达到最大值后,自增值就不变了,下次再插入就会报主键冲突,所以我们一般会设置主键id为unsigned bigint,8字节,最大值是2的64次方-1。
当一个表没有设置主键时,系统会为其额外增加一个主键row_id,6字节,最大值为2的48次方-1。当达到最大值后,下一次插入时不会报错,而是从0开始,会把之前的行覆盖,所以建议建表时一定要设置主键。
以上是关于MySQL自增主键那些事的主要内容,如果未能解决你的问题,请参考以下文章