深入谈谈MySQL中的自增主键
IT行业相对于一般传统行业,发展更新速度更快,一旦停止了学习,很快就会被行业所淘汰。所以我们需要踏踏实实的不断学习,精进自己的技术,尤其是初学者。今天golang学习网给大家整理了《深入谈谈MySQL中的自增主键》,聊聊主键、mysql自增,我们一起来看看吧!
MySQL的主键可以是自增的,那么如果在断电重启后新增的值还会延续断电前的自增值吗?自增值默认为1,那么可不可以改变呢?下面就说一下 MySQL的自增值。
特点
保存策略
1、如果存储引擎是 MyISAM,那么这个自增值是存储在数据文件中的;
2、如果是 InnoDB引擎,1)在 5.6之前是存储在内存中,没有持久化,在重启后会去找最大的键值,举个例子,如果一个表当前数据行里最大 id是10,AUTO_INCREMENT=11。这时候,我们删除 id=10 的行,AUTO_INCREMENT 还是 11。但如果马上重启实例,重启后这个表的 AUTO_INCREMENT 就会变成 10;
2)在 8.0开始,自增值就保存在 redo log中,重启后会从 redo log中读取之前保存的自增值。
自增值的确定
1、如果插入数据时 id字段指定为0、null或未指定,那么就把这个表当前的 AUTO_INCREMENT值填到自增字段,并且会以auto_increment_offset作为初始值,auto_increment_increment为步长,找出第一个大于当前自增值的值作为新的自增值。
2、如果插入的数据的 id字段指定了具体的值,就直接使用语句里的值。
在一些场景下,使用的就不全是默认值。比如,双 M 的主备结构里要求双写的时候,我们就可能会设置成 auto_increment_increment=2,让一个库的自增 id 都是奇数,另一个库的自增 id 都是偶数,避免两个库生成的主键发生冲突。
自增值的修改
假设某次要输入的值是 X,当前的自增值是 Y。那么:
1、如果 X 2、如果X≥Y,那么就把当前自增值修改为新的自增值。 假设有表t ,id是自增主键,在已有 (1,1,1)的情况下,插入一条 (null,1,1),那么执行过程就如下: 1、执行器调用 InnoDB 引擎接口写入一行,传入的这一行的值是 (0,1,1); 2、InnoDB 发现用户没有指定自增 id 的值,获取表 t 当前的自增值 2; 3、将传入的行的值改成 (2,1,1); 4、将表的自增值改成 3; 5、继续执行插入数据操作,由于已经存在 c=1 的记录,所以报 Duplicate key error,语句返回。 由于上面说得这种特性,在一些场景中会出现主键不连续的现象。 场景1:添加数据时唯一索引重复 在 c列索引重复后,原本要分配的主键值 2就会被丢弃,而下次再次插入就从 2 开始计算,也就变成了 3。 场景2:事务回滚 在第二条语句回滚后分配给其的主键 2也会被丢弃。 场景3:特殊批插入优化导致 这里说得特殊的批插入指的是insert … select、replace … select 和 load data 语句。为什么说这些语句可能会导致?这就要说到自增锁了。首先自增锁是为了避免多线程冲突,因为在多线程下,如果同时有多个线程来获取自增值,那么就可能会导致同一个自增值被分配给多条记录,导致逐渐冲突。所以需要自增锁,而为什么前面说得这些批插入语句会导致主键不连续,在下面自增锁部分会说到。 问题:在说自增锁之前,先思考一个问题,为什么对于前两个场景,不把自增主键值设为可以回滚的?这样不就可以避免不连续了么? 答:因为设计成可回滚的会导致性能下降,看下面这个场景。 1、假设事务 A 申请到了 id=2, 事务 B 申请到 id=3,那么这时候表 t 的自增值是 4,之后继续执行。 2、事务 B 正确提交了,但事务 A 出现了唯一键冲突。 3、如果允许事务 A 把自增 id 回退,也就是把表 t 的当前自增值改回 2,那么就会出现这样的情况:表里面已经有 id=3 的行,而当前的自增 id 值是 2。 4、接下来,继续执行的其他事务就会申请到 id=2,然后再申请到 id=3。这时,就会出现插入语句报错“主键冲突”。 而为了解决上面这个问题,就需要从下面两个方法中选一个。 方法一、每次申请 id 之前,先判断表里面是否已经存在这个 id。如果存在,就跳过这个 id。但是,这个方法的成本很高。因为,本来申请 id 是一个很快的操作,现在还要再去主键索引树上判断 id 是否存在。 方法二:把自增 id 的锁范围扩大,必须等到一个事务执行完成并提交,下一个事务才能再申请自增 id。这个方法的问题,就是锁的粒度太大,系统并发能力大大下降。 所以,综合来看,比如取消自增值回滚的功能。 自增锁是为了避免在多线程中多个线程获取到同一个主键值,导致主键冲突。 5.0版本:范围是语句,只有等到语句执行完后才会释放。 5.1.22开始:引入了一个innodb_autoinc_lock_mode参数,根据参数值的不同执行不同的策略。默认是1。 1、参数等于0,表示采用之前的策略,即语句执行结束就会释放。 2、参数等于1,对于普通 insert语句,自增锁在申请之后立马释放; 对于 insert...select这样的批量插入数据的语句,会等到语句执行完才会释放。加锁范围是 select所涉及到的范围和间隙。 3、参数等于3,所有的申请自增主键的动作都是申请后就释放锁。 问题:为什么默认情况下, insert...select这样的批操作要使用语句级的锁?为什么参数默认不是2? 答:因为对于 insert...select这样的批量插入数据的语句,可能会导致主从不一致的情况发生。 在 sessionB执行完 "create table t2 like t"后,sessionA和 sessionB同时操作 t2。如果没有锁,那么执行过程就可能会出现下面的情况。 session B 先插入了两个记录,(1,1,1)、(2,2,2);然后,session A 来申请自增 id 得到 id=3,插入了(3,5,5);之后,session B 继续执行,插入两条记录 (4,3,3)、 (5,4,4)。 虽然这样看起来确实没有什么问题,但是如果是在集群中,主机这样执行,提示 binlog是 statement格式的,那么从机执行的顺序就有可能和主机不一致,最终导致主从不一致。所以需要在批量插入时加锁。而如果设置为2,那么如果 binlog不是 row,就会导致主从数据不一致。 所以,要想保证数据一致,也保证系统的并发性,可以有两种方案: 方案一:将 binlog格式设为 statement,innodb_autoinc_lock_mode设为1。 方案二:将 binlog格式设为 row,innodb_autoinc_lock_mode设为2。一般我们为了保证 MySQL的高可用,都将 binlog设为 row,所以一般选择第二种方案。 在批插入时,由于不知道一次性插入的语句有多少,如果记录多达几千万甚至上亿条,那么每次插入都需要分配一次自增值,这样效率会很慢,所以 MySQL 对批操作进行了优化: 1、语句执行过程中,第一次申请自增 id,会分配 1 个; 2、1 个用完以后,这个语句第二次申请自增 id,会分配 2 个; 3、2 个用完以后,还是这个语句,第三次申请自增 id,会分配 4 个; 4、依此类推,同一个语句去申请自增 id,每次申请到的自增 id 个数都是上一次的两倍。 举个例子,执行下面的代码 insert…select,实际上往表 t2 中插入了 4 行数据。但是,这四行数据是分三次申请的自增 id,第一次申请到了 id=1,第二次被分配了 id=2 和 id=3, 第三次被分配到 id=4 到 id=7。由于这条语句实际只用上了 4 个 id,所以 id=5 到 id=7 就被浪费掉了。之后,再执行 insert into t2 values(null, 5,5),实际上插入的数据就是(8,5,5)。这就是前面说到主键不连续的第三种情况。 假设有表结构 如果执行的语句是: 如果我们查询慢日志,会发现 扫描行数是1,也就是直接在 t上通过索引找到那一条记录,然后插入 t2表。 如果将这条语句改成 那么此时查看慢日志就会发现变成了 5,这是为什么?就算全查出来也只会是4条,这时我们查看扫描行数的变化 发现前后变化是4行,所以确定了是使用了临时表,那么就可以确定过程是: 1、创建临时表,表里有两个字段 c 和 d。 2、按照索引 c 扫描表 t,依次取 c=4、3、2、1,然后回表,读到 c 和 d 的值写入临时表。这时,Rows_examined=4。 3、由于语义里面有 limit 1,所以只取了临时表的第一行,再插入到表 t 中。这时,Rows_examined 的值加 1,变成了 5。 至于为什么需要临时表,这是为了防止在读取时,读到了刚刚插入的值。 优化 因为select返回的记录数较少,所以可以使用内存临时表来优化, 这样扫描的总行数只有 select的 1加上临时表上的 1。 对于唯一索引的冲突,可以使用insert into … on duplicate key update来进行冲突后的更新处理,假设表 t中有(1,1,1)、(2,2,2)两条记录,那么执行: 在插入时发现冲突就对冲突的记录进行修改操作。 以上就是本文的全部内容了,是否有顺利帮助你解决问题?若是能给你带来学习上的帮助,请大家多多支持golang学习网!更多关于数据库的相关知识,也可关注golang学习网公众号。执行过程

带来的问题

insert into t values(null,1,1);
begin;
insert into t values(null,2,2);
rollback;
insert into t values(null,2,2);
//插入的行是(3,2,2)
自增锁
加锁策略

批插入的优化
insert into t values(null, 1,1);
insert into t values(null, 2,2);
insert into t values(null, 3,3);
insert into t values(null, 4,4);
create table t2 like t;
insert into t2(c,d) select c,d from t;
insert into t2 values(null, 5,5);
insert...select前后操作同一个表会用到临时表
CREATE TABLE `t` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`c` int(11) DEFAULT NULL,
`d` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `c` (`c`)
) ENGINE=InnoDB;
insert into t values(null, 1,1);
insert into t values(null, 2,2);
insert into t values(null, 3,3);
insert into t values(null, 4,4);
create table t2 like t
insert into t2(c,d) (select c+1, d from t force index(c) order by c desc limit 1);

insert into t(c,d) (select c+1, d from t force index(c) order by c desc limit 1);

create temporary table temp_t(c int,d int) engine=memory;
insert into temp_t (select c+1, d from t force index(c) order by c desc limit 1);
insert into t select * from temp_t;
drop table temp_t;
最后

总结
MySQL实现replace函数的几种实用场景
- 上一篇
- MySQL实现replace函数的几种实用场景
- 下一篇
- mysql聚合统计数据查询缓慢的优化方法
-
- 数据库 · MySQL | 1天前 |
- MySQL数值函数大全及使用技巧
- 117浏览 收藏
-
- 数据库 · MySQL | 2天前 |
- 三种登录MySQL方法详解
- 411浏览 收藏
-
- 数据库 · MySQL | 3天前 |
- MySQL数据备份方法与工具推荐
- 420浏览 收藏
-
- 数据库 · MySQL | 3天前 |
- MySQL数据备份方法与工具推荐
- 264浏览 收藏
-
- 数据库 · MySQL | 4天前 |
- MySQL索引的作用是什么?
- 266浏览 收藏
-
- 数据库 · MySQL | 5天前 |
- MySQL排序原理与实战应用
- 392浏览 收藏
-
- 数据库 · MySQL | 1星期前 |
- MySQLwhere条件查询技巧
- 333浏览 收藏
-
- 数据库 · MySQL | 1星期前 |
- MySQL常用数据类型有哪些?怎么选更合适?
- 234浏览 收藏
-
- 数据库 · MySQL | 1星期前 |
- MySQL常用命令大全管理员必学30条
- 448浏览 收藏
-
- 数据库 · MySQL | 1星期前 |
- MySQL高效批量插入数据方法大全
- 416浏览 收藏
-
- 数据库 · MySQL | 1星期前 |
- MySQL性能优化技巧大全
- 225浏览 收藏
-
- 数据库 · MySQL | 1星期前 |
- MySQL数据备份4种方法保障安全
- 145浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- ChatExcel酷表
- ChatExcel酷表是由北京大学团队打造的Excel聊天机器人,用自然语言操控表格,简化数据处理,告别繁琐操作,提升工作效率!适用于学生、上班族及政府人员。
- 3167次使用
-
- Any绘本
- 探索Any绘本(anypicturebook.com/zh),一款开源免费的AI绘本创作工具,基于Google Gemini与Flux AI模型,让您轻松创作个性化绘本。适用于家庭、教育、创作等多种场景,零门槛,高自由度,技术透明,本地可控。
- 3380次使用
-
- 可赞AI
- 可赞AI,AI驱动的办公可视化智能工具,助您轻松实现文本与可视化元素高效转化。无论是智能文档生成、多格式文本解析,还是一键生成专业图表、脑图、知识卡片,可赞AI都能让信息处理更清晰高效。覆盖数据汇报、会议纪要、内容营销等全场景,大幅提升办公效率,降低专业门槛,是您提升工作效率的得力助手。
- 3409次使用
-
- 星月写作
- 星月写作是国内首款聚焦中文网络小说创作的AI辅助工具,解决网文作者从构思到变现的全流程痛点。AI扫榜、专属模板、全链路适配,助力新人快速上手,资深作者效率倍增。
- 4513次使用
-
- MagicLight
- MagicLight.ai是全球首款叙事驱动型AI动画视频创作平台,专注于解决从故事想法到完整动画的全流程痛点。它通过自研AI模型,保障角色、风格、场景高度一致性,让零动画经验者也能高效产出专业级叙事内容。广泛适用于独立创作者、动画工作室、教育机构及企业营销,助您轻松实现创意落地与商业化。
- 3789次使用
-
- golang 数据库主键的问题
- 2023-02-25 217浏览
-
- 关于pt-archiver和自增主键的那些事
- 2023-02-16 243浏览
-
- MySQL8自增主键变化图文详解
- 2023-01-02 103浏览
-
- Mysql分库分表之后主键处理的几种方法
- 2022-12-31 469浏览
-
- 深入分析mysql为什么不推荐使用uuid或者雪花id作为主键
- 2022-12-29 234浏览

