首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >为啥不能用uuid做MySQL的主键 ?

为啥不能用uuid做MySQL的主键 ?

原创
作者头像
AlbertZhang
修改于 2020-09-09 06:36:59
修改于 2020-09-09 06:36:59
4.1K00
代码可运行
举报
文章被收录于专栏:技术文献技术文献
运行总次数:0
代码可运行

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

本篇博客我们就来分析这个问题,探讨一下内部的原因。

本篇博客的目录

  • mysql程序实例
  • 使用uuid和自增id的索引结构对比
  • 总结

一、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插入的时间来进行综合其效率,为了做到最真实的效果,所有的数据采用随机生成,比如名字、邮箱、地址都是随机生成。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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
class MysqlDemoApplicationTests {

    @Autowired
    private JdbcTemplateService jdbcTemplateService;

    @Autowired
    private AutoKeyTableService autoKeyTableService;

    @Autowired
    private UUIDKeyTableService uuidKeyTableService;

    @Autowired
    private RandomKeyTableService randomKeyTableService;


    @Test
    void testDBTime() {

        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<UserKeyAuto> 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<UserKeyUUID> 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<UserKeyRandom> 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博大精深,内部还有很多值得优化的点需要我们学习。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
如何从传统单体架构转向微服务
当今,把单体架构的应用拆成微型服务是很时髦的。让我想起了2000年世纪初的那些日子,那时SOA正在流行,大多数公司,供应商和系统集成商,正忙着挥动SOA魔杖,希望它能将他们的遗留应用程序转变为更加灵活和敏捷的SOA应用程序。一个供应商甚至说,“使用SOA,您不需要编写一行代码“。不幸的是,事实却根本不是这样。虽然他们的大肆宣传,努力去做,结果却不美好。虽然服务的概念还不错,但是SOA具有强类型的服务定义,并且在HTTP上使用SOAP非常麻烦。这些缺点似于谚语中所说的“当你有一个新的闪亮的锤子时,一切看起来都像钉子”,这就是SOA的末日。
程序你好
2018/05/24
2.1K0
如何从传统单体架构转向微服务
(译)Medium 的微服务架构
微服务架构的目标是帮助工程团队安全快速地完成高质量的产品交付。良好解耦的服务能够在最小化对其它系统的影响的条件下进行快速迭代。
崔秀龙
2019/07/23
4940
(译)Medium 的微服务架构
第12章 Spring Boot与微服务第12章 Spring Boot与微服务12.1 微服务架构12.2 Spring Cloud构建微服务架构
随着RESTful web服务和JSON数据交换格式流行,简单快速建立一个可连接的服务已经越来越方便了。随着持续交付概念推广以及Docker容器普及,微服务将这两种理念和技术结合起来,形成新的微服务+API + 平台的开发模式,以及容器化微服务的持续交付概念。
一个会写诗的程序员
2018/08/20
6180
第12章 Spring Boot与微服务第12章 Spring Boot与微服务12.1 微服务架构12.2 Spring Cloud构建微服务架构
微服务设计指南
微服务是当今软件工程师的一个热门话题。让我们了解如何使用微服务架构风格构建真正模块化、业务敏捷的IT系统。
yuanyi928
2018/12/07
1.2K0
微服务设计指南
微服务架构体系——它适合您的软件开发吗?
“Microservice architecture provides a range of technical benefits that contribute to the development velocity and product quality in software projects, while also contributing to the overall business agility”– Mark Emeis, Senior director of software technologies, CA Technologies
程序你好
2018/09/29
7620
微服务架构体系——它适合您的软件开发吗?
可组合架构与微服务:哪个更优?
两种软件开发架构都有各自的优缺点。下面是如何决定可组合架构还是微服务架构哪种更适合你的方法。
云云众生s
2024/03/28
1810
微服务的几种设计模式
定义:微服务架构是将软件系统分解为可独立部署的自治单元,这些单元通过轻量级、与语言无关的方式进行通信,并共同实现业务目标
素履coder
2022/02/17
9460
微服务的几种设计模式
解析微服务架构(一):什么是微服务
解析微服务架构系列文章将分几篇描述微服务的定义、特点、应用场景、企业集成架构的演进以及微服务转型思路和技术决策考虑等内容,并以IBM技术为例介绍如何实现微服务架构转型。 为什么需要微服务架构 “微服务”架构是近期软件应用领域非常热门的概念。让我们先来看看传统IT架构面临的一些问题: 使用传统的整体式架构(Monolithic Architecture)应用开发系统,如CRM、ERP等大型应用,随着新需求的不断增加,企业更新和修复大型整体式应用变得越来越困难; 随着移动互联网的发展,企业被迫将其应用迁移至现代
逸鹏
2018/04/11
1.2K0
解析微服务架构(一):什么是微服务
【微服务架构】一文读懂单片到微服务架构的模式和最佳实践
在本文中,我们将学习如何使用设计模式、原则和最佳实践来设计微服务架构。我们将使用正确的架构设计模式和技术。 在本文结束时,您将了解如何在微服务分布式架构上设计系统以实现高可用性、高可扩展性、低延迟和对网络故障的弹性,从而处理数百万个请求。 Event-Driven Architecture 本课程将是软件架构设计的旅程,逐步将架构单片演变为事件驱动的微服务。 我们将从设计处理少量请求的电子商务整体架构开始软件架构的基础知识。 Journey of Design Architectures 之后逐步演
架构师研究会
2022/03/30
9900
微服务架构设计中的设计模式、原则及最佳实践
本文既有理论知识,又有实用信息:我们将学习每一种具体的模式,为什么以及应该在什么地方使用;然后,我们将看下应用了这些模式的参考架构;接下来,我们将综合运用新学到的模式设计我们的架构;最后,我们将确定选用什么技术实现架构。
用户2781897
2021/11/10
5510
什么是微服务
在介绍微服务时,首先得先理解什么是微服务,顾名思义,微服务得从两个方面去理解,什么是"微"、什么是"服务", 微 狭义来讲就是体积小、著名的"2 pizza 团队"很好的诠释了这一解释(2 pizza 团队最早是亚马逊 CEO Bezos提出来的,意思是说单个服务的设计,所有参与人从设计、开发、测试、运维所有人加起来 只需要2个披萨就够了 )。 而所谓服务,一定要区别于系统,服务一个或者一组相对较小且独立的功能单元,是用户可以感知最小功能集。
前朝楚水
2018/07/26
1K0
简历驱动开发?微服务中的几种失败路径
在去年 11 月的 QCon Plus 上,我介绍了微服务可能走入歧途的一些路径。我是 IBM 的一名顾问,我的一部分工作是帮助业务迈向云原生。本文提到的这些问题都是从我的经验总结出来的 -- 不幸的是,它们在实践中是非常常见的。
深度学习与Python
2022/03/23
3880
简历驱动开发?微服务中的几种失败路径
实践微服务六年,我获得了这些心得体会
我是一名微服务架构的忠心拥趸,虽然有时也会对其感到不爽。使用微服务时,我时常能感受到“小中见大”、“稳中有快”等理念,另一方面也会警惕“厨子太多烧坏了汤”。
xcbeyond
2020/10/21
8160
实践微服务六年,我获得了这些心得体会
Medium 微服务策略
微服务架构的目标是帮助技术团队更快、更安全、更高质量的推动产品,服务解耦可以让团队快速迭代,对系统的影响最小。
dys
2018/12/17
1K1
跟着小程来学微服务--微服务思想
一直对微服务非常感兴趣,因为公司的架构改造正好有机会能够接触微服务,买来一些书,请教了很多微服务大牛同时自己也做了很多总结,写成了80页ppt,算是我对微服务的一个认识吧,微服务本身不同的人有不同的理解,而我就从我自己的角度来谈谈微服务是什么。
小程故事多
2018/08/22
4570
跟着小程来学微服务--微服务思想
【微服务架构】什么是微服务? — 全面了解微服务架构
What is Microservices — Edureka 您有没有想过,什么是微服务以及扩展行业如何与它们集成,同时构建应用程序以满足客户的期望? 要了解什么是微服务,您必须了解如何将单体应用程序分解为独立打包和部署的小型微型应用程序。本文将让您清楚了解开发人员如何使用微服务根据需要扩展其应用程序。 在本文中,您将了解以下内容: 为什么是微服务? 什么是微服务? 微服务架构的特点 微服务架构的优势 设计微服务的最佳实践 使用微服务的公司 为什么是微服务? 现在,在我告诉你微服务之前,让我们看看在
架构师研究会
2022/04/24
2.6K0
【微服务架构】什么是微服务? — 全面了解微服务架构
什么是微服务?
微服务体系结构是在体系结构级别应用单一责任原则的自然结果。与传统的单片体系结构相比,这带来了许多好处,例如不同组件的独立可部署性、语言、平台和技术独立性、不同的可伸缩性轴以及增加的体系结构灵活性。
霍格沃兹测试开发Muller老师
2022/12/01
5370
一文读懂微服务
在本文中,我将讨论什么是微服务,它们为何如此重要。我们将从微服务历史以及它们与单体架构的比较开始。然后,我们将讨论微服务架构的一些原理,其潜在的缺点,以及如何与容器和Kubernetes等现代工具结合使用。
DevOps持续交付
2021/03/16
6060
一文读懂微服务
微服务架构及其最重要的10个设计模式
微服务架构,独享数据库、事件驱动、CQRS、Saga、BFF、API 网关、Strangler、断路器、外部化配置、消费端驱动的契约测试
深度学习与Python
2021/01/06
1.3K0
什么是微服务架构
什么是微服务? 微服务(Microservices Architecture)是一种架构风格,一个大型复杂软件应用由一个或多个微服务组成。系统中的各个微服务可被独立部署,各个微服务之间是松耦合的。每个微服务仅关注于完成一件任务并很好地完成该任务。在所有情况下,每个任务代表着一个小的业务能力。 微服务的概念源于2014年3月Martin Fowler所写的章“Microservices”http://martinfowler.com/articles/microservices.html 单体架构(Monol
程序员鹏磊
2018/02/09
1.4K0
什么是微服务架构
推荐阅读
相关推荐
如何从传统单体架构转向微服务
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验