引入场景:在一般的单层架构和简单系统中,使用主键自增ID来使用是没用问题的,且查询和存储效率都是最高的。但是在分布式架构中,会出现新老系统的,因此重复ID也会成为可能,为保证全局id唯一,应使用UUID。
MySQL官方推荐不要使用uuid或者不重复不连续的雪花id(long型且id唯一,单机递增),而是推荐使用自增id,推荐使用auto_increment
。
自增id做主键
在设计数据库表时,自增id 通过auto_increment
,id依次递增,一般来说插入的id为上一个id值加1。
- 使用自增id,如果我们定义了主键(PRIMARY KEY),那么InnoDB会选择主键作为聚集索引、如果没有显式定义主键,则InnoDB会选择第一个不包含NULL值的唯一索引作为主键索引、如果也没有这样的值作为主键索引,则InnoDB会选择设置内置6字节长的ROWID作为隐含的聚集索引(ROWID随着行记录的写入设置逐渐递增)
- 记录本身存放在主索引(一颗B+tree)的叶子节点上。这就需要同一个叶子节点内(大小为一个内存页或者磁盘页)的各个记录按照主键顺序存放,每当一天记录插入时,MySQL会根据其主键将其插入当前适当的节点位置,如果页面达到装载因子(InnoDB默认为15/16),则开辟为一个新的页
- 使用主键自增,每次插入一条新的记录,记录就会沿着当前索引节点的后续位置,当一页写满,就会开辟一个新的页
- 减少了叶分裂和碎片的产生
- 自增id主键可能不连续,造成不连续的情况可能有:
- 唯一主键冲突
- 事务回滚
- insert…select语句批量申请自增id
UUID做主键
UUID 是 通用唯一识别码(Universally Unique Identifier)的缩写,是一种软件建构的标准,亦为开放软件基金会组织在分布式计算环境领域的一部分。其目的,是让分布式系统中的所有元素,都能有唯一的辨识信息,而不需要通过中央控制端来做辨识信息的指定。如此一来,每个人都可以创建不与其它人冲突的UUID。在这样的情况下,就不需考虑数据库创建时的名称重复问题。目前最广泛应用的UUID,是微软公司的全局唯一标识符(GUID),而其他重要的应用,则有Linux ext2/ext3文件系统、LUKS加密分区、GNOME、KDE、Mac OS X等等。另外我们也可以在e2fsprogs包中的UUID库找到实现。
UUID(Universally Unique Dentifer)的标准型式包含32个16进制数字,以连字号分为五段,形式为8-4-4-4-12的32个字符,示例:550e8400-e29b-41d4-a716-446655440000
UUID性能非常高:本地生成,没有网络消耗,如果只考虑唯一性UUID是ok的。但是入数据库的性能较差。
- 使用UUId做主键,可以减少主键ID冲突
- 使用UUID,生成的id不仅是独立的,而且库是独立的。对以后的数据库操作提供了极大地方便。
- 写入的目标页很可能已经刷新到磁盘上并从缓存上移除,或者还没有加载到缓存中,
InnoDB
在插入之前就不得不先找到并从磁盘中读取目标页到内存中,这将导致大量的随机IO操作
- 由于斜入式乱序(无序)的,
InnoDB
不得不频繁的做分页操作,以便为新的行分配空间,页分裂导致移动大量的数据,一次插入最少需要修改三个页以上 - 由于频繁的分页操作 ,页会变得稀疏并被不规则的填充,最终会导致数据有碎片。在把随机值(uuid和雪花id)载入到聚簇索引(innodb默认的索引类型)以后,有时候会需要做一次OPTIMEIZE TABLE来重建表并优化页的填充,这将又需要一定的时间消耗。
总结:
首先对于普通数据库,并不建议用UUID做主键,因为UUID会暴露mac地址啊,不安全。再就是对于MYSQL数据库,使用UUID做主键,会导致建立索引时产生页分裂(B+树的左旋右旋),严重影响性能。
为了存储和查找性能应该使用自增ID作为主键;为了全局唯一性,应该使用UUID做索引关联其他表或做外键
现在比较主流的解决方法:
自增id保留,使用雪花id来作为数据主键。这样既不会被遍历出来,也保留了新能。uuid是无序的,同样的sql,查询第一页可能会得到不同的结果
参考: