前言在mysql中设计表时,mysql官方建议不要使用uuid或者不连续不重复的雪花id(长且唯一,单机自增),推荐使用连续自增的主键id。官方推荐的是auto_increment,那么为什么不推荐使用uuid,使用uuid有什么缺点呢?在这篇博客中,我们将分析这个问题并讨论内部原因。本博客的目录mysql程序示例使用了uuid和自增id的索引结构对比。总结1.Mysql及程序示例1.1.为了解释这个问题,我们先创建三张表,分别是user_auto_key、user_uuid、user_random_key,分别代表automatic增长的主键,uuid为主键,randomkey为主键,其余的我们保持完全不变。根据控制变量的方法,我们只使用不同的策略生成每张表的主键,而其他字段完全相同,然后测试表的插入速度和查询速度:注:这里的随机键实际上是指雪花算法计算出的不连续、不重复、不规则的id:一串18位长值id自动生成表:用户uuid表随机主键表:1.2。光有理论不行,直接上程序,使用spring的jdbcTemplate实现增加测试:技术框架:springboot+jdbcTemplate+junit+hutool,程序原理是连接自己的测试数据库,和然后在同样的环境下写入同样数量的数据分析插入时间综合其效率。为了达到最真实的效果,所有数据都是随机生成的,比如姓名、邮箱地址、地址都是随机生成的。搜索Java知音公众号,回复“后端面试”,送你一份Java面试题宝典packagecom.wyq.mysqldemo;importcn.hutool.core.collection.CollectionUtil;importcom.wyq.mysqldemo.databaseobject.UserKeyAuto;importcom.wyq.mysqldemo.databaseobject.UserKeyRandom;importcom.wyq.mysqldemo.databaseobject.UserKeyUUID;importcom.wyq.mysqldemo.diffkeytest.AutoKeyTableService;importcom.wyq.mysqldemo.diffkeytest.RandomKeyTableService;importcom.wyq.mysqldemo.diffkeytest.UUIDKeyTableService;importcom。wyq.mysqldemo.util.JdbcTemplateService;importorg.junit.jupiter.api.Test;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.boot.test.context.SpringBootTest;importorg.springframework.util.StopWatch;importjava.util.List;@SpringBootTestclassMysqlDemoApplicationTests{@AutowiredprivateJdbcTemplateServicejdbcTemplateService;@AutowiredprivateAutoKeyTableServiceautoKeyTableService;@AutowiredprivateUUIDKeyTableServiceuuidKeyTableService;@AutowiredprivateRandomKeyTableServicerandomKeyTableService;@TestvoidtestDBTime(){StopWatchstopwatch=newStopWatch("执行sql耗时");/***auto_incrementkey任务*/finalStringinsertSql="INSERTINTOuser_key_auto(user_id,user_name,sex,address,city,email,state)VALUES(?,?,?,?,?,?,?)";ListinsertData=autoKeyTableService.getInsertData();stopwatch.start("自动生成键表任务开始");longstart1=System.currentTimeMillis();if(CollectionUtil.isNotEmpty(insertData)){booleaninsertResult=jdbcTemplateService.insert(insertSql,insertData,false);System.out.println(insertResult);}longend1=System.currentTimeMillis();System.out.println("autokey消耗的时间:"+(end1-start1));stopwatch.stop();/***uudID的键*/finalStringinsertSql2="INSERTINTOuser_uuid(id,user_id,user_name,sex,address,city,email,state)VALUES(?,?,?,?,?,?,?,?)";ListinsertData2=uuidKeyTableService.getInsertData();stopwatch.start("UUID键表任务开始");longbegin=System.currentTimeMillis();如果(CollectionUtil.isNotEmpty(insertData)){booleaninsertResult=jdbcTemplateService.insert(insertSql2,insertData2,true);System.out.println(insertResult);}longover=System.currentTimeMillis();System.out.println("UUIDkey消耗时间:"+(over-begin));stopwatch.stop();/***随机长值键*/finalStringinsertSql3="INSERTINTOuser_random_key(id,user_id,user_name,sex,address,city,email,state)VALUES(?,?,?,?,?,?,?,?)";ListinsertData3=randomKeyTableService.getInsertData();stopwatch.start("随机长值键表任务开始");Longstart=System.currentTimeMillis();if(CollectionUtil.isNotEmpty(insertData)){booleaninsertResult=jdbcTemplateService.insert(insertSql3,insertData3,true);System.out.println(insertResult);}Longend=System.currentTimeMillis();System.out.println("随机键任务消耗时间:"+(end-start));stopwatch.stop();Stringresult=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效率最低。在数据量很大的情况下,效率直线下降。那么为什么会出现这样的现象呢?带着疑惑,我们来讨论一下这个问题:2、使用uuid和自增id的索引结构比较2.1。使用自增id的内部结构自增主键的值是有顺序的,所以Innodb将每条记录存储在一条记录之后。当达到页面的最大填充因子时(innodb默认的最大填充因子是页面大小的15/16,会预留1/16的空间用于以后的修改):①下一条记录会被写入一个新的page,一旦按照这个顺序加载数据,主键页就会被几乎连续的记录填充,增加了页的最大填充率,不会出现页浪费的情况。对于下一行,mysql定位寻址非常快,不会为计算新行的位置做额外的消耗③减少分页和碎片的产生2.2。使用uuid索引的内部结构因为uuid的相对顺序自增id根本没有规则,新行的值不一定大于之前主键的值,所以innodb不能总是在索引的末尾插入新行,但需要为新行找到新行。分配新空间的适当位置。这个过程需要很多额外的操作,数据的无序会导致数据分布分散,会导致以下问题:yetbeenLoadedintocache,innodb在插入之前要从磁盘中找到目标page并读入内存,这会造成大量的随机IO②因为写乱序,innodb不得不频繁的做pagesplitting,在为了给新行分配空间,分页导致大量数据移动,一次插入至少需要修改三页。③由于频繁的分页,页面会变得稀疏,填充不规律,最终导致数据丢失。fragments加载随机值(uuid和snowflakeid)到聚簇索引(innodb默认索引类型)后,有时需要做一个OPTIMEIZETABLE重建表优化填充页面,这会占用消耗一定的时间。结论:使用innodb时,尽量按照主键自增的顺序插入,尽量使用单调递增的聚簇键值插入新行。Java面试题集2.3。使用自增id的缺点使用自增id是不是一点坏处都没有?不会的,自增id还会存在以下问题:①一旦别人爬取了你的数据库,就可以根据数据库的自增id获取到你的业务增长信息,很容易分析出你的业务情况。并发负载,InnoDB在按照主键插入时会造成明显的锁竞争,主键的上界会成为竞争的热点,因为所有的插入都发生在这里,并发插入会造成间隙锁竞争③Auto_Increment锁机制会造成自增锁的抢夺,会有一定的性能损失测试不同id生成策略在大数据插入时的性能,然后分析mysql索引结构中不同id机制,优缺点,并深入解释uuid和随机不重复id在数据插入时性能损失的原因,详细解释了问题。在实际开发中,最好按照mysql官方推荐使用自增id。Mysql博大精深,内部还有很多值得我们去学习优化的地方。