过去,我通常使用数据库序列来实现序列号生成。
例如,使用Postgres SERIAL类型http://www.neilconway.org/docs/sequences/
我很好奇如何为没有数据库的大型分布式系统生成序列号。是否有人对以线程安全的方式为多个客户端实现序列号生成有任何经验或最佳实践的建议?
过去,我通常使用数据库序列来实现序列号生成。
例如,使用Postgres SERIAL类型http://www.neilconway.org/docs/sequences/
我很好奇如何为没有数据库的大型分布式系统生成序列号。是否有人对以线程安全的方式为多个客户端实现序列号生成有任何经验或最佳实践的建议?
Answers:
好的,这是一个非常老的问题,我现在第一次看到。
您需要区分序列号和唯一ID,这些ID可以(可选)根据特定条件(通常是生成时间)进行松散排序。真实的序列号意味着知道所有其他工作人员都做了什么,因此需要共享状态。没有简单的方法可以以分布式,大规模的方式进行此操作。您可以研究诸如网络广播,每个工作人员的窗口范围以及用于唯一工作人员ID的分布式哈希表之类的东西,但这需要大量工作。
唯一ID是另一回事,有几种很好的方式可以分散生成唯一ID:
a)您可以使用Twitter的Snowflake ID网络服务。雪花是:
b)您可以使用从如何制作UUID和Snowflake的ID 派生的方法在客户端本身上生成唯一的ID。有多个选项,但有些类似:
40位左右的最高有效位:时间戳;ID的生成时间。(我们使用时间戳的最高有效位来使ID可按生成时间排序。)
接下来的14位:每个生成器计数器,每个生成器针对每个新生成的ID递增1。这样可以确保在同一时间(相同的时间戳记)生成的ID不会重叠。
最后10位左右:每个生成器的唯一值。使用此方法,我们不需要在生成器之间进行任何同步(这非常困难),因为由于该值,所有生成器都会生成不重叠的ID。
c)您可以仅使用时间戳和随机值在客户端上生成ID 。这避免了需要了解所有生成器并为每个生成器分配唯一值的需求。另一方面,不能保证此类ID 在全局上是唯一的,它们很有可能是唯一的。(要发生冲突,一个或多个生成器必须在完全相同的时间创建相同的随机值。)
d)简单的方法是使用UUID / GUID。
twitter/snowflake
不再维护
现在有更多选择。
尽管这个问题是“旧的”,但我到了这里,所以我认为(到目前为止)保留我所知道的选项可能会有用:
干杯
您可以让每个节点都有一个唯一的ID(无论如何您都可以拥有),然后将其添加到序列号之前。
例如,节点1生成序列001-00001 001-00002 001-00003等,而节点5生成005-00001 005-00002
独特 :-)
或者,如果您需要某种集中式系统,则可以考虑让序列服务器分块分发。这大大减少了开销。例如,您不必从中央服务器为必须分配的每个ID请求新的ID,而是从中央服务器以10,000为单位请求ID,然后在用尽时仅需执行另一个网络请求。
可以通过Redisson完成。它实现的分布式和可伸缩版本AtomicLong
。这是示例:
Config config = new Config();
config.addAddress("some.server.com:8291");
Redisson redisson = Redisson.create(config);
RAtomicLong atomicLong = redisson.getAtomicLong("anyAtomicLong");
atomicLong.incrementAndGet();
如果确实必须是全局顺序的,而不是唯一的,那么我将考虑创建一个简单的服务来分配这些数字。
分布式系统依赖于大量交互的小服务,对于这种简单的任务,您真的需要吗?或者您真的会从其他复杂的分布式解决方案中受益吗?
有一些策略;但我所知道的任何一个都无法真正分发并给出真实的顺序。
memcached
有一个快速的原子计数器,在大多数情况下,它对于整个集群都是足够快的。就个人而言,我倾向于UUID或memcached(如果我想拥有一个几乎连续的空间)。
为什么不使用(线程安全的)UUID生成器?
我可能应该对此进行扩展。
UUID保证是全局唯一的(如果避免使用基于随机数的UUID,则唯一性很可能是唯一的)。
无论您使用多少个UUID生成器,每个UUID的全局唯一性都可以满足您的“分布式”要求。
通过选择“线程安全” UUID生成器可以满足您的“线程安全”要求。
假定每个UUID的保证的全局唯一性可以满足您的“序列号”要求。
请注意,许多数据库序列号实现(例如Oracle)都不能保证序列号单调增加,或者(甚至)增加序列号(在每个“连接”的基础上)。这是因为在每个连接的基础上,在“缓存”块中分配了连续的序列号批次。这样可以确保全局唯一性并保持足够的速度。但是当由多个连接分配时,实际分配的序列号(随着时间的流逝)会变得混乱!
使用数据库,单核可以达到每秒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世界中,每个lun /音量必须由在客户端运行的启动器唯一地标识。iscsi标准说,前几位必须代表存储提供者/制造商的信息,其余的则单调递增。
类似地,可以使用节点的分布式系统中的初始位来表示nodeID,其余的可以单调增加。