面试官:分库分表后如何生成全局ID?

文章讨论了在分布式环境中,分库分表后不能使用自增ID作为主键的问题,介绍了UUID的全局唯一性但存在的长和低效问题,以及雪花ID的设计、优点和挑战,特别提到了雪花算法改进版本的百度UidGenerator在解决时间回拨问题上的应用。
摘要由CSDN通过智能技术生成

分库分表后就不能使用自增 ID 来作为表的主键了,因为数据库自增 ID 只适用于单机环境,但如果是分布式环境,是将数据库进行分库、分表或数据库分片等操作时,那么数据库自增 ID 就会生成重复 ID,从而导致业务查询上的问题。所以此时,可以使用 UUID 或雪花 ID 来作为全局主键 ID。

1.UUID作为全局ID

UUID(Universally Unique Identifier)是一种全局唯一标识符,它保证在空间和时间上的唯一性。通常由 128 位的数字组成,采用 32 位的十六进制数表示,格式为 8-4-4-4-12 这样的 36 个字符(32 个字母数字字符和 4 个短横线),例如 550e8400-e29b-41d4-a716-446655440000。 UUID 在 Java 中的实现如下:

 

java

复制代码

import java.util.UUID; public class UUIDExample { public static void main(String[] args) { // Generate a random UUID UUID uuid = UUID.randomUUID(); System.out.println("Random UUID: " + uuid); // Convert UUID to string String uuidString = uuid.toString(); System.out.println("UUID as string: " + uuidString); // Convert string to UUID UUID parsedUuid = UUID.fromString(uuidString); System.out.println("Parsed UUID: " + parsedUuid); } }

UUID 存在的问题

虽然 UUID 可以保证全局唯一,但并不推荐使用 UUID 来作为分库分表后的主键 ID,因为 UUID 有两个问题:

  1. UUID 太长,且生成效率较低。
  2. UUID 没有任何业务含义,不连续且没有任何顺序可言。

2.雪花ID作为全局ID

雪花 ID(Snowflake ID)是一个用于分布式系统中生成唯一 ID 的算法,由 Twitter 公司提出。它的设计目标是在分布式环境下高效地生成全局唯一的 ID,具有一定的有序性。 雪花 ID 的结构如下所示(共 64 位): 

 这四部分代表的含义:

  1. 符号位:最高位是符号位,始终为 0,1 表示负数,0 表示正数,ID 都是正整数,所以固定为 0。
  2. 时间戳部分:由 41 位组成,精确到毫秒级。可以使用该 41 位表示的时间戳来表示的时间可以使用 69 年。
  3. 节点 ID 部分:由 10 位组成,用于表示机器节点的唯一标识符。在同一毫秒内,不同的节点生成的 ID 会有所不同。
  4. 序列号部分:由 12 位组成,用于标识同一毫秒内生成的不同 ID 序列。在同一毫秒内,可以生成 4096 个不同的 ID。

Java 版雪花算法实现

接下来,我们来实现一个 Java 版的雪花算法:

 

java

复制代码

public class SnowflakeIdGenerator { // 定义雪花 ID 的各部分位数 private static final long TIMESTAMP_BITS = 41L; private static final long NODE_ID_BITS = 10L; private static final long SEQUENCE_BITS = 12L; // 定义起始时间戳(可根据实际情况调整) private static final long EPOCH = 1609459200000L; // 定义最大取值范围 private static final long MAX_NODE_ID = (1L << NODE_ID_BITS) - 1; private static final long MAX_SEQUENCE = (1L << SEQUENCE_BITS) - 1; // 定义偏移量 private static final long TIMESTAMP_SHIFT = NODE_ID_BITS + SEQUENCE_BITS; private static final long NODE_ID_SHIFT = SEQUENCE_BITS; private final long nodeId; private long lastTimestamp = -1L; private long sequence = 0L; public SnowflakeIdGenerator(long nodeId) { if (nodeId < 0 || nodeId > MAX_NODE_ID) { throw new IllegalArgumentException("Invalid node ID"); } this.nodeId = nodeId; } public synchronized long generateId() { long currentTimestamp = timestamp(); if (currentTimestamp < lastTimestamp) { throw new IllegalStateException("Clock moved backwards"); } if (currentTimestamp == lastTimestamp) { sequence = (sequence + 1) & MAX_SEQUENCE; if (sequence == 0) { currentTimestamp = untilNextMillis(lastTimestamp); } } else { sequence = 0L; } lastTimestamp = currentTimestamp; return ((currentTimestamp - EPOCH) << TIMESTAMP_SHIFT) | (nodeId << NODE_ID_SHIFT) | sequence; } private long timestamp() { return System.currentTimeMillis(); } private long untilNextMillis(long lastTimestamp) { long currentTimestamp = timestamp(); while (currentTimestamp <= lastTimestamp) { currentTimestamp = timestamp(); } return currentTimestamp; } }

调用代码如下:

 

java

复制代码

public class Main { public static void main(String[] args) { // 创建一个雪花 ID 生成器实例,传入节点 ID SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1); // 生成 ID long id = idGenerator.generateId(); System.out.println(id); } }

其中,nodeId 表示当前节点的唯一标识,可以根据实际情况进行设置。generateId 方法用于生成雪花 ID,采用同步方式确保线程安全。具体的生成逻辑遵循雪花 ID 的位运算规则,结合当前时间戳、节点 ID 和序列号生成唯一的 ID。

需要注意的是,示例中的时间戳获取方法使用了 System.currentTimeMillis(),根据实际需要可以替换为其他更精确的时间戳获取方式。同时,需要确保节点 ID 的唯一性,避免不同节点生成的 ID 重复。

雪花ID存在的问题

虽然雪花算法是一种被广泛采用的分布式唯一 ID 生成算法,但它也存在以下几个问题:

  1. 时间回拨问题:雪花算法生成的 ID 依赖于系统的时间戳,要求系统的时钟必须是单调递增的。如果系统的时钟发生回拨,可能导致生成的 ID 重复。时间回拨是指系统的时钟在某个时间点之后突然往回走(人为设置),即出现了时间上的逆流情况。
  2. 时钟回拨带来的可用性和性能问题:由于时间依赖性,当系统时钟发生回拨时,雪花算法需要进行额外的处理,如等待系统时钟追上上一次生成 ID 的时间戳或抛出异常。这种处理会对算法的可用性和性能产生一定影响。
  3. 节点 ID 依赖问题:雪花算法需要为每个节点分配唯一的节点 ID 来保证生成的 ID 的全局唯一性。节点 ID 的分配需要有一定的管理和调度,特别是在动态扩容或缩容时,节点 ID 的管理可能较为复杂。

如何解决时间回拨问题?

百度 UidGenerator 框架中解决了时间回拨的问题,并且解决方案比较经典,所以咱们这里就来给大家分享一下百度 UidGenerator 是怎么解决时间回拨问题的?

UidGenerator 介绍:UidGenerator 是百度开源的一个分布式唯一 ID 生成器,它是基于 Snowflake 算法的改进版本。与传统的 Snowflake 算法相比,UidGenerator 在高并发场景下具有更好的性能和可用性。它的实现源码在:github.com/baidu/uid-g…

UidGenerator 是这样解决时间回拨问题的:UidGenerator 的每个实例中,都维护一个本地时钟缓存,用于记录当前时间戳。这个本地时钟会定期与系统时钟进行同步,如果检测到系统时钟往前走了(出现了时钟回拨),则将本地时钟调整为系统时钟。

小结

数据库自增 ID 只适用于单机数据库环境,而对于分库、分表、数据分片来说,自增 ID 不具备唯一性,所以要要使用雪花 ID 来替代数据库自增 ID。但雪花算法依然存在一些问题,例如时间回拨的问题,所以此时,可以使用雪花算法的改进框架,如百度的 UidGenerator 来作为全局 ID 的生成方案会比较好。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值