Skip to content

面试速答(先看这里)

**一句话结论:**在单体应用中,我们可以通过数据库的主键ID来生成唯一的ID,但是如果数据量变大,就需要进行分库分表,在分库分表之后,如何生成一个全局唯一的ID,就是一个关键的问题。

60秒标准回答:

在单体应用中,我们可以通过数据库的主键ID来生成唯一的ID,但是如果数据量变大,就需要进行分库分表,在分库分表之后,如何生成一个全局唯一的ID,就是一个关键的问题

通常情况下,对于分布式ID来说,我们一般希望他具有以下几个特点

通常,在分布式ID的生成方案主要有以下6种

**答题顺序:**结论 → 原理/机制 → 关键流程 → 场景与取舍 → 易错点

回答主线:

  • **要点1:**UUID(Universally Unique Identifier)全局唯一标识符,是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。
  • **要点2:**UUID的优点就是他的性能比较高,不依赖网络,本地就可以生成,使用起来也比较简单。
  • **要点3:**但是他也有两个比较明显的缺点,那就是长度过长和没有任何含义 。
  • **要点4:**分布式ID也可以使用数据库的自增ID,但是这种实现中就要求一定是一个单库单表才能保证ID自增且不重复,这就带来了一个 单点故障的问题 。
  • **要点5:**一旦这个数据库挂了,那整个分布式ID的生成服务就挂了。

**记忆锚点:**Redis → UUID → 号段模式 → UidGenerator → Universally → Identifier

关键取舍:

  • 高性能&高可用:需要保证ID的生成是稳定且高效的。
  • 标准的UUID格式为:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12),共32个字符,通常由以下几部分的组合而成:当前日期和时间,时钟序列,全局唯一的IEEE机器识别号 UUID的优点就是他的性能比较高,不依赖网络,本地就可以生成,使用起来也比较简单。
  • 用UUID当做分布式ID,存在着不适合范围查询、不方便展示以及查询效率低等问题。

易错提醒:

  • 但是Redis存在数据丢失的情况,无论是那种持久化机制,都无法完全避免。

加分表达:

  • 在单体应用中,我们可以通过数据库的主键ID来生成唯一的ID,但是如果数据量变大,就需要进行分库分表,在分库分表之后,如何生成一个全局唯一的ID,就是一个关键的问题。
  • 而且还存在一个性能问题,如果高并发访问数据库的话,就会带来阻塞问题。

追问准备:

  • 围绕「Redis」:底层原理是什么?使用时有哪些边界和常见坑?
  • 围绕「UUID」:底层原理是什么?使用时有哪些边界和常见坑?
  • 围绕「号段模式」:底层原理是什么?使用时有哪些边界和常见坑?
  • 如果线上出现异常,你会如何定位、验证并规避?

典型回答 ​

在单体应用中,我们可以通过数据库的主键ID来生成唯一的ID,但是如果数据量变大,就需要进行分库分表,在分库分表之后,如何生成一个全局唯一的ID,就是一个关键的问题。

通常情况下,对于分布式ID来说,我们一般希望他具有以下几个特点:

  • 全局唯一:必须保证全局唯一性,这个是最基本的要求。
  • 高性能&高可用:需要保证ID的生成是稳定且高效的。
  • 递增:根据不同的业务情况,有的会要求生成的ID呈递增趋势,也有的要求必须单调递增(后一个ID必须比前一个大),也有的没有严格要求。

通常,在分布式ID的生成方案主要有以下6种:

  • UUID
  • 数据库自增ID
  • 号段模式
  • 基于Redis 实现
  • 雪花算法
  • 第三方ID生成工具

扩展知识 ​

UUID ​

UUID(Universally Unique Identifier)全局唯一标识符,是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。

标准的UUID格式为:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12),共32个字符,通常由以下几部分的组合而成:当前日期和时间,时钟序列,全局唯一的IEEE机器识别号

UUID的优点就是他的性能比较高,不依赖网络,本地就可以生成,使用起来也比较简单。

但是他也有两个比较明显的缺点,那就是长度过长和没有任何含义。长度自然不必说,他有32位16进制数字。对于"550e8400-e29b-41d4-a716-446655440000"这个字符串来说,我想任何一个程序员都看不出其表达的含义。一旦使用它作为全局唯一标识,就意味着在日后的问题排查和开发调试过程中会遇到很大的困难。

📄 ✅什么是UUID,能保证唯一吗?

打开文档:✅什么是UUID,能保证唯一吗?

用UUID当做分布式ID,存在着不适合范围查询、不方便展示以及查询效率低等问题。

📄 ✅uuid和自增id做主键哪个好,为什么?

打开文档:✅uuid和自增id做主键哪个好,为什么?

数据库自增 ​

分布式ID也可以使用数据库的自增ID,但是这种实现中就要求一定是一个单库单表才能保证ID自增且不重复,这就带来了一个单点故障的问题。

一旦这个数据库挂了,那整个分布式ID的生成服务就挂了。而且还存在一个性能问题,如果高并发访问数据库的话,就会带来阻塞问题。

号段模式 ​

号段模式是在数据库的基础上,为了解决性能问题而产生的一种方案。他的意思就是每次去数据库中取ID的时候取出来一批,并放在缓存中,然后下一次生成新ID的时候就从缓存中取。这一批用完了再去数据库中拿新的。

📄 ✅详细介绍下号段模式生成分布式ID的原理和优缺点?

打开文档:✅详细介绍下号段模式生成分布式ID的原理和优缺点?

而为了防止多个实例之间发生冲突,需要采用号段的方式,即给每个客户端发放的时候按号段分开,如客户端A取的号段是1-1000,客户端B取的是1001-2000,客户端C取的是2001-3000。当客户端A用完之后,再来取的时候取到的是3001-4000。

号段模式的好处是在同一个客户端中,生成的ID是顺序递增的。并且不需要频繁的访问数据库,也能提升获取ID的性能。缺点是没办法保证全局顺序递增,也存在数据库的单点故障问题。

其实很多分库分表的中间件的主键ID的生成,主要采用的也是号段模式,如TDDL Sequence

Redis 实现 ​

基于数据库可以实现,那么基于Redis也是可以的,我们可以依赖Redis的incr命令实现ID的原子性自增。

Redis的好处就是可以借助集群解决单点故障的问题,并且他基于内存性能也比较高。

但是Redis存在数据丢失的情况,无论是那种持久化机制,都无法完全避免。

📄 ✅Redis的持久化机制是怎样的?

打开文档:✅Redis的持久化机制是怎样的?

雪花算法 ​

📄 什么是雪花算法,怎么保证不重复的?

打开文档:什么是雪花算法,怎么保证不重复的?

第三方工具 ​

除了以上方案以外,还有一些第三方的工具可以用来实现分布式ID,如百度的UidGenerator、美团的Leaf以及滴滴的Tinyid等等。

这些框架在功能上有的是整合了我们前面提到的多种实现方式,有的是针对不同的方式做了改进,如解决雪花算法的时钟拨回问题等。