0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

主键不用随机字符串用什么?主键自增?

dyquk4xk2p3d 来源:江南一点雨 2023-05-09 09:04 次阅读

主键不用随机字符串用什么?主键自增?主键自增就是最佳方案吗?有没有其他坑?今天我们就来讨论下这个话题

1. 为什么不用 UUID

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

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

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

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

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

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

2. 主键自增的问题

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

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

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

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

2.1 数据插入的三种形式

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

insert into user(name) values('javaboy')或者replace into user(name) values('javaboy'),这种没有嵌套子查询并且能够确定具体插入多少行的插入叫做simple insert,不过需要注意的是INSERT ... ON DUPLICATE KEY UPDATE不算是simple insert。

load data或者insert into user select ... from ....,这种都是批量插入,叫做bulk insert,这种批量插入有一个特点就是插入多少条数据在一开始是未知的。

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 的取值:

97387408-edf9-11ed-90ce-dac502259ad0.png

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

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

9747ca0c-edf9-11ed-90ce-dac502259ad0.png

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

976346d8-edf9-11ed-90ce-dac502259ad0.png

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

现在假设我有如下表:

CREATETABLE`user`(
`id`intunsignedNOTNULLAUTO_INCREMENT,
`username`varchar(255)CHARACTERSETutf8mb4COLLATEutf8mb4_unicode_ciDEFAULTNULL,
PRIMARYKEY(`id`)
)ENGINE=InnoDBAUTO_INCREMENT=100DEFAULTCHARSET=utf8mb4COLLATE=utf8mb4_unicode_ci;

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

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

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

97767c9e-edf9-11ed-90ce-dac502259ad0.png

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

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

978a2352-edf9-11ed-90ce-dac502259ad0.png

还是上面相同的 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 属性取一个合适的值。‍

‍‍‍‍‍‍‍‍



审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • SQL
    SQL
    +关注

    关注

    1

    文章

    738

    浏览量

    43468
  • MySQL
    +关注

    关注

    1

    文章

    776

    浏览量

    26012
  • UUID
    +关注

    关注

    0

    文章

    22

    浏览量

    8029
  • INC
    INC
    +关注

    关注

    0

    文章

    11

    浏览量

    5799

原文标题:MySQL 主键自增,那些奇奇怪怪的坑

文章出处:【微信号:良许Linux,微信公众号:良许Linux】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    连接字符串有什么

    连接字符串有什么
    发表于 10-10 20:59

    while循环搜字符串,当没搜到需要的字符串,如何退出循环

    如图,while循环搜字符串,当没搜到需要的字符串,如何退出循环,并且能够重新进入for循环,循环次数重新变为20次
    发表于 12-18 16:54

    如何设置字符串中的字符根据不同条件显示不同颜色?如何将不同颜色的字符串合并显示后保留字符串原来的颜色?

    字符串原来的颜色?我连接字符串函数把两个不同颜色的字符串连接起来后,字符的颜色会恢复黑色,这个如何解决?
    发表于 09-25 00:35

    labview一字符串输入(包含数字)如何变成输出纯字母类的新字符串

    想问一下,labview一字符串输入(包含数字)如何变成输出纯字母类的新字符串 我想用公式节点去做,但要先把原字符串转换成数组吗?还是可
    发表于 10-19 00:23

    阿里云mysql数据库怎么设置主键和时间格式怎么显示时分秒?

    `需要将测试的数据保存到阿里云mysql数据库上,利用NI的数据库工具包怎么创建表实现主键?还有保存的时间数据只显示日期,不显示时分秒(DB Tools Format Datet
    发表于 11-13 10:39

    labview中已知4个字符串(每个字符串包含一个字符)如何随机排列?

    labview中已知4个字符串(每个字符串包含一个字符)如何随机排列?求大神给个程框图。
    发表于 01-22 15:43

    labview向oracle插入数据,怎样可以主键1?如果不插入主键的字段,会报插入的数目与表中的数据不相等

    本帖最后由 电子人steve 于 2018-5-23 20:30 编辑 labview向oracle数据库插入数据时,怎样可以主键1啊,服务器数据库表中设置了一个主键,如果不
    发表于 05-23 18:53

    请问labview怎么随机删除一组字符串

    labview怎么实现随机删除其中一行字符串
    发表于 11-26 13:08

    串行字符串随机字符结尾

    嗨,GuySi正在尝试把一系列字符串传送到Python,所有的值都显示得很完美,但是它们都以结尾的随机字符结尾,每当我重新启动MPLAB X IDE时,我得到新的字符串
    发表于 01-25 14:09

    字符串拆分

    串口连续传来如是字符串”V-12.98C0.18“希望把字符串拆分为V后的为一组数,C后的为一组数。上述字符串应该拆为-12.98和0.18。我尝试了匹配模式匹配中间的空格分成两个
    发表于 05-22 11:57

    聊聊字符串

    大家好,我是惊觉,今天聊聊字符串字符串的使用场景非常之多,人机交互和双机通信都会用到。比如:通过串口向单片机发送指令,以执行操作或配置参数。单片机读取传感器数据,数据格式是字符串。一般GPS数据
    发表于 02-28 06:52

    关于MySQL的基础知识简析

    录入客户订单新建一个数据库新建Vendors表存储卖产品的供应商。先切换到该数据库NOT NULL表示值不能为空char(10) 保存固定长度的字符串,这里指定10个字符串vend_id 主键
    发表于 11-03 11:50

    MySQL自增主键一定是连续的吗?

    众所周知,自增主键可以让聚集索引尽量地保持递增顺序插入,避免了随机查询,从而提高了查询效率
    的头像 发表于 02-20 18:06 451次阅读

    MySQL自增主键一定是连续的吗?

    如果你的业务设计依赖于自增主键的连续性,这个设计假设自增主键是连续的。但实际上,这样的假设是错的,因为自增主键不能保证连续递增。
    的头像 发表于 03-21 16:55 397次阅读

    MySQL自增主键一定是连续的吗?

    如果你的业务设计依赖于自增主键的连续性,这个设计假设自增主键是连续的。但实际上,这样的假设是错的,因为自增主键不能保证连续递增。
    的头像 发表于 06-11 11:35 352次阅读
    MySQL自增<b class='flag-5'>主键</b>一定是连续的吗?