问题:如何在分布式集群中生成全局唯一的ID?

本文介绍了三种全局唯一ID生成方法。UUID能生成32位全局唯一码,但无序入库性能差;数据库自增主键可提高性能,但依赖数据库,挂掉则服务不可用;SnowFlake算法生成的ID分四部分,不依赖DB,性能高且ID递增,但依赖系统时钟一致性。

https://www.cnblogs.com/haoxinyue/p/5208136.html

方法一:UUID
UUID是通用唯一识别码 (Universally Unique Identifier),在其他语言中也叫GUID,可以生成一个长度32位的全局唯一识别码。

//e493a18e-61d0-499a-87c1-650d03735edb
String uuid = UUID.randomUUID().toString()
1
2
UUID可以保证全局唯一,但是占用32位有些太长,而且是无序的,入库时性能较差。

众所周知,关系型数据库的索引大都是B+树的结构,拿ID字段来举例,索引树的每一个节点都存储着若干个ID。 
如果我们的ID按递增的顺序来插入,比如陆续插入8,9,10,新的ID都只会插入到最后一个节点当中。当最后一个节点满了,会裂变出新的节点。这样的插入是性能比较高的插入,因为这样节点的分裂次数最少,而且充分利用了每一个节点的空间。 
但是,如果我们的插入完全无序,不但会导致一些中间节点产生分裂,也会白白创造出很多不饱和的节点,这样大大降低了数据库插入的性能。

方法二:数据库自增主键
为了提高性能,在分布式系统中可以用DB proxy请求不同的分库,每个分库设置不同的初始值,步长和分库数量相等:这样一来,DB1生成的ID是1,4,7,10,13….,DB2生成的ID是2,5,8,11,14…..

ID的生成对数据库严重依赖,不仅影响性能,而且数据库一旦挂掉,服务不可用。

方法三:SnowFlake算法
SnowFlake所生成的ID一共分成四部分:

第一位:占用1bit,其值始终是0,没有实际作用。
时间戳:占用41bit,精确到毫秒,总共可以容纳约140年的时间。
工作机器ID:占用10bit,其中高位5bit是数据中心ID(datacenterId),低位5bit是工作节点ID(workerId),做多可以容纳1024个节点。
序列号:占用12bit,这个值在同一毫秒同一节点上从0开始不断累加,最多可以累加到4095。
同一毫秒可以生成1024*4096个ID。

SnowFlake算法的优缺点
SnowFlake算法的优点: 
1.生成ID时不依赖于DB,完全在内存生成,高性能高可用。 
2.ID呈趋势递增,后续插入索引树的时候性能较好。 
SnowFlake算法的缺点: 
依赖于系统时钟的一致性。如果某台机器的系统时钟回拨,有可能造成ID冲突,或者ID乱序。
--------------------- 
作者:chenxiaokang97 
来源:优快云 
原文:https://blog.youkuaiyun.com/chen45682kang/article/details/81218299 
版权声明:本文为博主原创文章,转载请附上博文链接!

### 实现 Redis 自增流水号的全局唯一性 在分布式系统中,使用 Redis 实现全局唯一的自增流水号,可以通过 Redis 的原子操作 `INCR` 来确保生成的流水号在并发环境下仍然保持唯一性和有序性。Redis 的所有命令是单线程的,因此 `INCR` 操作具有天然的原子性,可以安全地用于生成全局唯一ID [^1]。 #### Redis Key 设计 为了生成全局唯一的流水号,可以使用一个固定的 Key,例如 `globalSerialNumber`,每次调用 `INCR` 命令时,Redis 会自动将该 Key 的值增加 1,并返回新的值。这种方式生成的流水号是全局唯一的,适用于订单编号、日志追踪号等需要唯一标识的场景。 ```java @Component public class RedisGlobalSerialNumberGenerator { private final RedisTemplate<String, String> redisTemplate; public RedisGlobalSerialNumberGenerator(RedisTemplate<String, String> redisTemplate) { this.redisTemplate = redisTemplate; } public String generateGlobalSerialNumber() { String key = "globalSerialNumber"; Long sequence = redisTemplate.opsForValue().increment(key); return String.format("%010d", sequence); } } ``` 该方法生成的流水号格式为 `0000000001`,适用于需要全局唯一标识的业务场景。 #### Redis 集群部署 为了确保 Redis 在分布式系统中的高可用性,可以采用 Redis 集群部署。通过 Redis 集群,可以实现数据的分片和故障转移,从而提高系统的可用性和扩展性。在 Redis 集群中,每个节点负责一部分数据,客户端可以通过 Redis 集群客户端库自动发现和连接到正确的节点[^2]。 #### 数据持久化与恢复 为了防止 Redis 宕机后数据丢失,可以启用 Redis 的持久化功能,例如 RDB(Redis Database Backup)和 AOF(Append Only File)。RDB 是通过快照的方式将内存中的数据保存到磁盘上,而 AOF 则是通过记录所有写操作命令来实现数据的持久化。通过持久化功能,即使 Redis 宕机,也可以通过磁盘上的数据恢复到最近的状态 [^1]。 #### 结合数据库实现流水号的持久化 在某些业务场景中,流水号不仅需要在 Redis 中生成,还需要持久化到数据库中,以便在 Redis 宕机后能够恢复流水号的状态。可以通过定时任务将 Redis 中的流水号同步到数据库中,或者在每次生成流水号时同时更新数据库中的流水号记录。这样可以确保即使 Redis 宕机,流水号的状态也不会丢失 [^3]。 #### 异常处理与容错机制 在实际运行中,需要考虑 Redis 服务不可用的情况。可以通过重试机制、熔断机制和降级策略来处理 Redis 服务不可用的情况。例如,当 Redis 服务不可用时,可以尝试从数据库中获取流水号,并在 Redis 恢复后将流水号同步回 Redis。此外,还可以使用 Redis 的哨兵模式或集群模式来实现自动故障转移,确保系统的高可用性 [^2]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值