分布式序列号的生成?

我通常在过去使用数据库序列来实现序列号的生成

例如使用Postgres SERIALtypeshttp://www.neilconway.org/docs/sequences/

我很好奇,但是如何为没有数据库的大型分布式系统生成序列号。 有没有人有任何经验或build议的最佳做法,以线程安全的方式为多个客户端实现序列号的生成?

好吧,这是我现在第一次看到的一个非常古老的问题。

您需要区分序列号唯一ID (可选)按照特定标准(通常是生成时间)进行松散sorting。 真正的序列号意味着知道所有其他工作人员所做的事情,因此需要共享状态。 以分布式,高规模的方式做这件事并不容易。 您可以查看networking广播,每个工作人员的窗口范围以及分布式哈希表等独特的工作人员ID ,但这是很多工作。

唯一ID是另外一个问题,有几种分散生成唯一ID的好方法:

a)您可以使用Twitter的Snowflake IDnetworking服务 。 雪花是一个:

  • 联网服务,即您进行networking呼叫以获取唯一的ID;
  • 它产生64位唯一ID,按照生成时间sorting;
  • 该服务具有高度可扩展性和(潜在)高度可用性; 每个实例每秒可以产生数千个ID,并且可以在LAN / WAN上运行多个实例;
  • 用Scala编写,运行在JVM上。

b)您可以使用从UUID和雪花ID的制作方式中产生方式 ,在客户端自己生成唯一的ID。 有多种select,但是可以采取以下方式:

  • 最重要的40位左右: 时间戳; ID的生成时间。 (我们使用时间戳的最高有效位来使得ID可以按照生成时间sorting。)

  • 接下来的14位: 每个发生器计数器,每个发生器对于每个生成的新ID都增加一个计数器 。 这确保了在同一时刻(相同时间戳)生成的ID不重叠。

  • 最后10位左右: 每个发生器的唯一值。 使用这个,我们不需要在发生器之间做任何同步(这是非常困难的),因为所有发生器由于这个值而产生不重叠的ID。

c)您可以使用时间戳和随机值在客户端上生成ID 这样可以避免需要知道所有发生器,并为每个发生器分配一个唯一的值。 另一方面,这些ID不能保证是全球唯一的,它们很可能是唯一的。 (为了碰撞,一个或多个发生器必须在同一时间创build相同的随机值)。

  • 最重要的32位: 时间戳, ID的生成时间。
  • 最不重要的32位: 随机性的32位,为每个ID重新生成。

d)简单的方法是使用UUID / GUID 。

你可以让每个节点有一个唯一的ID(你可能有),然后把它加到序列号上。

例如,节点1生成序列001-00001 001-00002 001-00003等,节点5生成005-00001 005-00002

独特 :-)

或者,如果你想要某种集中式的系统,你可以考虑让你的序列服务器分块发放。 这显着降低了开销。 例如,您不需要从中央服务器为每个必须分配的ID请求一个新的ID,而是从中央服务器请求以10,000个为单位的ID,然后在用完时只需要执行另一个networking请求。

现在有更多的select。

你这个问题是“古老的”,我到了这里,所以我认为留下我所知道的选项可能是有用的(到目前为止):

  • 你可以试试Hazelcast 。 在它的1.9版本中,它包含一个java.util.concurrent.AtomicLong的分布式实现
  • 你也可以使用Zookeeper 。 它提供了创build序列节点的方法(附加到znode名称,我更喜欢使用节点的版本号)。 要小心这个你:如果你不想在你的序列中遗漏数字,它可能不是你想要的。

干杯

如果真的必须全球连贯,而不是简单的独特,那么我会考虑创build一个简单的服务来分配这些数字。

分布式系统依赖大量小型服务进行交互,而对于这种简单的任务,您真的需要还是真的会从其他复杂的分布式解决scheme中受益呢?

这可以用Redisson完成。 它实现了AtomicLong分布式和可扩展版本。 这里是例子:

 Config config = new Config(); config.addAddress("some.server.com:8291"); Redisson redisson = Redisson.create(config); RAtomicLong atomicLong = redisson.getAtomicLong("anyAtomicLong"); atomicLong.incrementAndGet(); 

有几个策略; 但是我所知道的没有一个可以真正地分布并给出一个真实的序列。

  1. 有一个中央号码发生器。 它不一定是一个大的数据库。 memcached有一个快速的primefaces计数器,在绝大多数情况下,对于整个群集来说足够快。
  2. 为每个节点分开一个整数范围(如Steven Schlanskter的答案 )
  3. 使用随机数字或UUID
  4. 使用一些数据,连同节点的ID,并将其全部散列(或hmac )

个人而言,如果我想拥有大部分连续的空间,我会倾向于使用UUID或memcached。

为什么不使用(线程安全的)UUID生成器?

我应该扩大这个。

UUID保证是全球唯一的(如果你避免那些基于随机数的唯一性很可能)。

无论您使用多less个UUID生成器,都可以满足您的“分布式”要求,即每个UUID的全局唯一性。

您可以通过select“线程安全”的UUID生成器来满足您的“线程安全”要求。

假定每个UUID的保证全局唯一性满足您的“序列号”要求。

请注意,许多数据库序列号实现(例如Oracle)不保证单调递增或(甚至)增加序列号(基于每个“连接”)。 这是因为连续的一批序列号在每个连接的基础上被分配在“caching”的块中。 这保证了全球唯一性保持足够的速度。 但是当多个连接被分配时,实际分配的序列号(随着时间的推移)可能会混乱!

分布式ID生成可以使用Redis和Lua进行存档。 在Github中可用的实现。 它产生一个分布式和k-sortable独特的ID。

我写了一个简单的服务,可以生成半唯一的非顺序的64位长号码。 它可以部署在多台机器上以实现冗余和可扩展性。 它使用ZeroMQ进行消息传递。 有关它如何工作的更多信息,请参阅gitub页面: zUID

使用数据库,您可以使用单个内核以每秒1.000+的增量进行操作。 这很容易。 您可以使用自己的数据库作为后端来生成该数字(因为它应该是自己的聚合,在DDD方面)。

我有什么似乎是类似的问题。 我有几个分区,我想为每个分区得到一个抵消计数器。 我实现了这样的东西:

 CREATE DATABASE example; USE example; CREATE TABLE offsets (partition INTEGER, offset LONG, PRIMARY KEY (partition)); INSERT offsets VALUES (1,0); 

然后执行下面的语句:

 SELECT @offset := offset from offsets WHERE partition=1 FOR UPDATE; UPDATE offsets set offset=@offset+1 WHERE partition=1; 

如果你的应用程序允许你,你可以立即分配一个块(这是我的情况)。

 SELECT @offset := offset from offsets WHERE partition=1 FOR UPDATE; UPDATE offsets set offset=@offset+100 WHERE partition=1; 

如果您需要进一步的吞吐量,无法预先分配偏移量,您可以使用Flink实现自己的服务以进行实时处理。 我能够获得每个分区100K的增量。

希望能帮助到你!

问题类似于:在iscsi世界中,每个lns /卷必须由运行在客户端的发起者唯一地识别。 iscsi标准说,前几位必须代表存储提供商/制造商的信息,其余的则单调递增。

类似地,可以使用分布式节点系统中的初始位来表示节点ID,其余的可以单调递增。