centos修改磁盘uuid_为什么MySQL用uuid做主键会被骂?

在 MySQL 中设计表的时候,MySQL 官方推荐不要使用 uuid 或者不连续不重复的雪花 id(long 形且唯一,单机递增),而是推荐连续自增的主键 id,官方的推荐是 auto_increment。

那么为什么不建议采用 uuid,使用 uuid 究竟有什么坏处?本问我们从以下几个部分来分析这个问题,探讨一下内部的原因:

  • MySQL 程序实例

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

  • 总结

MySQL 程序实例

要说明这个问题,我们首先来建立三张表,分别是:

  • user_auto_key

  • user_uuid

  • user_random_key

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

根据控制变量法,我们只把每个表的主键使用不同的策略生成,而其他的字段完全一样,然后测试一下表的插入速度和查询速度。注:这里的随机 key 其实是指用雪花算法算出来的前后不连续不重复无规律的id:一串 18 位长度的 long 值。

id 自动生成表:

862ae5a2726a57feeb3e78e7e741637d.png

用户 uuid 表:

538302072170f94b16340581eafe6fcb.png

随机主键表:f99f8853895bed49a96806c50c740f4f.png

光有理论不行,直接上程序,使用 Spring 的 jdbcTemplate 来实现增查测试。

技术框架:Spring Boot+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
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 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);
    }

程序写入结果

user_key_auto 写入结果:

c5aa0bc6441a95e2b1cf5711d2a3c5c6.png

user_random_key 写入结果:

4b8300278eb6ee111e948d03e863ce34.png

user_uuid 表写入结果:

4bd369782d10ef2bb7389db0efbd9eab.png

效率测试结果

5a38b1e6772407241e6307d1ae9d08ba.png

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

0bcf6e828135625a219fe492b5fcaf26.png

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

时间占用量总体可以打出的效率排名为:auto_key>random_key>uuid。

uuid 的效率最低,在数据量较大的情况下,效率直线下滑。那么为什么会出现这样的现象呢?带着疑问,我们来探讨一下这个问题:

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

使用自增 id 的内部结构

9a9c19f19baa66e298f2706d76bfd2a5.png

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

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

下一条记录就会写入新的页中,一旦数据按照这种顺序的方式加载,主键页就会近乎于顺序的记录填满,提升了页面的最大填充率,不会有页的浪费。新插入的行一定会在原有的最大数据行下一行,MySQL 定位和寻址很快,不会为计算新行的位置而做出额外的消耗。减少了页分裂和碎片的产生。

使用 uuid 的索引内部结构

93462ccd98ae0c3558499f517f6ad4f4.png

因为 uuid 相对顺序的自增 id 来说是毫无规律可言的,新行的值不一定要比之前的主键的值要大,所以 innodb 无法做到总是把新行插入到索引的最后,而是需要为新行寻找新的合适的位置从而来分配新的空间。这个过程需要做很多额外的操作,数据的毫无顺序会导致数据分布散乱,将会导致以下的问题:写入的目标页很可能已经刷新到磁盘上并且从缓存上移除,或者还没有被加载到缓存中,innodb 在插入之前不得不先找到并从磁盘读取目标页到内存中,这将导致大量的随机 IO。因为写入是乱序的,innodb 不得不频繁的做页分裂操作,以便为新的行分配空间,页分裂导致移动大量的数据,一次插入最少需要修改三个页以上。由于频繁的页分裂,页会变得稀疏并被不规则的填充,最终会导致数据会有碎片。在把随机值(uuid 和雪花 id)载入到聚簇索引(InnoDB 默认的索引类型)以后,有时候会需要做一次 OPTIMEIZE TABLE 来重建表并优化页的填充,这将又需要一定的时间消耗。结论:使用 InnoDB 应该尽可能的按主键的自增顺序插入,并且尽可能使用单调的增加的聚簇键的值来插入新行。

使用自增 id 的缺点

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

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

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

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

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

总结

本篇博客首先从开篇的提出问题,建表到使用 jdbcTemplate 去测试不同 id 的生成策略在大数据量的数据插入表现,然后分析了 id 的机制不同在 MySQL 的索引结构以及优缺点,深入的解释了为何 uuid 和随机不重复 id 在数据插入中的性能损耗,详细的解释了这个问题。在实际的开发中还是根据 MySQL 的官方推荐最好使用自增 id,MySQL 博大精深,内部还有很多值得优化的点需要我们学习。

出处:cnblogs.com/wyq178/p/12548864.html

更多精彩文章

  • ?分布式大并发系列
  • ?架构设计系列
  • ?趣学算法和数据结构系列
  • ?设计模式系列

af13adcf271f5902355da02c8f34455b.gif

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

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

相关文章

从客户端登陆服务器的配置文件,BLE模式和配置文件

BLE模式和配置文件(原文)本文档探讨了BLE如何工作,特别是如何将两种BLE模式(连接和广告)用于不同的目的。外围设备和中央设备vs服务器和客户端当我们通过BLE连接设备时,我们将它们视为外设(从设备)设备或中央(主设备)设备。 蓝牙标准建立了该区分方式以匹…

和preload_通过LD_PRELOAD绕过disable_functions

0x00 前言前段时间碰到拿到shell以后限制了basedir并且无法执行命令的情况,解决办法是上传恶意的.so文件,并通过设置LD_PRELOAD,然后调用新进程来加载恶意.so文件,达到绕过的效果。当时做这道题目的时候是跟着别人的题解直接套的(…

ajax循环输出,Ajax轮询 select循环输出

弹出层.del{color:red}.addname{color:#337ab7}款项名目操作{$vo.name}删除添加...$(".addname").on("click",function(){layer.prompt({title: 添加款项名目, formType: 0}, function(text, index){layer.close(index);$.post("{:U(Contracts/setmon…

群晖服务器性能测试,对群晖DS716+进行性能测试_群晖 DS716+_企业存储技术与评测-中关村在线...

Iometer是一个工作在单系统和集群系统上用来衡量和描述I/O子系统的工具。可以被配置为模拟任何程序或者基准测试程序的磁盘和网络I/O的负载,或者用来产生整个综合的I/O负载。它也可以用来产生并测量单系统或者多系统(网络)的负载。在性能测试环节,我们采…

传播路由_什么路由器穿墙效果好?购买防骗知识

买无线路由器,首要辨识什么路由器穿墙效果好。面对市面上五花八门的路由器,很多小白在看到纷繁的参数就凌乱了,上了无良商家的当。小编觉得授人以鱼不如授人以渔,所以给大家写了一篇辨别什么路由器穿墙效果好的防入坑购买指南。我…

小程序消息服务器webapi,小程序订阅消息

# 小程序订阅消息# 功能介绍消息能力是小程序能力中的重要组成,我们为开发者提供了订阅消息能力,以便实现服务的闭环和更优的体验。 订阅消息推送位置:服务通知订阅消息下发条件:用户自主订阅订阅消息卡片跳转能力:点击…

php生成pdf中文断码_Rmarkdown导出中文PDF解决方案

Rmarkdown非常适合R语言使用者导出格式漂亮的文档,但是输出中文PDF的过程中会遇到各种麻烦的问题,本文整理了我的经验供大家参考。总的来说,通过Rmarkdown输出中文版pdf报告,要解决以下两个核心问题:导出PDF需要配置Te…

分数的大小比较优秀教案_人教版小学数学五年级下册异分母分数加、减法公开课优质课课件教案视频...

教学案例-《异分母分数加减法》教学目标:1.借助直观图形,在操作、观察、比较、的活动中理解异分母分数加减法的算理,掌握计算方法,并能正确进行计算。2.渗透转化、迁移的数学思想,积累研究分数问题的数学活动经验。3.能…

docker给容器分配固定ip

1.为 Docker 容器设置一个固定的 IP 地址 要为 Docker 容器设置一个固定的 IP 地址,有几种常见的方法: 使用自定义网络和静态 IP 地址: 你可以创建一个自定义的 Docker 网络,并在这个网络上为容器分配静态 IP 地址。首先&#x…

分号可以用来分段么_更高效的GMX分段模拟方法:修改tpr文件

■2020-11-01 22:42:18在以前的一篇文章中, 我简单说过基于GROMACS的分段模拟方法[1]. 这种方法非常通用, 几乎能完成任意的功能, 且无须修改源代码, 但是运行效率比较差, 因为每次运行mdrun都要重新生成tpr文件. 对大分子来说, 使用grompp生成tpr还是很耗时的, 可能会成为运行…

java list拷贝_深入了解浅拷贝与深拷贝

在学习深拷贝和浅拷贝之前&#xff0c;咱们先来一个例子&#xff1a;import java.util.ArrayList;public class MyBaby implements Cloneable {/*** 私有变量*/private ArrayList<String> list new ArrayList<>();Overrideprotected Object clone() throws CloneN…

吗 极域软件可以装win10_关于win10企业版在极域电子教室软件 v4.0 2015 豪华版的全屏控制下如何取得自由...

注.可能因为系统和软件的缘故无法实现背景由于在听课过程过于自闭&#xff0c;于是想自己去网上搜点东西看下于是 经过了一番乱搞 逐渐摸索出了现方法。方案1:大力出奇迹由于电脑在刚刚进入的状态的时候有段时间是断网的并且该鬼畜的学生端可以通过任务资源管理器直接退所以可以…

python怎么启动mne_mne-python学习之一 入门介绍

mne-python脑电图和肌电图是一个开源软件分析、处理和显示。遵循bsd许可协议,由哈佛大学和共同开发的社区。主要功能包括:预处理和脑电图\/梅格信号的去噪,源估计、时频分析、统计测试,功能连接,机器学习,可视化的传感器、来源等外资支持最常见的原始数据格式。默认的(和附带的…

dcdc升压计算器excel_DC/DC升压转换器MAX8815A

DC&#xff0f;DC升压转换器MAX8815A佚名【摘要】MAX8815A具有97&#xff05;的最高效率、3OuA低静态电流以及低噪声强制PWM工作模式。该boost转换器专为2节NiMH&#xff0f;NiCdAA电池或单节Li&#xff0b;电池输入设计&#xff0c;可从1&#xff0e;2&#xff5e;5&#xff0…

python类怎么实例化rnn层_Python backend.rnn方法代码示例

本文整理汇总了Python中keras.backend.rnn方法的典型用法代码示例。如果您正苦于以下问题&#xff1a;Python backend.rnn方法的具体用法&#xff1f;Python backend.rnn怎么用&#xff1f;Python backend.rnn使用的例子&#xff1f;那么恭喜您, 这里精选的方法代码示例或许可以…

vscode删除文件夹,VSCode:删除文件中的所有注释

Is there an easy way to delete all comments from an open file in VSCode? Preferably both line and block comments.Most interested in Java, but also Python and R.解决方案Easy way:Open extensions (ctrl-shift-x)type in remove comments in the search box.Instal…

放大镜_屏幕放大镜怎么样使用方法

首先&#xff0c;打开控制面板&#xff0c;然后找到并单击“显示”&#xff0c;然后启动放大镜。放大镜的放大倍率基于原始屏幕&#xff0c;而不是矢量放大倍率。有关放大镜的详细操作&#xff0c;请单击帮助按钮&#xff0c;其中有特定说明。捷径一&#xff1a;win 可以快速调…

python清空语句_怎么清除python编译器的语句

清除python编辑器的方法&#xff1a;1、下载清屏函数clearwindow.py&#xff0c;然后复制clearwindow.py文件&#xff0c;并放在Python安装目录PythonXLibidlelib下面2、在Python XLibidlelib目录下找到config-extensions.def(IDLE扩展的配置文件)&#xff0c;用记事本打开&…

mysql字段是否存在_Mysql判断表字段或索引是否存在

判断字段是否存在&#xff1a;DROP PROCEDURE IF EXISTS schema_change;DELIMITER //CREATE PROCEDURE schema_change() BEGINDECLARE CurrentDatabase VARCHAR();SELECT DATABASE() INTO CurrentDatabase;IF NOT EXISTS (SELECT * FROM information_schema.columns WHERE tabl…

mysql取消主键_mysql如何删除主键?

当一个表中设置了主键之后&#xff0c;如果想要删除主键了要怎么做&#xff1f;下面本篇文章就给大家介绍MySQL删除主键的方法&#xff0c;希望对你们有所帮助。首先我们来看看删除主键的语法&#xff1a;ALTER TABLE TABLE_NAME DROP PRIMARY KEY;在MySQL中删除主键要考虑两种…