Mysql主键UUID和自增主键区别优劣

本文对比MySQL中自增主键与UUID主键的区别,探讨两者适用场景及性能影响,给出最优实践建议。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

引言

之前有段时间用postgresql 数据库,在上云之后,从自增主键变为uuid,感觉uuid全球唯一,很方便。

最近用mysql,发现mysql主键都是选择自增主键,仔细比较一下,为什么mysql选择自增主键,有什么不同。

在mysql5.0之前,如果是多个master复制的环境,无法用自增主键,因为可能重复。在5.0以及之后的版本通过配置自增偏移量解决了整个问题。

什么情况下我们希望用uuid

1. 避免重复,便于scale,这就是我们做cloud service的时候选择uuid的主要原因

2. 入库之前可以知道id

3.相对安全,不能简单的从uuid获取信息,但是如果自增,则容易暴露信息,如果一个客户id是123456,很容易猜到有客户id是123456.

UUID有什么问题

1.uuid有16个字节,比int(4 byte)和bigint(8 byte)占用更多存储空间

2.由于size和无序性,可能引起性能问题

Mysql的uuid原理

mysql的innodb存储引擎处理storage的方式是靠聚集索引。

聚集索引是指数据库表行中数据的物理顺序与键值的逻辑(索引)顺序相同。一个表只能有一个聚集索引,因为一个表的物理顺序只有一种情况

1.为什么要使用uuid做主键

(1).其实在innodb存储引擎下,自增长的id做主键性能已经达到了最佳。不论是存储和读取速度都是最快的,而且占的存储空间也是最小。

(2).但是在我们实际到项目中会碰到问题,历史数据表的主键id会与数据表的id重复,两张自增id做主键的表合并时,id一定会有冲突,但如果各自的id还关联了其他表,这就很不好操作。

(3).如果使用UUID,生成的ID不仅是表独立的,而且是库独立的。对以后的数据操作很有好处,可以说一劳永逸。

2.UUID优缺点

缺点: 1. 影响插入速度, 并且造成硬盘使用率低 
2. uuid之间比较大小相对数字慢不少, 影响查询速度。 
3. uuid占空间大, 如果你建的索引越多, 影响越严重

优点:出现数据拆分、合并存储的时候,能达到全局的唯一性

3.最优方案

(1).InnoDB引擎表是基于B+树的索引组织表。

(2).B+树:B+树是为磁盘或其他直接存取辅助设备而设计的一种平衡查找树,在B+树中,所有记录节点都是按键值的大小顺序存放在同一层的叶节点中,各叶节点指针进行连接。

(3).InnoDB主索引:叶节点包含了完整的数据记录。这种索引叫做聚集索引。InnoDB 的索引能提供一种非常快速的主键查找性能。不过,它的辅助索引也会包含主键列,所以,如果主键定义的比较大,其他索引也将很大。如果想在表上定义 、很多索引,则争取尽量把主键定义得小一些。InnoDB 不会压缩索引

(4).聚集索引这种实现方式使得按主键的搜索十分高效,但是辅助索引搜索需要检索两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录。

综合上述可得:

(1).如果InnoDB表的数据写入顺序能和B+树索引的叶子节点顺序一致的话,这时候存取效率是最高的。为了存储和查询性能应该使用自增长id做主键。

(2).对于InnoDB的主索引,数据会按照主键进行排序,由于UUID的无序性,InnoDB会产生巨大的IO压力,此时不适合使用UUID做物理主键,可以把它作为逻辑主键,物理主键依然使用自增ID。为了全局的唯一性,应该用uuid做索引关联其他表或做外键。

4.如果非要使用uuid做主键,下面是小建议:

如果是主从即M-S模式,最好是不使用mysql自带函数uuid来生成唯一主键,因为主表生成的uuid要再关联从表时,需要再去数据库查出这个uuid,需要多进行一次数据库交互,而且在这个时间差里面主表很有可能还有数据生成,这样就很容易导致关联的uuid出错。如果真要使用uuid,可以在Java中生成后,直接存储到DB里,这时主从的uuid就是一样的了!

### MyBatis Plus 中主键生成策略及其标注方法 在 MyBatis Plus 中,可以通过 `@TableId` 注解为主键字段指定不同的生成策略。常见的主键生成方式包括数据库自UUID、雪花算法等。对于某些特定场景,可以使用 `@KeySequence` 注解配合序列(Sequence)实现主键生成。 #### 数据库主键 当数据库本身支持主键时,可以在实体类中通过如下方式进行配置: ```java @TableId(type = IdType.AUTO) private Long id; ``` 这种方式适用于 MySQL 的 AUTO_INCREMENT 或 Oracle 的 SEQUENCE[^1]。 --- #### UUID 主键生成 如果希望使用 UUID 作为主键,则可以选择以下配置: ```java @TableId(type = IdType.UUID) private String id; ``` 该策略会自动为新记录生成一个基于 UUID 的字符串型主键[^2]。 --- #### 雪花算法 (Snowflake Algorithm) MyBatis Plus 提供了内置的雪花算法用于分布式环境下的唯一 ID 生产。可通过以下方式启用: ```java @TableId(type = IdType.ASSIGN_ID) private Long id; ``` 这种策略适合高并发场景下生成全局唯一的长整型主键[^3]。 --- #### 自定义主键值 (手动输入) 有时开发者可能需要完全控制主键值的生成逻辑,在这种情况下可以设置主键类型为 `INPUT` 并自行赋值给主键属性: ```java @Data @KeySequence(value = "SEQ_ACL_ROLE", clazz = Integer.class) // 定义使用的 Sequence 名称 public class AclUser implements Serializable { private static final long serialVersionUID = 780903014942735924L; @TableId(value = "ID", type = IdType.INPUT) private Integer id; } ``` 上述代码片段展示了如何利用 `@KeySequence` 注解绑定数据库中的序列对象,并将其应用于主键生成过程。需要注意的是,这种方法依赖于底层数据库的支持(如 PostgreSQL Oracle),因此需确保目标表已创建相应的序列资源。 --- #### 综合示例 假设有一个名为 `user_info` 的数据表以及对应的 Java 实体类 `UserInfo`,以下是完整的主键配置案例: ```java import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import org.springframework.data.annotation.KeySequence; @Data @KeySequence(value = "USER_INFO_SEQ") // 关联数据库中的 USER_INFO_SEQ 序列 public class UserInfo { @TableId(value = "id", type = IdType.INPUT) private Long id; // 手动填充或由 KeySequence 自动生成 private String username; private String email; } ``` 在此基础上,框架会在保存操作前调用指定的序列获取下一个可用编号并赋予至 `id` 属性。 --- ### 注意事项 - 如果采用 `ASSIGN_ID` 类型,则无需额外编码即可完成高效可靠的主键分配。 - 对于复杂业务需求或者跨平台兼容考虑,请优先选用标准化方案而非硬编码固定规则。 - 当项目涉及多套异构存储引擎时应审慎评估各选项优劣再做决定[^4]。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值