龙空技术网

MySQL为什么不能用UUID作为主键?

Java西西 9443

前言:

当前各位老铁们对“mysql 主键 uuid”可能比较讲究,你们都需要分析一些“mysql 主键 uuid”的相关文章。那么小编在网络上搜集了一些对于“mysql 主键 uuid””的相关内容,希望姐妹们能喜欢,各位老铁们快快来学习一下吧!

为什么不能UUID作为数据库表主键?

数据存储是开发中不可缺少的一个环节,而承载着数据存储的功能的是数据库。MySQL数据库因为开源免费的特点,深受国内中小型互联网公司的喜爱。

在设计数据库表时,MySQL官方不推荐使用UUID作为主键,官方推荐的是使用连续自增的ID作为主键。那么官方推荐使用自增和不推荐使用UUID的原因是什么呢?

存储的数据结构

在MySQL中,目前使用主流的引擎是InnoDB,在MySQL5.5版本后更是成了默认的存储引擎。使用InnoDB的表必须有主键,如果不显性定义主键,默认情况下会生成一个6byte空间的自动增长主键,该主键字段是_rowid,但是直接用查询语句(select)是查询不到这个字段存在的。

InnoDB索引的数据结构默认是B+树,而MySQL数据存储的最小单元为页,即page,每个page大小为16KB,一条条的数据记录在page上。可以把数据库一张表想象成一本书,每页只能记录那么多行,按照索引的大小规律去记录,每页记录到相应的行数,下一条数据就分页,只不过与书本文字印刷死不一样的是,数据库的页数据内容是可以动态变化的。

数据存储特点

当表存入数据时,必须严格按照索引的大小进行排序,然后将数据插入到索引对应的位置(主键索引存的整条记录,非主键索引存储的是主键的ID)。

ID主键自增存储

ID主键自增是有规律有顺序的,当新增一条数据时,直接在上条数据末尾添加一条新的数据,如果上条数据达到page存储的最大值(InnoDB引擎默认是达到15/16时就会满,剩下的1/16则用于数据变更修改操作),则会在下一个新的page存储。这样的主键自增方式能快速让page填充,page存储空间利用率非常高。除此之外,在因为新的一行数据存储一定是上一条记录的下一行,所以数据库寻址和插入十分快,不会因为计算新行和寻址做出额外更复杂的计算。

ID自增使用int(bigint)类型的,空间占用较少,非常适合做主键索引,但也存在一定的局限性:

ID自增的主键是通过MySQL的自增锁获取,在并发量高的情况下,性能会有所下降。ID自增存取的数据,一旦被爬虫获取,容易因为规律增长特性导致表其他数据信息被爬取。适合业务体量不大的系统,对于分布式微服务等大型系统容易造成主键冲突,因此分布式系统使用ID主键时一般不适用ID自增,而是推荐使用雪花算法或其他算法能保证唯一性作为ID主键。UUID存储

UUID 是通用唯一识别码(Universally Unique Identifier)的缩写,UUID是一个128bit的数值,这个数值可以通过一定的算法计算出来。为了提高效率,常用的UUID可缩短至16位。UUID用来识别属性类型,在所有空间和时间上被视为唯一的标识。一般来说,可以保证这个值是真正唯一的任何地方产生的任意一个UUID都不会有相同的值。但也不是绝对的,经过科学概率计算,假如每秒产生10亿笔UUID,100年后只产生一次重复的机率是50%,这个概率目前来说是完全可以忽略的。

那么优秀的而且符合唯一性规则的字符为何不能用来当作主键呢?首先UUID可以快速生成一个128bit的数值,相比于ID自增使用的int(4 byte)或者bigint(8 byte)类型来说,占用的空间更大,尽管能缩短到16位,但仍然比自增的int或bigint类型占用空间大。

其次,UUID有一个无法弥补的缺陷,生成的字符,虽然是按照一定的规则生成,但是无法确保数值是依次增大,每次新增数据时都要根据主键值寻址,不能像ID自增那样在末尾增加即可。

这样毫无规律地插入会产生几点影响:

插入的数据当前没有在缓存中,需要从磁盘中读取到缓存,这个时候需要大量的IO操作,比较耗时。主键值有可能在已经存满数据的page页里,这个时候需要重新页分裂来排序数据,而且一条数据会导致三个以上page页修改。由于频繁的页分裂,会形成填充不规律,最终导致page页的数据存在大量的碎片。总结

总的来说,UUID并非不能作为ID主键,只是使用UUID作为主键,会导致数据库表读写能力相比于ID自增的方式要低,这是因为UUID生成的是128bit字符串和生成时的字符不能保证递增两个主要原因造成的。所以作为开发者来说,无论是从性能和存储方面考虑,都不会去使用UUID作为数据库表主键。

标签: #mysql 主键 uuid #mysql自动生成uuid #mysql 主键uuid