【问题标题】:Proposal: locally unique GUID alternative建议:本地唯一的 GUID 替代方案
【发布时间】:2017-10-30 05:37:56
【问题描述】:

问题

我正在寻找有关本地唯一的 GUID 替代方案的反馈,并满足以下要求:

  1. 发生碰撞的几率非常低(以至于我们宁愿每年碰撞一次也不愿进行检查)
  2. 不会泄露敏感信息,例如存在多少项
  3. 在 SQL 数据库中具有高性能
  4. 可复制/粘贴以供手动查询(作为查询字符串和查询结果)
  5. 无需编码即可用作 URI 组件

为了满足要求,我决定采用64位无符号整数的形式。它在 CPU 上很容易,主键使用美观且小巧,半人类可读,仅数字,并且在手动查询时易于复制/粘贴。 (作为反例,BLOB 严重阻碍了大多数 SQL 数据库的手动查询。)

此外,Percona demonstrates 单调递增的值作为主键的性能要好得多,尤其是在插入速度方面,因此这是一个目标。

建议的结构

从左到右,最重要的位在左边

  1. 46 位。 时间戳。 Unix 时间,以毫秒为单位。 (至少在 C# 中,亚毫秒级的时间并不容易获得。)这将持续到 4199 年的某个地方。它为我们提供了单调递增的值。
  2. 8 位。 本地 IP 的一部分。机器内部 IP 地址的最后一个组成部分,是最快的可用网络接口。大多数服务器应该是以太网 LAN。
  3. 10 位。 唯一性。一个静态计数器,在使用时递增(互锁),带有环绕。

碰撞

任何时候都有 1/1024 (~0.1%) 的碰撞几率:

  1. 两个系统共享相同的最后一个 IP 地址组件并且在相同的毫秒内进行呼叫。 这是完全可以避免的。
  2. 系统时钟被调回并且它在时间更改前的同一毫秒内进行调用。 这应该是符合要求的极少数情况。

限制

有趣的是,我们似乎满足了要求(#2 是一个狡猾的要求)。让我们来看看其中的一些限制。

  1. 必须仔细维护服务器的本地 IP 地址 - 即使在不同的数据中心之间(如果适用)。
  2. 我们不能支持超过 255 台服务器 - 如果 IP 存在其他限制,可能会更少。
  3. 我们泄露了有关哪些标识符是由同一服务器创建的信息。不过,我相信许多 GUID 实现也是如此。
  4. 可以通过检查用户自己的请求之间的计数器增量来获取有关流量的信息。由于计数器用于各种类型的数据,并且以难以归因于任何特定类型的数据的方式快速增加,因此效率会降低。
  5. 标识符比具有大量随机性的标识符更容易猜测。蛮力攻击每尝试毫秒需要大约 512 次调用(唯一性)。理想情况下,这种攻击不会产生任何结果,即无论标识符是不存在还是不属于用户,系统都会报告“未经授权”,并且可以抵抗定时攻击。实际上,我们假设专门的攻击者会发现漏洞。

注意事项

  1. 限制 #1 和 #2 必须适合公司。

  2. 限制 #3 似乎在现有 GUID 实现中被认为是可以接受的,并且是我愿意接受的。

  3. 限制 #4 是一个棘手的限制。这些信息有多敏感? “所以我们每分钟插入 10K 次,插入到未知数量的表中。”相对交易量确实提供了更多洞察力:“在 08:00-09:00 之间,活动量是一个小时前的两倍。”尽管如此,这通常是给定领域的常识。意外的峰值可能会泄漏更多信息。 “所以系统在凌晨03:00努力工作。”这一切有多糟糕?从公开自动增量标识符的公司数量来看,我们可能会说这是一种改进,但它可能会破坏交易。

  4. 我们可以使用(加密)随机位作为唯一性来处理限制 #4,但这会引入第三次冲突机会:每当系统在一毫秒内生成多个标识符时。生日悖论在那里尤其成问题。

  5. 如果我们允许时间戳在 2527 中回绕,我们可以释放 2 位。自私和对后代不敏感,或者傲慢地假设我们的代码会被使用更长时间? :-)

还有什么?

我欢迎您的反馈、改进、想法和我错过的限制!你会如何解决这个问题?

【问题讨论】:

  • 更新:为了确保服务器或应用程序实例之间的唯一性,我做了一个实现,其静态初始化器尝试从 config 中提取服务器标识符,用作中间 8位。如果失败,它会退回到检查最快的网络适配器 IP(即 LAN IP)的最后一部分。最后,它恢复为使用 0,这实际上仅用于单元测试。
  • 更新:45 位时间戳就足够了。这允许使用有符号整数而不会得到负数(是的,你,SQL Server)。所以我们只触及 63 个最低有效位。

标签: indexing primary-key collision guid identifier


【解决方案1】:

冒着成为那种回答“你为什么要这样做?”的人的风险。 -我想知道您的潜在业务问题是什么,导致您无法使用 GUID?

BIGINT、GUID 和 HashTables..

我使用BIGINT 作为主键,它使所有内容保持顺序、不臃肿且快速。这适用于所有内部工作,即在我的存储过程中、SQL 连接等。然后我有一个带有GUID 的哈希表,它成为外部调用者的起点。

由于我使用表继承,BIGINT ID 可以用作我的哈希表中的顺序主键,因为所有 ID 在整个数据库中都是唯一的(但仍然是连续的)。然后更进一步,我在哈希表上创建了一个复合键,其中包括GUID 的最后几位数字,然后我根据这些值对哈希表进行分区,以便每个值单独存储在磁盘上并且仍然是连续的,但给出我很自然地在GUID 上建立索引,我正在查找。

当我最初开始这样做时,我在这里发布了一个操作方法(不包括分区部分):

What is the fastest way to look for duplicate uniqueidentifier in Sql Server?

针对 100,000,000 条记录的初始性能测试非常轻松。

不是您问题的答案,但对某人来说可能值 2 美分。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-08
    • 2011-10-02
    • 2013-01-22
    • 2018-11-21
    相关资源
    最近更新 更多