iis服务器助手广告广告
返回顶部
首页 > 资讯 > 数据库 >MySql主键id不推荐使用UUID的原因分析
  • 626
分享到

MySql主键id不推荐使用UUID的原因分析

摘要

目录前言规范原因前言 昨天在某个技术群中,有个老哥发送了一个技术视频:讲的是一个毕业生面试被问,前后端的交互ID是使用自增的吗?为什么不使用UUID?最后的解释是说性能问题,这个引起了我的兴趣,查了一下资料总结一下。 规

前言

昨天在某个技术群中,有个老哥发送了一个技术视频:讲的是一个毕业生面试被问,前后端的交互ID是使用自增的吗?为什么不使用UUID?最后的解释是说性能问题,这个引起了我的兴趣,查了一下资料总结一下。

规范

在《阿里巴巴 Java 开发手册》第五章 Mysql 规定第九条中,强制规定了单表的主键 id 必须为无符号的 bigint 类型,且是自增的。

MySql主键id不推荐使用UUID的原因分析

mysql开发规范中经常可以看到:

推荐使用int,bigint 无符号做自增键

禁止使用uuid做主键

关于主键的类型选择上最常见的争论是用整型还是字符型的问题,关于这个问题《高性能Mysql》一书中有明确论断:

整数通常是标识列的最好选择,因为它很快且可以使用AUTO_INCREAMENT,如果可能,应该避免使用字符串类型作为标识列,因为很消耗空间,且通常比数字类型慢。

如果是使用MyISAM,则就更不能用字符型,因为MyISAM默认会对字符型采用压缩引擎,从而导致查询变得非常慢。

原因

通常主键 id 的数据类型有两种选择:字符串或者整数,主键通常要求是唯一的,如果使用字符串类型,我们可以选择 UUID 或者具有业务含义的字符串来作为主键。

对于 UUID 而言,它由 32 个字符+4 个’-'组成,长度为 36,虽然 UUID 能保证唯一性,但是它有两个致命的缺点:

1.不是递增的。MySQL 中索引数据结构是 B+Tree,这种数据结构的特点是索引树上的节点的数据是有序的,而如果使用 UUID 作为主键,那么每次插入数据时,因为无法保证每次产生的 UUID 有序,所以就会出现新的 UUID 需要插入到索引树的中间去,这样可能会频繁地导致页分裂,使性能下降。

2.太占用内存。每个 UUID 由 36 个字符组成,在字符串进行比较时,需要从前往后比较,字符串越长,性能越差。另外字符串越长,占用的内存越大,由于页的大小是固定的,这样一个页上能存放的关键字数量就会越少,这样最终就会导致索引树的高度越大,在索引搜索的时候,发生的磁盘 io 次数越多,性能越差。

对于整数的数字类型,MySQL 中主要有 int 和 bigint 类型。其中 int 占用 4 个字节,bigint 占用 8 个字节,这和 Java 中的 int 和 long 对应。如果使用无符号的 int 类型作为主键,那么主键的最大值为 2^32-1,即 4294967295,这个值不到 43 亿,似乎有点太小了。虽然一张表的数据,我们不可能让其达到 43 亿条(太大会影响性能),但是对于频繁进行插入、删除的表来说,43 亿这个值是可以达到的。而如果使用无符号的 bigint 类型的话,主键的最大值可以达到 2^64-1,这个数足够大了,如果以每秒插入 100 万条数据计算的,58 万年以后才能达到最大值。所以 bigint 作为主键的数据类型,完全不用担心超过最大值的问题。

而强制要求主键 id 是自增的,则是为了在数据插入的过程中,尽可能的避免索引树上页分裂的问题。

关于主键是聚簇索引,如果没有主键,InnoDB会选择一个唯一键来作为聚簇索引,如果没有唯一键,会生成一个隐式的主键。

隐式主键:

InnoDB会自动帮你创建一个不可见的、长度为6字节的row_id,而且InnoDB维护了一个全局的dictsys.row_id,所有未定义主键的表都会共享该row_id,每次插入一条数据都把全局row_id当成主键id,然后全局row_id加1。

该全局row_id在代码实现上使用的是bigint unsigned类型,但实际上只给row_id保留了6字节,所以这种设计就会存在一个问题:如果全局row_id一直涨,直到2的48次幂-1时,这个时候再加1,row_id的低48位都会变为0,如果再插入新一行数据时,拿到的row_id就为0,这样的话就存在主键冲突的可能,所以为了避免这种隐患,每个表都需要一个主键。

详解-重点:

MySql主键id不推荐使用UUID的原因分析

InnoDB引擎使用聚集索引,数据记录本身被存于主索引(一颗B+Tree)的叶子节点上。这就要求同一个叶子节点内(大小为一个内存页或磁盘页)的各条数据记录按主键顺序存放,因此每当有一条新的记录插入时,MySQL 会根据其主键将其插入适当的节点和位置,如果页面达到装载因子(InnoDB默认为15/16),则开辟一个新的页(节点)

所以在使用innoDB表时要避免随机的(不连续且值的分布范围非常大)聚簇索引,特别是针对I/O密集型的应用。例如:从性能角度考虑,使用UUID的方案就会导致聚簇索引的插入变得完全随机。

理论总结:

自增的主键的值是顺序的,所以 Innodb 把每一条记录都存储在一条记录的后面。

当达到页面的最大填充因子时候 ( innodb默认的最大填充因子是页大小的15/16,会留出1/16的空间留作以后的修改):

1)下一条记录就会写入新的页中,一旦数据按照这种顺序的方式加载,主键页就会近乎于顺序的记录填满,提升了页面的最大填充率,不会有页的浪费

2)新插入的行一定会在原有的最大数据行下一行,mysql定位和寻址很快,不会为计算新行的位置而做出额外的消耗

3)减少了页分裂和碎片的产生

选择 主键id:

MySql主键id不推荐使用UUID的原因分析

tinyint、smallint、mediumint,这三个不常用就不说了。无符号是设置了 unsigned 属性,表示不允许负值,这大致可以使正数的上限提高一倍。

以无符号int类型为例,42亿虽然看起来是个很大的数字,但是对于一些插入删除很频繁的业务来说,并非无法触达这个上限。特别是有的业务表设置的步长比较大,会导致id自增的速度更快。如果你的业务预期会产生很多数据,那么建议你在创建表时,直接使用bigint。

因为MySQL的主键策略:id自增值达到上限以后,再申请下一个 id 时,仍然是最大值。

如果bigint真的还不够使用的话,我们可以使用雪花算法生成的id做主键,由于其也是大致递增的,对性能也不会产生影响,只需要由bigint改成更大范围的decimal就行。

UUID:

一:使用场景

UUID是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。在UUID的算法中,可能会用到诸如网卡Mac地址,IP,主机名,进程ID等信息以保证其独立性

二:有的开发就是喜欢使用UUID怎么办?

所以MySQL8.0也是顺应时代潮流,担负时代的革命重任,MySQL8.0也对uuid的存储做了进一步的提升。整体上看MySQL8.0现在的重点方向也是对开发的友好度支持上。

结论:

在MySQL8.0中还是推荐使用无符号的int, bigint做主键,如果要使用uuid可以建一个唯一索引

MySQL和Java两者默认生成的uuid是version 1格式:datetime|mac地址,因为高低位顺序乱了,造成顺序乱掉,可以使用MySQL的函数uuid_to_bin(@uuid,1) , bin_to_uuid(@uuid,1)进行调整转换,实现有序化

对于使用uuid_to_bin转化后的uuid存储,使用binary(16)或是varbinary(16)替代varchar(36),从而实现从36byte降到16byte。

这个技巧不是万能的,如果你的数据库CPU是瓶颈,使用转化存储,可能带来CPU上更重的开销,反之,如果你的IO是瓶颈,但CPU有较大的空闲,使用这个技巧就是一个不错的优化方案。如果不好把握,就用你可以用得到的最好硬件就可以了,一般情况下如果用上SSD后IO都没啥问题,但也可以使用这个技术去降低表的物理大小。

实战:

环境准备

在MySQL 5.7中分别创建三张数据表:

test_varchar:以UUID作为主键。

test_long:以bigint作为主键。

test_int:以int作为主键。

三个表的字段,除了主键ID 分别采用varchar,bigint 和自动增长int不同外,其他三个字段都为 varchar 36位

另外,建表时使用InnoDB存储引擎,并且向数据库中插入100W条数据,用以测试

压测信息

表类型:InnoDB

数据量:100W条

数据库:

在这里插入图片描述

主键采用uuid 32位

运行查询语句1:
SELECT COUNT(id) FROM test_varchar;

运行查询语句2:
SELECT * FROM test_varchar WHERE vname='71e88bab-2f0f-6811-89ff-4cc935c075d8';

运行查询语句3:

SELECT * FROM test_varchar WHERE id='00004599b05211e196aa002655b28d7b';

三条查询语句的耗时分别如下所示:

语句1消耗时间平均为:2.81秒;

语句2消耗时间平均为:3.11秒;

语句3消耗时间平均为:0秒;(多方测试,条件里只要有主键ID,查询速度毫秒级都显示000。测试的ID值,有前一百条的,也有后90多万条的。查询时间完全一样,毫秒级都为000)

主键采用bigint

主键采用bigint,使用uuid_short()产生数据,数据为有序列的纯数字(22461015967875697)。(其相当于自动增长,只是固定的基数值较大而已。)

运行查询语句1:

SELECT COUNT(id) FROM test_long;

运行查询语句2:

SELECT * FROM test_long WHERE vname='63b10f80-0e20-28cc-3078-d7331ba410b6';

运行查询语句3:

SELECT * FROM test_long WHERE id='22461015967875702';

三条查询语句的耗时分别如下所示:

语句1消耗时间平均为:1.31秒;

语句2消耗时间平均为:1.51秒;

语句3消耗时间平均为:0秒;(多方测试,条件里只要有主键ID,查询速度毫秒级都显示000。测试的ID值,有前一百条的,也有后90多万条的。查询时间完全一样,毫秒级都为000)

主键采用自增int

运行查询语句1:

SELECT COUNT(id) FROM test_int;

运行查询语句2:

SELECT * FROM test_int WHERE vname='908b57a5-cdef-32d1-0320-e14209b08894';

运行查询语句3:

SELECT * FROM test_int WHERE id=900002;

其中,主键采用mysql自带的自动增长,数据为纯数字(1,2,3,4,5……)。

三条查询语句的耗时分别如下所示:

查询语句1消耗时间平均为:1.20秒;

查询语句2消耗时间平均为:1.41秒;

查询语句3消耗时间平均为:0秒;(多方测试,条件里只要有主键ID,查询速度毫秒级都显示000。测试的ID值,有前一百条的,也有后90多万条的。查询时间完全一样,毫秒级都为000)

新增:

UUID做主键,其他字段相同,插入100万条数据,用了2.5个小时
自增主键,其他字段相同,插入相同的100万条数据,用了26分钟

总结:由此可见,MySQL InnoDB 主键采用自动增长性能较高,但是在技术工作中,能否直接使用自增int类型的数字作为MySQL的主键,大家需要根据具体需求确定。

如果你设计的系统,数据量还没有超过100W,你用啥主键类型都无所谓。我测试电脑是台式机,如果是专业的服务器,估计100W条,mysql MyISAM 的这些测试,根本都测不出来时间差吧。

到此这篇关于MySql主键id不推荐使用UUID的文章就介绍到这了,更多相关MySql主键id不推荐使用UUID内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

您可能感兴趣的文档:

--结束END--

本文标题: MySql主键id不推荐使用UUID的原因分析

本文链接: https://www.lsjlt.com/news/198579.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • MySql主键id不推荐使用UUID的原因分析
    目录前言规范原因前言 昨天在某个技术群中,有个老哥发送了一个技术视频:讲的是一个毕业生面试被问,前后端的交互ID是使用自增的吗?为什么不使用UUID?最后的解释是说性能问题,这个引起了我的兴趣,查了一下资料总结一下。 规...
    99+
    2023-03-06
    MySql主键id不推荐使用UUID MySql主键id MySql主键不推荐使用UUID
  • MySql主键id不推荐使用UUID的原因是什么
    本文小编为大家详细介绍“MySql主键id不推荐使用UUID的原因是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“MySql主键id不推荐使用UUID的原因是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧...
    99+
    2023-07-05
  • 【MySQL】mysql中不推荐使用uuid或者雪花id作为主键的原因以及差异化对比
    文章目录 前言什么是UUID什么是雪花ID什么是MySql自增ID优缺点对比UUID:优点1.全球唯一性2.无需数据库支持 缺点1.存储空间大2.索引效率低3.查询效率低 雪花ID:优点1.分布式环...
    99+
    2023-12-22
    mysql 数据库
  • Mysql分析设计表主键为何不用uuid
    目录一、mysql和程序实例1.1 建表1.2 测试1.3 程序写入结果1.4 效率测试结果二、使用uuid和自增id的索引结构对比2.1 使用自增id的内部结构2.2 使用uuid...
    99+
    2024-04-02
  • 推荐使用For-Each而不是For循环遍历元素的原因分析
    这篇文章主要介绍了推荐使用For-Each而不是For循环遍历元素的原因分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。一、for循环的缺...
    99+
    2024-04-02
  • 浅析MySQL 主键使用数字还是uuid查询快
    在实际开发中mysql的主键不能重复,可能会采用主键自增,为了防止主键重复也可能会采取雪花算法之类的算法保证,这两种主键保存的都是number类型 但是实际开发中可能会生成uuid作...
    99+
    2024-04-02
  • Vue中不推荐用index做key的原因有哪些
    这篇文章主要介绍了Vue中不推荐用index做key的原因有哪些,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。前端开发中,只要涉及到列表渲染,那么无论是 React 还是 V...
    99+
    2023-06-21
  • css中不推荐使用行内样式的示例分析
    这篇文章给大家分享的是有关css中不推荐使用行内样式的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。css不推荐使用行内样式,即直接将样式属性写在HTML标签的style属性中。行内样式的缺点:1、结构样...
    99+
    2023-06-14
  • MySQL分区表中分区键必须是主键一部分的原因是什么
    这篇文章主要介绍了MySQL分区表中分区键必须是主键一部分的原因是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇MySQL分区表中分区键必须是主键一部分的原因是什么文章都会有所收获,下面我们一起来看看吧。前...
    99+
    2023-06-29
  • Python使用pytest-playwright的原因分析
    目录1 用playwright能不能不用这个包?2 安装3 代码和文档4 示例代码5 结论pytest-playwright 是一个 Python 包,它允许您使用 Microsof...
    99+
    2023-03-02
    python使用pytest-playwright 使用pytest playwright
  • React中的setState使用细节和原理解析(最新推荐)
    目录setState使用详解使用setState的原因setState的基本用法setState的异步更新setState获取异步结果setState使用详解 前面我们有使用过set...
    99+
    2022-12-19
    React中setState使用原理 React中setState使用
  • MySQL Aborted_connects值不断增大的可能性原因分析
    这篇文章给大家介绍MySQL Aborted_connects值不断增大的可能性原因分析,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。 最近登录数据库,查...
    99+
    2024-04-02
  • 大白话讲解调用Redis的increment失败原因及推荐使用详解
    大家在项目中基本都会接触到redis,在spring-data-redis-2.*.*.RELEASE.jar中提供了两个Helper class,可以让我们更方便的操作redis中...
    99+
    2024-04-02
  • 用SQLite和FMDB而不用Core Data的原因分析
    这篇文章主要讲解了“用SQLite和FMDB而不用Core Data的原因分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“用SQLite和FMDB而不用C...
    99+
    2024-04-02
  • vue使用fengMap速度慢的原因分析
    目录使用fengMap速度慢原因vue在使用中的一些小技巧1. 多图表resize事件去中心化2. 全局过滤器注册3. 全局组件注册4. 不同路由的组件复用5. 高阶组件6. 路由根...
    99+
    2024-04-02
  • 在MyBatis中使用MySQL如何实现返回插入的主键ID
    这篇文章给大家介绍在MyBatis中使用MySQL如何实现返回插入的主键ID,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。需求:使用MyBatis往MySQL数据库中插入一条记录后,需要返回该条记录的自增主键值。方法:...
    99+
    2023-05-31
    mybatis 主键id
  • CSS不要随意使用Id命名的示例分析
    这篇文章给大家分享的是有关CSS不要随意使用Id命名的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。 在DIV CSS机关时,CSS代码不能率性应用ID定名,率性滥用c...
    99+
    2024-04-02
  • @Transactional注解不起作用的原因分析及解决
    目录Transactional失效场景介绍第一种第二种第三种@Transactional注解不起作用原理分析第一种不创建代理对象不进行代理调用第二种第三种Transactional失...
    99+
    2024-04-02
  • MySQL中不使用Text类型的原因是什么
    MySQL中不使用Text类型的原因是什么,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。众所周知,MySQL广泛应用于互联网的OLTP(联机事...
    99+
    2024-04-02
  • MySQL中不建议使用SELECT *的原因是什么
    本篇内容介绍了“MySQL中不建议使用SELECT *的原因是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!“不要使用...
    99+
    2023-06-29
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作