网易首页 > 网易号 > 正文 申请入驻

公司新来一个技术总监:禁止将 UUID 和雪花 ID 列入主键选型!

0
分享至

钓友宝 (微信小程序):一款专门为 钓友 开发的 免费的 分享钓点地图与实时天气的软件,地图中标记了所有野钓、钓场、公共水域等的精确位置,支持导航、 预测钓鱼位置的鱼情 等功能。
前言

在 mysql 中设计表的时候,mysql 官方推荐不要使用 uuid 或者不连续不重复的雪花 id(long 形且唯一,单机递增),而是推荐连续自增的主键 id,官方的推荐是 auto_increment,那么为什么不建议采用 uuid,使用 uuid 究竟有什么坏处?

一、mysql 和程序实例

1.1.要说明这个问题,我们首先来建立三张表

分别是 user_auto_key,user_uuid,user_random_key,分别表示自动增长的主键,uuid 作为主键,随机 key 作为主键,其它我们完全保持不变。

根据控制变量法,我们只把每个表的主键使用不同的策略生成,而其他的字段完全一样,然后测试一下表的插入速度和查询速度:

注:这里的随机 key 其实是指用雪花算法算出来的前后不连续不重复无规律的 id:一串 18 位长度的 long 值

id 自动生成表:

用户 uuid 表:

随机主键表:

1.2.光有理论不行,直接上程序,使用 spring 的 jdbcTemplate 来实现增查测试:

技术框架:springboot+jdbcTemplate+junit+hutool,程序的原理就是连接自己的测试数据库,然后在相同的环境下写入同等数量的数据,来分析一下 insert 插入的时间来进行综合其效率,为了做到最真实的效果,所有的数据采用随机生成,比如名字、邮箱、地址都是随机生成。

package com.wyq.mysqldemo; import cn.hutool.core.collection.CollectionUtil; import com.wyq.mysqldemo.databaseobject.UserKeyAuto; import com.wyq.mysqldemo.databaseobject.UserKeyRandom; import com.wyq.mysqldemo.databaseobject.UserKeyUUID; import com.wyq.mysqldemo.diffkeytest.AutoKeyTableService; import com.wyq.mysqldemo.diffkeytest.RandomKeyTableService; import com.wyq.mysqldemo.diffkeytest.UUIDKeyTableService; import com.wyq.mysqldemo.util.JdbcTemplateService; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.util.StopWatch; import java.util.List; @SpringBootTest classMysqlDemoApplicationTests{     @Autowired     private JdbcTemplateService jdbcTemplateService;     @Autowired     private AutoKeyTableService autoKeyTableService;     @Autowired     private UUIDKeyTableService uuidKeyTableService;     @Autowired     private RandomKeyTableService randomKeyTableService;     @Test     voidtestDBTime(){         StopWatch stopwatch = new StopWatch("执行sql时间消耗");         /**          * auto_increment key任务          */         final String insertSql = "INSERT INTO user_key_auto(user_id,user_name,sex,address,city,email,state) VALUES(?,?,?,?,?,?,?)";         List insertData = autoKeyTableService.getInsertData();         stopwatch.start("自动生成key表任务开始");         long start1 = System.currentTimeMillis();         if (CollectionUtil.isNotEmpty(insertData)) {             boolean insertResult = jdbcTemplateService.insert(insertSql, insertData, false);             System.out.println(insertResult);         }         long end1 = System.currentTimeMillis();         System.out.println("auto key消耗的时间:" + (end1 - start1));         stopwatch.stop();         /**          * uudID的key          */         final String insertSql2 = "INSERT INTO user_uuid(id,user_id,user_name,sex,address,city,email,state) VALUES(?,?,?,?,?,?,?,?)";         List insertData2 = uuidKeyTableService.getInsertData();         stopwatch.start("UUID的key表任务开始");         long begin = System.currentTimeMillis();         if (CollectionUtil.isNotEmpty(insertData)) {             boolean insertResult = jdbcTemplateService.insert(insertSql2, insertData2, true);             System.out.println(insertResult);         }         long over = System.currentTimeMillis();         System.out.println("UUID key消耗的时间:" + (over - begin));         stopwatch.stop();         /**          * 随机的long值key          */         final String insertSql3 = "INSERT INTO user_random_key(id,user_id,user_name,sex,address,city,email,state) VALUES(?,?,?,?,?,?,?,?)";         List insertData3 = randomKeyTableService.getInsertData();         stopwatch.start("随机的long值key表任务开始");         Long start = System.currentTimeMillis();         if (CollectionUtil.isNotEmpty(insertData)) {             boolean insertResult = jdbcTemplateService.insert(insertSql3, insertData3, true);             System.out.println(insertResult);         }         Long end = System.currentTimeMillis();         System.out.println("随机key任务消耗时间:" + (end - start));         stopwatch.stop();         String result = stopwatch.prettyPrint();         System.out.println(result);     }

1.3.程序写入结果

user_key_auto 写入结果:

user_random_key 写入结果:

user_uuid 表写入结果:

1.4.效率测试结果

在已有数据量为 130W 的时候:我们再来测试一下插入 10w 数据,看看会有什么结果:

可以看出在数据量 100W 左右的时候,uuid 的插入效率垫底,并且在后序增加了 130W 的数据,uudi 的时间又直线下降。

时间占用量总体可以得出的效率排名为:auto_key>random_key>uuid,uuid 的效率最低,在数据量较大的情况下,效率直线下滑。那么为什么会出现这样的现象呢?带着疑问,我们来探讨一下这个问题。

二、使用 uuid 和自增 id 的索引结构对比

2.1.使用自增 id 的内部结构

自增的主键的值是顺序的,所以 Innodb 把每一条记录都存储在一条记录的后面。当达到页面的最大填充因子时候(innodb 默认的最大填充因子是页大小的 15/16,会留出 1/16 的空间留作以后的修改):

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

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

  • 减少了页分裂和碎片的产生。

2.2.使用 uuid 的索引内部结构

因为 uuid 相对顺序的自增 id 来说是毫无规律可言的,新行的值不一定要比之前的主键的值要大,所以 innodb 无法做到总是把新行插入到索引的最后,而是需要为新行寻找新的合适的位置从而来分配新的空间。

这个过程需要做很多额外的操作,数据的毫无顺序会导致数据分布散乱,将会导致以下的问题:

写入的目标页很可能已经刷新到磁盘上并且从缓存上移除,或者还没有被加载到缓存中,innodb 在插入之前不得不先找到并从磁盘读取目标页到内存中,这将导致大量的随机 IO

因为写入是乱序的,innodb 不得不频繁的做页分裂操作,以便为新的行分配空间,页分裂导致移动大量的数据,一次插入最少需要修改三个页以上

由于频繁的页分裂,页会变得稀疏并被不规则的填充,最终会导致数据会有碎片

在把随机值(uuid 和雪花 id)载入到聚簇索引(innodb 默认的索引类型)以后,有时候会需要做一次 OPTIMEIZE TABLE 来重建表并优化页的填充,这将又需要一定的时间消耗。

结论:使用 innodb 应该尽可能的按主键的自增顺序插入,并且尽可能使用单调的增加的聚簇键的值来插入新行

2.3.使用自增 id 的缺点

那么使用自增的 id 就完全没有坏处了吗?并不是,自增 id 也会存在以下几点问题:

别人一旦爬取你的数据库,就可以根据数据库的自增 id 获取到你的业务增长信息,很容易分析出你的经营情况

对于高并发的负载,innodb 在按主键进行插入的时候会造成明显的锁争用,主键的上界会成为争抢的热点,因为所有的插入都发生在这里,并发插入会导致间隙锁竞争

Auto_Increment 锁机制会造成自增锁的抢夺,有一定的性能损失。

附:Auto_increment 的锁争抢问题,如果要改善需要调优 innodb_autoinc_lock_mode 的配置
三、总结

本篇博客首先从开篇的提出问题,建表到使用 jdbcTemplate 去测试不同 id 的生成策略在大数据量的数据插入表现,然后分析了 id 的机制不同在 mysql 的索引结构以及优缺点,深入的解释了为何 uuid 和随机不重复 id 在数据插入中的性能损耗,详细的解释了这个问题。

在实际的开发中还是根据 mysql 的官方推荐最好使用自增 id,mysql 博大精深,内部还有很多值得优化的点需要我们学习。

附:本篇博客 demo 地址:https://gitee.com/Yrion/mysqlIdDemo

Java精选面试题 (微信小程序):5000+道面试题和选择题,包含Java基础、MQ、Redis、SpringBoot、Elasticsearch、Docker、K8s、Flink、Spark、架构设计、大厂真题等,在线随时刷题!
来源:https://www.cnblogs.com/wyq178/p/12548864.html

公众号“Java精选”所发表内容注明来源的,版权归原出处所有(无法查证版权的或者未注明出处的均来自网络,系转载,转载的目的在于传递更多信息,版权属于原作者。如有侵权,请联系,笔者会第一时间删除处理!

最近有很多人问,有没有读者或者摸鱼交流群!加入方式很简单,公众号Java精选,回复“加群”,即可入群!

文章有帮助的话,点在看,转发吧!

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
“大不了给我一颗子弹,我就是要扎死她”,24岁男子新婚两月杀妻

“大不了给我一颗子弹,我就是要扎死她”,24岁男子新婚两月杀妻

易玄
2026-06-21 09:27:52
央视曝光!多款水果跌落神坛,滥用甜味剂8000倍甜度、违规防腐剂

央视曝光!多款水果跌落神坛,滥用甜味剂8000倍甜度、违规防腐剂

陈博世财经
2026-06-21 16:42:08
四种青训模式全部走死后!终于明白:中国缺的不是踢球的人

四种青训模式全部走死后!终于明白:中国缺的不是踢球的人

云景侃记
2026-06-18 09:32:18
朱之文彻底不装了,曝料儿子离婚隐情,与儿媳生孩子传闻真相大白

朱之文彻底不装了,曝料儿子离婚隐情,与儿媳生孩子传闻真相大白

萧狡科普解说
2026-06-22 00:34:53
日本队是亚洲之光?4:0仍有可能出局,F组堪称死亡之组

日本队是亚洲之光?4:0仍有可能出局,F组堪称死亡之组

蜜桔娱乐
2026-06-21 15:47:10
波兰总统撤销授予泽连斯基的“白鹰勋章” 泽连斯基发文:已邮寄退还

波兰总统撤销授予泽连斯基的“白鹰勋章” 泽连斯基发文:已邮寄退还

财联社
2026-06-21 10:22:10
阿尔特塔卸磨杀驴!阿森纳刚夺冠就甩功臣,8000 万抢世界杯神锋

阿尔特塔卸磨杀驴!阿森纳刚夺冠就甩功臣,8000 万抢世界杯神锋

澜归序
2026-06-22 06:21:27
罗永浩误吃安眠药被挤上热搜:尝试催吐没成功 原地拼命蹦跳也不行

罗永浩误吃安眠药被挤上热搜:尝试催吐没成功 原地拼命蹦跳也不行

快科技
2026-06-21 23:01:07
刚断了我们一半的锰矿进口,加蓬就笑眯眯找上门:

刚断了我们一半的锰矿进口,加蓬就笑眯眯找上门:

小马姨
2026-06-21 19:30:09
1457万枚烂在银行!武夷山纪念币创下近十年最尴尬纪录

1457万枚烂在银行!武夷山纪念币创下近十年最尴尬纪录

老孟谈钱
2026-06-19 03:17:17
日本队大胜却笑不出来,淘汰赛将遇“魔鬼赛程”,进16强都难!

日本队大胜却笑不出来,淘汰赛将遇“魔鬼赛程”,进16强都难!

绿茵舞着
2026-06-21 22:43:51
世界杯头号漏勺!24 分钟丢 3 球还进乌龙 全队被他坑惨了

世界杯头号漏勺!24 分钟丢 3 球还进乌龙 全队被他坑惨了

澜归序
2026-06-22 04:15:54
唐斯未婚妻透露并未遵守尼克斯老板规定:季后赛开始后10周要禁欲

唐斯未婚妻透露并未遵守尼克斯老板规定:季后赛开始后10周要禁欲

Emily说个球
2026-06-22 06:00:43
巴西女排爆冷输球,全胜战绩终结,日本队两连败,泰国无缘三连胜

巴西女排爆冷输球,全胜战绩终结,日本队两连败,泰国无缘三连胜

跑者排球视角
2026-06-21 23:57:00
喊了几十年节约用水,为何突然没声?原来中国水危机是这样翻盘的

喊了几十年节约用水,为何突然没声?原来中国水危机是这样翻盘的

混沌录
2026-06-21 21:39:13
多名院士呼吁快停止食用,吃一口等于14斤塑料袋,女子因肾衰走了

多名院士呼吁快停止食用,吃一口等于14斤塑料袋,女子因肾衰走了

路医生健康科普
2026-06-20 17:05:07
104岁澳洲科学家没病也要安乐死,嫌活太久、摔地两天没人知,临终吐槽这过程太长了

104岁澳洲科学家没病也要安乐死,嫌活太久、摔地两天没人知,临终吐槽这过程太长了

童童聊娱乐啊
2026-06-20 01:41:02
特斯拉FSD入华:6.4万买断还是700月租?算完账我醒了!

特斯拉FSD入华:6.4万买断还是700月租?算完账我醒了!

生活魔术专家
2026-06-22 04:43:59
祝贺!上海运动员覃海洋夺冠

祝贺!上海运动员覃海洋夺冠

上海体育
2026-06-21 21:26:55
马宁56分钟出4黄!外网怒批:瞎吹 媒体人:FIFA不会再给他机会了

马宁56分钟出4黄!外网怒批:瞎吹 媒体人:FIFA不会再给他机会了

风过乡
2026-06-21 09:36:50
2026-06-22 08:28:49
Java精选
Java精选
一场永远也演不完的戏
1794文章数 3859关注度
往期回顾 全部

科技要闻

马斯克拿下7800亿元天价薪酬 2028年可兑现

头条要闻

渐冻症女子被男护工猥亵案宣判 多次被对方亲吻、摸胸

头条要闻

渐冻症女子被男护工猥亵案宣判 多次被对方亲吻、摸胸

体育要闻

德国的超级替补,10年前还在工厂上班

娱乐要闻

原来她就是张颂文老婆

财经要闻

“床垫界的特斯拉”破产了

汽车要闻

惊出冷汗!重庆实测奥迪A5L,华为智驾这波操作绝了…

态度原创

本地
房产
艺术
数码
公开课

本地新闻

龙腾资江 韵动邵阳

房产要闻

商业清零式退潮,大量住宅登场!三亚又要大规模调规!

艺术要闻

310米!欧盟第一高楼,坐落于波兰

数码要闻

存储价格暴涨超300%!手机电脑又要涨价了 涨幅预计还是千元起

公开课

李玫瑾:为什么性格比能力更重要?

无障碍浏览 进入关怀版