我一般在过去使用数据库序列实现序列号生成。
例如使用Postgres SERIAL类型http://www.neilconway.org/docs/sequences/
我很好奇,因为如何为没有数据库的大型分布式系统生成序列号。对于多个客户端,是否有任何经验或建议以线程安全的方式生成序列号生成的最佳实践?
好的,这是一个非常古老的问题,我现在才开始看到。
您需要区分序列号和唯一ID(可选)按特定条件(通常为生成时间)进行松散排序。真正的序列号意味着知道所有其他工作者所做的事情,因此需要共享状态。没有简单的方法以分布式,高规模的方式这样做。您可以查看网络广播,每个工作人员的窗口范围和distributed hash tables for unique worker IDs等内容,但这需要做很多工作。
唯一ID是另一个问题,有几种以分散方式生成唯一ID的好方法:
a)你可以使用Twitter's Snowflake ID network service。雪花是:
b)您可以使用从how UUIDs派生的方法生成客户端本身的唯一ID,并制作Snowflake的ID。有多种选择,但有以下几点:
c)您可以仅使用时间戳和随机值在客户端上生成ID。这避免了需要知道所有生成器,并为每个生成器分配唯一值。另一方面,这些ID不能保证全局唯一,它们很可能是唯一的。 (要碰撞,一个或多个生成器必须在同一时间创建相同的随机值。)以下内容:
d)简单的出路,use UUIDs / GUIDs。
我编写了一个简单的服务,可以生成半唯一的非连续64位长数字。它可以部署在多台计算机上,以实现冗余和可扩展性。它使用ZeroMQ进行消息传递。有关它如何工作的更多信息,请参阅github页面: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世界中,每个luns /卷必须由客户端上运行的启动器唯一标识。 iscsi标准表示前几位必须代表存储提供商/制造商信息,其余部分单调增加。
类似地,可以使用分布式节点系统中的初始位来表示nodeID,其余的可以单调递增。
一个体面的解决方案是使用基于长时间的生成。它可以在分布式数据库的支持下完成。
您可以让每个节点都有一个唯一的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)不保证单调增加或(甚至)增加序列号(基于每个“连接”)。这是因为连续批次的序列号在每个连接的基础上分配在“高速缓存”块中。这保证了全球唯一性并保持足够的速度。但是,当多个连接分配时,实际分配的序列号(随着时间的推移)可能会混乱!
我知道这是一个老问题,但我们也面临同样的需求,无法找到满足我们需求的解决方案。我们的要求是得到一个独特的序列(0,1,2,3 ... n),因此雪花没有帮助。我们创建了自己的系统来使用Redis生成ID。 Redis是单线程的,因此它的列表/队列机制总是一次给我们一个pop。
我们所做的是,我们创建一个id的缓冲区。最初,队列将有0到20个ID,可以在请求时分派。多个客户端可以请求一个id,redis将一次弹出1个id。在左边的每次弹出之后,我们向右边插入BUFFER + currentId,这样可以保持缓冲区列表的运行。实施here
可以使用Redis和Lua存档分布式ID生成。 Github中提供的实现。它生成分布式和k可排序的唯一ID。