MySQL 主键自增也有坑?

news2024/11/18 21:28:15

在上篇文章中,松哥和小伙伴们分享了 MySQL 的聚簇索引,也顺便和小伙伴们分析了为什么在 MySQL 中主键不应该使用随机字符串。但是主键不用随机字符串用什么?主键自增?主键自增就是最佳方案吗?有没有其他坑?今天我们就来讨论下这个话题。

1. 为什么不用 UUID

经过上篇文章的介绍,我们知道在 MySQL 中,主键索引就是聚簇索引,MySQL 表中的数据是根据主键值聚集在一起的,聚簇索引是一棵 B+Tree,这棵树中的数据是有序的。

所以,如果我们使用 UUID 字符串作为主键,那么就会导致每次数据插入的时候,都需要在 B+Tree 中寻找到适合它自己的位置,找到之后就有可能要挪动后面的节点(就像在数组中插入一条记录),挪动后面的节点,就有可能涉及到页分裂,插入效率就会降低。

另一方面,在非聚簇索引中,叶子结点保存的是主键值,主键如果是一个很长的 UUID 字符串,就会占据较大的存储空间(相对 int 而言),那么同一个叶子结点能够保存的主键值数量就会减少,进而可能会导致树变高,树变高,意味着查询的时候 IO 次数增加,查询效率降低。

基于上面的分析,我们在 MySQL 中尽量不使用 UUID 作为主键,不用 UUID,可能会有小伙伴想到,那我使用主键自增行不行?

对于上面提到的两个使用 UUID 作为主键的问题,使用主键自增显然都可以解决。主键自增,每次只需要往树的末尾添加就行了,基本上不会涉及到页分裂问题;主键自增意味着主键是数字,占用的存储空间相对来说就比较小,对非聚簇索引的影响也会小一些。

那么主键自增就是最佳方案吗?主键自增有没有一些需要注意的问题?

2. 主键自增的问题

以下内容,有一个共同的大前提,就是我们的表设置了主键自增。

一般来说,主键自增是没有什么问题的。但是,如果在高并发环境下,就会有问题了。

首先最容易想到的就是在高并发插入的时候产生的尾部热点问题,并发插入时,大家都需要去查询这个值然后计算出自己的主键值,那么主键的上界就会成为热点数据,并发插入时这里会产生锁竞争。

为了解决这个问题,我们就需要选择适合自己的 innodb_autoinc_lock_mode

2.1 数据插入的三种形式

首先,我们在向数据表中插入数据的时候,一般来说有三种不同的形式,分别如下:

  1. insert into user(name) values('javaboy') 或者 replace into user(name) values('javaboy') ,这种没有嵌套子查询并且能够确定具体插入多少行的插入叫做 simple insert,不过需要注意的是 INSERT ... ON DUPLICATE KEY UPDATE 不算是 simple insert
  2. load data 或者 insert into user select ... from ....,这种都是批量插入,叫做 bulk insert,这种批量插入有一个特点就是插入多少条数据在一开始是未知的。
  3. insert into user(id,name) values(null,'javaboy'),(null,'江南一点雨'),这种也是批量插入,但是跟第二种又不太一样,这种里边包含了一些自动生成的值(本案例中的主键自增),并且能够确定一共插入多少行,这种称之为 mixed insert,对于前面第一点提到的 INSERT ... ON DUPLICATE KEY UPDATE 也算是一种 mixed insert

将数据插入分为这三类,主要是因为在主键自增的时候,锁的处理方案不同,我们继续往下看。

2.2 innodb_autoinc_lock_mode

我们可以通过控制 innodb_autoinc_lock_mode 变量的值,来控制在主键自增的时候,MySQL 锁的处理思路。

innodb_autoinc_lock_mode 变量一共有三个不同的取值:

  • 0: 这个表示 traditional,在这种模式下,我们上面提到的三种不同的插入 SQL,对于自增锁的处理方案是一致的,都是在插入 SQL 语句开始的时候,获取到一个表级的 AUTO-INC 锁,然后当插入 SQL 执行完毕之后,再释放掉这把锁,这样做的好处是可以确保在批量插入的时候,自增主键是连续的。
  • 1: 这个表示 consecutive,在这种模式下,对 simple insert(能够确定具体插入行数的,对应上面 1、3 两种情况)做了一些优化,由于 simple insert 插入多少行这个很好计算,于是可以一次性生成几个连续的值用在对应的插入 SQL 语句上,这样就可以提前释放掉 AUTO-INC 锁,可以减少锁等待,提高并发插入效率。
  • 2: 这个表示 interleaved,这种情况下不存在 AUTO-INC 锁,来一个处理一个,批量插入的时候,就有可能出现主键虽然自增,但是不连续的问题。

从上面的介绍中小伙伴们可以看到,实际上第三种,也就是 innodb_autoinc_lock_mode 取值为 2 的情况下,并发效率是最强的,那么我们是不是就应该设置 innodb_autoinc_lock_mode=2 呢?

这得看情况。

松哥之前写过一篇文章和小伙伴们介绍 MySQL binlog 日志文件的三种格式:

  • row:binlog 中记录的是具体的值而不是原始的 SQL,举一个简单例子,假设表中有一个字段是 UUID,用户执行的 SQL 是 insert into user(username,uuid) values('javaboy',uuid()),那么最终记录到 binlog 中的 SQL 是 insert into user(username,uuid) values('javaboy',‘0212cfa0-de06-11ed-a026-0242ac110004’)
  • statement:binlog 中记录的就是原始的 SQL 了,以 row 中的为例,最终 binlog 中记录的就是 insert into user(username,uuid) values('javaboy',uuid())
  • mixed:在这种模式下,MySQL 会根据具体的 SQL 语句来决定日志的形式,也就是在 statement 和 row 之间选择一种。

对于这三种不同的模式,很明显,在主从复制的时候,statement 模式可能会导致主从数据不一致,所以现在 MySQL 默认的 binlog 格式都是 row。

回到我们的问题:

  • 如果 binlog 格式是 row,那么我们就可以设置 innodb_autoinc_lock_mode 的值为 2,这样就能尽最大程度保证数据并发插入的能力,同时不会发生主从数据不一致的问题。
  • 如果 binlog 格式是 statement,那么我们最好设置 innodb_autoinc_lock_mode 的值为 1,这样对于 simple insert 的并发插入能力进行了提高,批量插入还是先获取 AUTO-INC 锁,等插入成功之后再释放,这样也能避免主从数据不一致,保证数据复制的安全性。
  • 以上两点主要是针对 InnoDB 存储引擎,如果是 MyISAM 存储引擎,都是先获取 AUTO-INC 锁,插入完成再释放,相当于 innodb_autoinc_lock_mode 变量的取值对 MyISAM 不生效。

2.3 实践

接下来我们来通过一个简单的 SQL 来和小伙伴们演示一下 innodb_autoinc_lock_mode 不同取值对应不同结果的情况。

首先,我们可以通过如下 SQL 查看当前 innodb_autoinc_lock_mode 的取值:

可以看到,我使用的 8.0.32 这个版本目前默认值是 2。

我先把它改成 0,修改方式就是在 /etc/my.cnf 文件中添加一行 innodb_autoinc_lock_mode=0

改完之后再重启查看,如下:

可以看到,现在就已经改过来了。

现在假设我有如下表:

CREATE TABLE `user` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=100 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

这个自增是从 100 开始计的,现在假设我有如下插入 SQL:

insert into user(id,username) values(1,'javaboy'),(null,'江南一点雨'),(3,'www.javaboy.org'),(null,'lisi');

插入完成之后,我们来看查询结果:

按照我们前文的介绍,这个情况应该是可以解释的通的,我这里不再赘述。

接下来,我把 innodb_autoinc_lock_mode 取值改为 1,如下:

还是上面相同的 SQL,我们再执行一遍。执行完成之后结果也和上文相同。

但是!!!**当上面的 SQL 执行完毕之后,如果我们还想再插入数据,并且新插入的 ID 不指定值,则我们发现自动生成的 ID 值为 104。**这就是因为我们设置了 innodb_autoinc_lock_mode=1,此时,执行 simple insert 插入的时候,系统一看我要插入 4 条记录,就直接给我提前拿了 4 个 ID 出来,分别是 100、101、102 以及 103,结果该 SQL 实际上只用了两个 ID,剩下两个没用,但是下次插入还是从 104 开始了。

3. 小结

好啦,这就是关于主键自增的一个小小知识点,小伙伴们一定要根据实际情况来为 innodb_autoinc_lock_mode 属性取一个合适的值。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/437782.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大数据实战 --- 日志文件

目录 开发环境 数据描述 功能需求 数据准备 分析数据 HBase HIive 统计查询 开发环境 HadoopHiveSparkHBase 启动Hadoop:start-all.sh 启动zookeeper:zkServer.sh start 启动Hive: nohup hiveserver2 1>/dev/null 2>&1 …

Python 图像处理实用指南:1~5

原文:Hands-On Image Processing with Python 协议:CC BY-NC-SA 4.0 译者:飞龙 本文来自【ApacheCN 计算机视觉 译文集】,采用译后编辑(MTPE)流程来尽可能提升效率。 当别人说你没有底线的时候,…

如何恢复回收站中被删除的文件?高效的恢复技巧

一般情况下,我们从电脑上普通删除的文件,会经过回收站(除非文件过大),想要在回收站找回删除的东西,是很简单的,我们只需要打开回收站,找到删除的文件,右键点击并选择还原…

jmeter配置文件

在jmeter安装目录的bin目录下,有多个配置文件 其中最核心的是jmeter.properties jmeter.properties 修改语言:languagezh_CN #简体中文,也可以在GUI页面修改 远程主机配置   # 配置远程主机的 IP,默认为本机。用逗号&q…

反转链表(链表篇)

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。 思路: ①双指针法 ②递归法 如果再定义一个新的链表,实现链表元素的反转,其实这是对内存空间的浪费。 其实只需要改变链表的next指针的指向,…

Ae:摄像机选项

摄像机选项 Camera Options 快捷键:AA 摄像机选项 Camera Options与“摄像机设置”中的参数大同小异且同步变化,额外增加了一些与镜头模糊和散景光斑形状有关的摄像机属性。 请参阅: 《Ae:摄像机设置》 在合成设置中,选…

“不要放过这个春天”解锁品牌春日宣传新玩法

在万物复苏的春天,人们换新装、踏青等需求蓄势待发,出现了全民消费热情高涨的趋势,让品牌「贩卖春天」的宣传此起彼伏。 品牌洞察到用户的消费需求,打造具有品牌特色的浪漫宣传,如采用春日限定元素、创新春天宣传场景…

[算法总结] 关于字符串类型题你应该知道这些?精心汇总!!

😚一个不甘平凡的普通人,致力于为Golang社区和算法学习做出贡献,期待您的关注和认可,陪您一起学习打卡!!!😘😘😘 🤗专栏:算法学习 &am…

50+常用工具函数之xijs更新指南(v1.2.3)

xijs 是一款开箱即用的 js 业务工具库, 聚集于解决业务中遇到的常用的js函数问题, 帮助开发者更高效的进行业务开发. 目前已聚合了50常用工具函数, 接下来就和大家一起分享一下v1.2.3 版本的更新内容. 1. 添加将树结构转换成扁平数组方法 该模块主要由 EasyRo 贡献, 添加内容如…

我用 VSCode 写博客编代码

VSCode(Visual Studio Code)是一款由微软开发且跨平台的免费源代码编辑器。VScode 的扩展功能非常强大,我们可以找到几乎所有开发需要的工具。我用他编辑 Markdown 、php 、html 等,甚至用它集成的终端来完成一些 git 和 linux 命…

一个有意思的404页面

老规矩,先上效果图: 下面代码直接拷过去就能用: <!DOCTYPE html> <html lang="en"> <head>

【Linux】环境变量与进程优先级知识点

目录 环境变量1.基本概念2.常见环境变量3.我们写的程序和命令行指令有什么区别&#xff1f;4.自己的程序为什么要用 ./ 执行&#xff0c;而命令行指令可以直接执行&#xff1f;5.如何追加环境变量&#xff1f;6.Linux如何查看环境变量7.如何在代码层面获取环境变量main函数的参…

Java技术接单介绍

今天给大家介绍一个阶段性&#xff08;周期性&#xff09;能获取一定收益的Java技术接单群&#xff0c;分享给大家&#xff01;主要对搞Java的粉丝有帮助&#xff0c;因为可以赚点小钱&#xff0c;对Java技术的要求不高&#xff01; 那些感兴趣或者想直接加技术群的我给大家讲一…

〖Python网络爬虫实战⑰〗- 网页解析利器parsel实战

订阅&#xff1a;新手可以订阅我的其他专栏。免费阶段订阅量1000 python项目实战 Python编程基础教程系列&#xff08;零基础小白搬砖逆袭) 说明&#xff1a;本专栏持续更新中&#xff0c;目前专栏免费订阅&#xff0c;在转为付费专栏前订阅本专栏的&#xff0c;可以免费订阅付…

奇葩营销之看各品牌如何玩转“营销疯学”

相信有很多人和我一样&#xff0c;最近的快乐都来自于《黑暗荣耀2》。 令人奇怪的是&#xff0c;但比起故事的主线&#xff0c;剧中妍珍等配角的”发疯”片段却成为了网友造梗的来源。 “妍珍疯驴子”“妍珍呐””“黑暗荣耀演我每天精神状态”等。让这部剧话题热度持续…

【LeetCode: 1105. 填充书架 | 暴力递归=>记忆化搜索=>动态规划 | 线性dp 业务限制】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

Nuxt3项目从零开始开发

之前讲过Nuxt3项目新建和配置&#xff1a;《Nuxt3从零开始配置与打包发布_nuxt打包发布_范特西_jay的博客-CSDN博客》 本篇文章主要讲解一些nuxt3的基本功能。 Nuxt3官网&#xff1a;https://nuxt.com/docs Nuxt3的官方库&#xff1a;https://nuxt.com/modules 常用的库有&a…

Camtasia Studio2023mac电脑最新屏幕录制编辑工具

作为一个自媒体人&#xff0c;每天都要录制编辑视频&#xff0c;选择一个好的视频编辑工具就是大家首先面临的一个难题&#xff0c;选择一个好工具&#xff0c;可以起到事半功倍的效果&#xff0c;大大提高工作效率。视频编辑非常多&#xff0c;今天给大家推荐的是一款功能强大…

最经典的电脑病毒,适合练手,大家一定要增强安全防范意识

资源地址&#xff1a; 最经典的电脑病毒&#xff0c;坑死自己和别人 大部分都是恶作剧病毒&#xff0c;适合小白练手 感想&#xff1a;病毒种类多种多样&#xff0c;要搞清楚原理&#xff0c;增强安全防范意识啊 示意图&#xff1a; 病毒原理 熊猫烧香病毒会删除扩展名为g…

maven的下载和配置

目录 一、配置核心程序 1、MAVEN下载 2、指定本地仓库 3、配置阿里云提供的镜像仓库 4、配置Maven工程的基础JDK版本 二、配置环境变量 1、检查JAVA_HOME配置是否正确 2、配置MAVEN_HOME 3、配置PATH 4、验证 一、配置核心程序 1、MAVEN下载 官网&#xff1a;https:…