当前位置: 首页 > 图灵资讯 > 技术篇> 分布式系统唯一 ID 生成方案

分布式系统唯一 ID 生成方案

来源:图灵教育
时间:2023-05-31 09:22:14

分布式系统唯一 ID 生成方案_字符串

0x01:简介

系统中唯一的ID是我们在开发过程中遇到的一个常见问题,简单地说,生成ID的方法有很多,它们适应不同的性能。

0x02:常见方案

1.数据库自增长序列或字段

这是使用数据库的AUTO_最常见的方法INCREMENT

优点

  • 代码简单,方便,性能可接受
  • 数字ID有自然排序,对需要分页或排序的结果非常有帮助

缺点

  • 当数据库迁移或数据库版本支持时,需要处理不同数据库的语法和实现。
  • 只有一个主库可以在单个数据库、读写分离或一主多从的情况下生成,可能会导致单点故障。
  • 在性能不符合要求的情况下,很难扩展。
  • 分表分库会比较麻烦

优化点

  • 对于主库的单点,如果有多个master库,则每个master库设置的起始数字不同,但它们的步长相同,可以是master的数量,如1、4、7、10.master2生成2、5、8、11.master3生成3、6、9、这样,集群中的唯一ID就可以有效地生成,ID生成数据库操作的负载也可以大大降低。

二、UUID

这是最常见的方法,可以使用数据库或程序生成。

优点

  • 代码生成简单方便
  • 生成ID的性能很好,基本上没有性能问题。
  • 在数据迁移、系统数据合并或数据库变更的情况下,世界上唯一的冲突很难产生,也很容易解决。

缺点

  • 没有排名,就不能保证趋势的增长
  • UUID通常使用字符串存储,查询效率较低
  • 存储空间相对较大,一般为16或32位
  • 传输数据量大
  • 不可读

三、UUID 变种

UUID可用于解决UUID不可读问题 to Int64的方法。以及

/// <summary>/// 唯一的数字序列//////按GUID获得 </summary>public static long Guidtoint64() {    byte[] bytes = Guid.NewGuid().ToByteArray();    return BitConverter.ToInt64(bytes, 0);}

NHibernate为解决UUID无序问题提供了Comb算法(combined guid/timestamp)。保留GUID的10个字节,用另外6个字节表示GUID生成的时间(DateTime)。

/// <summary> /// Generate a new <see cref="Guid"/> using the comb algorithm. /// </summary> private Guid GenerateComb(){    byte[] guidArray = Guid.NewGuid().ToByteArray();    DateTime baseDate = new DateTime(1900, 1, 1);    DateTime now = DateTime.Now;    // Get the days and milliseconds which will be used to build        //the byte string        TimeSpan days = new TimeSpan(now.Ticks - baseDate.Ticks);    TimeSpan msecs = now.TimeOfDay;    // Convert to a byte array            // Note that SQL Server is accurate to 1/300th of a        // millisecond so we pide by 3.333333        byte[] daysArray = BitConverter.GetBytes(days.Days);    byte[] msecsArray = BitConverter.GetBytes((long)      (msecs.TotalMilliseconds / 3.333333));    // Reverse the bytes to match SQL Servers ordering        Array.Reverse(daysArray);    Array.Reverse(msecsArray);    // Copy the bytes into the guid        Array.Copy(daysArray, daysArray.Length - 2, guidArray,      guidArray.Length - 6, 2);    Array.Copy(msecsArray, msecsArray.Length - 4, guidArray,      guidArray.Length - 4, 4);    return new Guid(guidArray);}

使用上述算法进行测试,得到以下结果:作为比较,前三个是使用COMB算法获得的结果,最后12个字符串是时间顺序(统一毫秒生成的3个UUID)。如果在一段时间内再次生成,则12个字符串将大于图中显示的。后三个是直接生成的GUID。

四、Redis 生成 ID

当使用数据库生成ID性能不能满足要求时,可以使用Redis生成ID,这主要取决于Redis是单线程,也可以使用生成全球唯一的ID,可以使用RedisINCR或INCRBY来实现

Redis集群可以用来获得更高的吞吐量。如果一个集群中有5个Redis。每个Redis的值可以初始化为1、2、3、4、5,然后步长为5。每个Redis生成的ID为:

A:1,6,11,16,21B:2,7,12,17,22C:3,8,13,18,23D:4,9,14,19,24E:5,10,15,20,25

这个,任何负载到哪台机器都很难在未来进行修改。但3-5台服务器基本上可以满足,并且可以获得不同的ID。但步长和初始值必须提前需要。使用Redis集群也可以是单点故障问题。

此外,Redis更适合生成每天从0开始的流水号。例如,订单号 = 日期 + 当天的自增长号。可以每天在Redis中生成Key,用INCR累积。

优点

  • 不依赖数据库,灵活方便,性能优于数据库
  • 数字ID自然排序,对分页或需要排序的结果很好

缺点

  • 如果系统中没有Redis,则需要引入Redis,以增加系统组件的复杂性
  • 需要编码和配置的工作量相对较大

四、利用 zookeeper 生成唯一 ID

zookeper主要通过znode数据版生成序列号,可生成32位和64位的数据版本号,客户端可以使用此版本号作为唯一的序列号。

zookeper很少用来生成唯一的ID。主要是因为需要依靠zookeper,多步调用API。如果竞争激烈,需要考虑使用分布式锁。因此,在高并发的分布式环境下,性能并不理想。

五、MongoDB 的 ObjectId

MongoDB的Objectid类似于snowflake算法。它被设计成轻量级的,不同的机器可以很容易地用同样的方法生成它。MongoDB 设计从一开始就被用作分布式数据库,处理多个节点是一个核心要求。使其在分片环境中更容易生成。

六、Twittersnowflake算法 法

snowflake是twitter开源的分布式ID生成算法,结果是longID。其核心思想是:使用41bit作为毫秒,10bit作为机器ID(5bit是数据中心,5bit机器ID),12bit作为毫秒内的流量(意味着每个节点可以在每毫秒产生 4096 个 ID),最后还有一个符号位,永远是0。具体实现代码可参考:

https://github.com/twitter/snowflake