阿里面试题:表中十个字段,你主键用自增ID还是UUID,为什么?
今天咱们聊个稍微“技术性”一点的话题,那就是:面试题:表中十个字段,主键用自增ID还是UUID,为什么?
每次看到这种问题,我就想,面试官到底是想考我对数据库的理解深度,还是单纯想看看我能不能在高压下保持冷静。毕竟,UUID还是自增ID,乍一看似乎是个简单的选择题,但往往能考察出你对系统设计、性能优化,甚至是分布式架构的认识。
在这篇文章里,我将从程序员的角度来跟大家分析这个问题,看看自增ID和UUID到底哪个更适合在不同场景中使用。
一、自增ID:简洁高效,但有“潜在的隐患”
自增ID的做法大家都不陌生,它通常是数据库表的主键字段。每插入一条记录,ID会自动增长,从1开始,依次增加。
优点
简单易用:自增ID是最常见的主键类型,几乎每个数据库系统都支持,尤其在单机应用中,它是最简单的一种设计方式。
性能优越:因为ID是按顺序递增的,数据库插入操作通常非常高效。MySQL等数据库在处理自增ID时,通常能够进行优化,确保操作速度快,避免不必要的锁。
空间占用小:自增ID通常是整数类型(
INT或BIGINT),占用的存储空间较小,通常仅占4字节或8字节。
缺点
然而,自增ID并非完美无缺。它的缺点在于,当你的系统需要分布式架构时,它就容易暴露出一些问题:
分布式环境中的问题:如果你采用的是多台机器,多数据库的方式,如何确保每个节点生成的ID是唯一的呢?单纯的自增ID是无法跨服务器生成唯一值的。这就需要额外的处理,比如使用全局锁、ID生成器服务等,但这些方法通常会带来性能上的瓶颈。
暴露系统架构:自增ID是按顺序生成的,这意味着你能通过ID的大小判断记录的插入顺序。这对一些业务来说可能不是一个好事,尤其是需要隐藏数据库内部结构的场景。
存在ID碰撞的风险:在分布式环境下,如果多个服务同时向数据库插入数据,它们很可能会在ID生成的瞬间发生碰撞。即使你使用雪花算法、数据库全局锁等机制解决了ID冲突,性能和复杂度也会大大增加。
示例:自增ID的代码示例
下面是一个简单的Java代码示例,展示如何在Spring Boot中使用自增ID来作为数据库主键。
@Entity
public class User { @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // 自增主键
private Long id;
private String username;
private String email;
// getters and setters
}
上面代码使用了@GeneratedValue(strategy = GenerationType.IDENTITY)注解来标记id字段为自增主键。这里的GenerationType.IDENTITY就是自增ID的策略,表示数据库会自动生成ID。
二、UUID:看似完美,但也有代价
接下来,我们说说UUID,也就是通用唯一标识符(Universally Unique Identifier)。它是一种128位的数字表示形式,能够生成非常大的、唯一的ID。
优点
跨分布式系统的唯一性:UUID的最大优点是它的唯一性。无论你在多少台服务器、多少个数据库中生成UUID,它们都不会重复。UUID采用的是一种标准化算法,理论上可以保证全球唯一。
无序生成:UUID的生成不依赖于数据库,因此它可以在应用层生成,避免了数据库的瓶颈。在需要支持多数据源或者多个服务同时插入数据时,UUID显得格外强大。
分布式系统的灵活性:在分布式系统中,使用UUID作为主键可以避免在多个节点间进行ID协调,从而减少了分布式协调带来的复杂性和性能开销。
缺点
性能问题:虽然UUID具有唯一性,但它的缺点也是显而易见的。UUID是一个128位的字符串(通常表示为32个字符),它比自增ID要大得多。这样的大小意味着数据库索引的效率较低,尤其在查询、排序时,会导致性能下降。
空间占用大:UUID的长度较大,占用存储空间多。假设你使用
VARCHAR(36)来存储UUID,每个UUID占用36个字符(或16字节)。相比于一个自增ID的整数类型(4字节或8字节),UUID会显得占用空间更大。可读性差:UUID的可读性差,通常人类无法直接通过UUID去理解相关数据,比如你不能仅通过ID的值知道记录的插入顺序或者创建时间。
示例:UUID的代码示例
以下是一个使用UUID的简单代码示例:
@Entity
public class User { @Id
@GeneratedValue(strategy = GenerationType.AUTO) // UUID主键
private UUID id;
private String username;
private String email;
// getters and setters
}
在这里,@GeneratedValue(strategy = GenerationType.AUTO)表示JPA会自动生成主键,但具体生成什么类型的ID取决于数据库。UUID通常是@GeneratedValue(strategy = GenerationType.AUTO)的默认行为。
三、总结:自增ID与UUID的选择
什么时候使用自增ID?
单机应用:如果你的应用不涉及分布式系统,使用自增ID非常合适。它简单、直观,性能优越,空间占用小。 对外暴露ID不敏感:如果你不需要隐藏ID的生成顺序(比如ID顺序表示创建的先后),那么自增ID也可以作为一种不错的选择。
什么时候使用UUID?
分布式系统:在分布式系统中,UUID能够跨多个节点生成唯一的ID,避免了ID冲突的问题,特别适用于微服务架构。 数据安全性高:如果你需要隐藏数据的插入顺序,或者不希望别人通过ID来推测记录的创建时间,UUID会是一个不错的选择。
个人看法
我的建议是,如果你在做一个单机的应用或者数据库表数据量不是特别庞大,使用自增ID就完全够了。性能好,系统简单,而且开发起来很方便。毕竟,什么事情都得考虑成本,没人会为了一个ID而去做复杂的分布式系统吧?
但如果你的系统涉及到多台服务器、多个数据库,甚至未来可能要做分布式扩展,那么UUID绝对是更好的选择。你可以避免很多麻烦,至少能保证每个节点的ID生成都是唯一的。
所以,选择自增ID还是UUID,最重要的还是要看你当前的系统架构和未来的扩展需求。毕竟,技术选择没有绝对的对错,只有更合适与否。
这也就是我对面试题“主键用自增ID还是UUID”的看法。
-END-
以上,就是今天的分享了,看完文章记得右下角给何老师点赞,也欢迎在评论区写下你的留言。