我一直在浏览网络,试图找到一种解决方案,使我们能够在区域分布式环境中生成唯一的 ID。
我查看了以下选项(除其他外):
雪花(推特)
- 这似乎是一个很棒的解决方案,但我只是不喜欢仅仅为了创建 ID 而必须管理另一个软件所带来的额外复杂性;
- 现阶段缺乏文档,所以我认为这不是一个好的投资;
- 节点需要能够使用 Zookeeper 相互通信(延迟/通信失败怎么办?)
UUID
- 看看它:550e8400-e29b-41d4-a716-446655440000;
- 它是一个 128 位 ID;
- 存在一些已知的冲突(取决于我猜测的版本)看到这个帖子 https://stackoverflow.com/questions/3038023/uuid-collision-risk-using-different-algorithms.
关系数据库(如 MYSQL)中的自动增量
- 这看起来很安全,但不幸的是,我们没有使用关系数据库(可扩展性偏好);
- 我们可以为此部署一个 MySQL 服务器,就像 Flickr 所做的那样,但同样,这会引入另一个故障点/瓶颈。也增加了复杂性。
非关系数据库(如 COUCHBASE)中的自动增量
- 这可以工作,因为我们使用 Couchbase 作为数据库服务器,但是;
- 当我们在不同地区有多个集群、延迟问题、网络故障时,这一点就行不通了:在某些时候,ID 会根据流量大小而发生冲突;
我建议的解决方案(这是我需要帮助的)
假设我们的集群由 5 个不同区域(非洲、欧洲、亚洲、美洲和大洋洲)的 10 个 Couchbase 节点和 10 个应用程序节点组成。这是为了确保从最靠近用户的位置提供内容(以提高速度)并确保发生灾难等情况时的冗余。
现在,任务是生成在复制(和平衡)发生时不会发生冲突的 ID,我认为这可以通过 3 个步骤来实现:
Step 1
所有区域都将被分配整数 ID(唯一标识符):
- 1 - 非洲;
- 2 - 美国;
- 3 - 亚洲;
- 4 - 欧洲;
- 5 - 大洋洲。
Step 2
为添加到集群的每个应用程序节点分配一个 ID,请记住,一个集群中可能最多有 99 999 台服务器(尽管我怀疑:只是作为一种安全预防措施)。这看起来像这样(假IP):
- 00001 - 192.187.22.14
- 00002 - 164.254.58.22
- 00003 - 142.77.22.45
- 等等。
请注意,所有这些都位于同一集群中,因此这意味着每个区域可以有节点 00001。
Step 3
对于插入数据库的每条记录,将使用递增的 ID 来标识它,这就是它的工作原理:
Couchbase 提供了一个增量功能,我们可以使用它在集群内部创建 ID。为了确保冗余,将在集群内创建 3 个副本。由于它们位于同一位置,因此我认为应该可以安全地假设,除非整个集群发生故障,否则负责此操作的节点之一将可用,否则可以增加多个副本。
将所有内容整合在一起
假设用户从欧洲注册:
服务请求的应用程序节点将获取区域代码(4在这种情况下),获得它自己的ID(比如说00005) 然后得到一个递增的 ID (1)来自 Couchbase(来自同一集群)。
我们最终得到 3 个组件:4, 00005,1
。现在,要从中创建一个 ID,我们只需将这些组件加入到4.00005.1
。为了让它变得更好(我对此不太确定),我们可以连接(不是将它们相加)最终得到的组件:4000051
.
在代码中,这看起来像这样:
$id = '4'.'00005'.'1';
NB: Not $id = 4+00005+1;
.
Pros
- ID 看起来比 UUID 更好;
- 它们看起来足够独特。即使另一个区域中的节点生成了相同的递增 ID,并且与上面的节点 ID 相同,我们也总是有区域代码来将它们分开;
- 它们仍然可以存储为整数(可能是大无符号整数);
- 这都是架构的一部分,没有增加复杂性。
Cons
我知道每个解决方案都有缺陷,而且可能比我们表面上看到的还要多。您能发现整个方法有什么问题吗?
预先感谢您的帮助 :-)
EDIT
正如@DaveRandom建议的,我们可以添加第四步:
Step 4
我们可以生成一个随机数并将其附加到 ID 中以防止可预测性。实际上,你最终会得到这样的结果:
4000051357
而不是仅仅4000051
.