【问题标题】:Generating a Not-Quite-Globally Unique Identifier生成一个不完全全局唯一的标识符
【发布时间】:2009-12-23 16:23:35
【问题描述】:

我发现了许多关于生成 UID 的不同问题,但据我所知,我在这里的要求有些独特 (ha)。

总结一下:我需要生成一个非常短的 ID,它是“本地”唯一的,但不必是“全局”或“普遍”唯一的。这些限制不仅仅是基于美学或空间问题,而是因为它本质上被用作硬件标签,并且受硬件限制。规格如下:

硬性要求

  • ID 只能包含十进制数字(基础数据是 BCD);
  • ID 的最大长度为 12 个字符(数字)。
  • 必须离线生成 - 数据库/网络连接并不总是可用!

软要求

  • 我们希望它以日历年和/或月份开头。由于这确实会浪费大量熵,因此我不介意对此做出妥协或完全废弃它(如有必要)。
  • 从特定机器生成的 ID 应按顺序显示。
  • ID 不必必须按机器排序 - 例如,机器 1 吐出 [123000, 124000, 125000] 和机器 2 吐出 [123500, 123600, 124100]。
  • 但是,从整体上看,越顺序越好。像 [200912000001, 200912000002, 200912000003, ...] 这样的一组 ID 将是完美的,尽管这显然不能跨多台机器进行扩展。

使用场景:

  • 本方案范围内的 ID 将由 10 台、最多 100 台不同的机器生成。
  • 生成的 ID 总数不会超过几百万个。
  • 并发性极低。单台机器不会比每 5 分钟左右更频繁地生成 ID。此外,很可能一次不超过 5 台机器会在同一小时甚至同一天生成 ID。我预计一天内在给定机器上生成的 ID 少于 100 个,而所有机器上生成的 ID 少于 500 个。
  • 少数机器 (3-5) 很可能负责生成超过 80% 的 ID。

我知道可以使用少于 12 个十进制数字将时间戳编码到 100 毫秒甚至 10 毫秒的精度,这足以保证此应用程序的“足够唯一”ID。我之所以在 SO 上问这个问题,是因为我真的很想尝试在其中加入人类可读的年/月,或者编码一些关于源机器的信息,或者两者兼而有之。

我希望有人可以帮助在这些软性要求上做出妥协……或者解释为什么考虑到其他要求,这些都是不可能的。

(P.S. 我的“原生”语言是 C#,但如果有人有任何绝妙的想法,任何语言甚至伪代码的代码都可以。)

更新:

既然我有机会睡在上面,我想我实际上要做的是默认使用时间戳编码,并允许各个安装通过定义自己的来切换到机器顺序 ID 2 位或 3 位机器 ID。这样一来,想要弄乱 ID 并打包人类可读信息的客户可以自行找出确保唯一性的方法,我们不对滥用行为负责。如果机器恰好在进行所有在线安装,我们也许可以通过提供一个服务器实用程序来处理机器 ID 来提供帮助。

【问题讨论】:

  • 软件安装后会有机会在机器上设置种子吗?
  • 我不明白为什么不这样做,尽管我不一定有任何方法来保证种子本身来自一个集中的来源。我对你的想法很感兴趣...
  • 嗯,我写的不是很清楚。我的意思是,我不认为分发会受到严格控制,所以种子必须以某种方式生成,它不能硬编码成同一个安装程序的 100 种不同变体。

标签: algorithm language-agnostic unique uniqueidentifier


【解决方案1】:

“我在这里问这个的原因 所以,是因为我真的很想 要么尝试合并 那里有人类可读的年/月或 编码一些关于 源机器,或两者兼而有之。”

首先让我说我之前已经处理过这个问题,并且尝试将有用的信息存储到序列号中是一个长期的坏主意。设备序列号应该没有意义。就像数据库记录的主键应该没有意义一样。

当您开始尝试将真实数据放入您的序列号时,您只是将 BUSINESS LOGIC 投入其中,您将不得不像维护任何其他代码一样对其进行维护。未来你会讨厌过去的你。相信我。 ;o)

如果您尝试存储日期/时间值,那么您将浪费带有无效时间/日期的数字空间。例如,您在月份字段中永远不会有任何大于 12 的值。

直接的纪元/单位时间计数器会更好,但对于每分钟只生成几个 id 的机器,你仍然会浪费很多空间。

12 位的空间并不大。查看维基百科上的 VIN 页面。空间只有几家制造商,只有几千辆汽车。他们现在正在重复使用 VIN,因为它们在其中包含含义而耗尽了空间。

http://en.wikipedia.org/wiki/VIN

这并不是说序列号中的所有含义都不好,只是严格限制在确保数字不会发生冲突。

这样的……

  • 位置 1-3:999 台机器
  • 位置 4-12:序号

这是所有你需要避免碰撞。如果你添加一个位置数字,那么当你到达 11 个位置时,你就完蛋了。

对不起,如果这听起来像咆哮。我处理了很多制造电子产品和各种机加工零件。除非有大量可用空间或辅助标签(-wow- 提供前面提到的必要 id 空间),否则它永远不会长期结束

【讨论】:

  • 我大部分同意;这里的细微差别在于该编号是客户特定的,因此与 VIN 或典型的序列号不同,如果两个不同的客户提出相同的编号并不重要。 “真实”的序列号是独立的。我和你有同样的感觉,但想看看是否有办法满足某些人喜欢的命名约定。
  • 汽车 VIN 号码难道不是一个包含有用信息的唯一序列吗?
  • Chris:汽车 VIN 不是唯一的 - 这是我的观点。我写过经销商管理系统和碰撞,虽然它们很少发生。问题是多年来和/或国家/地区有多种 VIN 编码方案发生了变化。必须修复处理这个“不独特”的独特价值的东西是昂贵的,我在其他行业也看到过类似的问题。只是试图帮助其他人尽可能避免问题。毕竟号码是免费的。 :)
【解决方案2】:

yyMMddhhmmID怎么样?

yy = two-digit year
MM = two-digit month
dd = two-digit day
hh = two-digit hour (24-hour time)
mm = two-digit minute
ID = machine-specific ID

例如:0912113201 来自带有ID = 01 的机器。

或者(如果您不喜欢两位数的年份(Y2K 哈哈)),yyyyMMIDxxxx 怎么样?

yyyy = four-digit year
MM = two-digit month
ID = machine-specific ID
xxxx = sequentially-incremented integer

例如:200912010001 来自带有ID = 01 的机器。

正如您所说,每台机器最多每五分钟生成一个标识符,这为您提供了每月 8,928 (24 * 31 * 60 / 5 = 8928) 个标识符的空间,这些标识符适合 xxxx。如果您需要 xxxx 序列或机器 ID 中的额外数字,您可以在此处将年份压缩为三位数字的年份 yyy(例如 009)。

这两者都符合您的要求。

我们都喜欢具体的代码:

class Machine {
    public int ID { get; private set; }
    public Machine(int id) {
        ID = id;
    }
}

 class IdentifierGenerator {
    readonly Machine machine;
    int seed;
    const int digits = 4;
    readonly int modulus;
    readonly string seedFormat;
    public IdentifierGenerator(Machine machine) {
        this.machine = machine;
        this.modulus = (int)Math.Pow(10, digits);
        this.seedFormat = new string('0', digits);
    }

    public string Generate() {
        string identifier = DateTime.Now.ToString("yyyyMM") 
                                + machine.ID.ToString("00") 
                                + seed.ToString(seedFormat);
        seed = (seed + 1) % modulus;
        return identifier;
    }
}

Machine m = new Machine(1);
IdentifierGenerator gen = new IdentifierGenerator(m);
Console.WriteLine(gen.Generate());
Console.WriteLine(gen.Generate());

输出:

200912010000
200912010001

【讨论】:

  • 仍然没有解决机器 ID 来自何处的问题,并且严格控制的分发(ID 硬编码到安装程序中)几乎是不可能的(现在我有两个问题)。对此有什么想法吗?
  • 您有哪些部署选项?
  • 单个 MSI/EXE 安装程序,可以在线或通过 CD 分发(很可能每个客户一张 CD,而不是每台机器)。没有特殊的许可系统 - 硬件几乎是一个大加密狗。我可以看到一个客户使用 10 台设备或 100 万台设备;除了 .NET Framework 之外,真的无法对他们的环境做出任何假设。
  • 太糟糕了。艰难的。您将不得不进行一些人工干预(请致电 1-800-867-5309 获取机器 ID)或通过互联网安装常规电话回家以获取机器 ID。
【解决方案3】:

安装软件时,还要安装一个包含唯一数字 ID 的机器 ID 文件/注册表项。由于您只有几台机器,因此不应超过 3 或 4 位数。使用这些作为 MS 数字。从 1 开始依次生成剩余的数字。

【讨论】:

  • 我同意这种方法,尽管它在乞求一个问题:为了生成唯一 ID,我必须首先生成唯一 ID。如何生成唯一的机器 ID?
  • 机器 1 获取,001 机器 2 获取 002 等等。您有一个自动构建过程来生成每个安装,“下一个 ID”存储在构建机器上。
  • 自动构建过程失败,让安装强制拨打电话以获取机器 ID。
  • 我想我可以做到这一点。该应用程序旨在离线使用,但从技术上讲,规范中没有任何内容要求它离线安装。不过,我必须在某个地方有一个“激活”服务器,并想办法为不同的客户生成并行序列,尽管这些问题稍微容易处理。我将不得不咀嚼一下......
【解决方案4】:

我收集到您正在为 Windows 开发(回复:您对“MSI/EXE”的评论以回应 Jason 的回答)。因此,您可以使用 WMI 或类似方法来获取一些唯一的硬件属性(例如处理器或 HDD 序列号,或 NIC 的 MAC 地址)作为唯一机器 ID 的基础。另一种方法可能是使用您自己开发的硬件的唯一序列号(如果有的话)。

这很可能比您需要的要长,因此您可能会截断或散列它以将其减少到(例如)16 位左右并将其用作您的机器 ID。显然,这可能会导致冲突,但机器数量少(约 100 台)意味着这不太可能发生,而使用加密散列(例如 MD5)的截断输出会使这种情况变得更糟。

然后,由于您有一个(很可能是唯一的)机器 ID,因此您可以使用其他答案列出的方法生成本质上唯一的 ID。

【讨论】:

    【解决方案5】:

    在 24 小时内有 864000 个 100 毫秒的滴答声,因此将其添加到日期可能会 09.12.24.86400.0,但是您必须丢失世纪以适应 12 位数字,并且您没有任何空间用于机器 ID .

    【讨论】:

      【解决方案6】:

      想法一:

      YYMMDDmmnnnn

      在哪里

      YY is two digit year
      MM is two digit month
      DD is two digit day
      mm is a two digit code unique to that machine (00 - 99)
      nnnn is a sequential four digit code for that machine on that day.
      

      ~~

      想法二:

      mmmmnnnnnnnn

      在哪里

      mmmm is four digit code unique to the machine
      nnnnnnnn is a sequential number.
      

      【讨论】:

        【解决方案7】:

        我的建议是在一个 id 中结合多种方法。例如:从两个年份数字、两个月份数字开始,然后生成一个随机数,其中时间作为接下来几位数字的种子,然后是最后一对的唯一机器 ID。或者类似的东西。

        【讨论】:

          【解决方案8】:

          每台机器都有一个 DDNNN 的起始 id,其中 DD 是唯一的机器标识符,NNN 是该机器当天生成的当前标识符。每台机器都会跟踪它在特定日期生成的 ID,并在需要新 ID 时通过将最后一个 ID 加 1 来分配下一个 ID。它在每天开始时将其计数器重置为 0。日期 YYYYDOY 附加在每台机器生成的数字之前(4 位数年份,3 位数年份日期)。该编号保证唯一,因为机器标识符是唯一的。

          如果您需要更多空间来容纳更多机器,您可以从年份中去掉千禧年,并为机器 ID 添加一个数字:YYYDOYDDDNNN。

          【讨论】:

            【解决方案9】:

            “单台机器生成ID的频率不会超过每5分钟左右一次”

            假设这是真的,那么只需使用时间戳。 (32 位 Unix 时间有 10 个十进制数字,但会在 2038 年用完)

            但我认为假设不会发生碰撞是相当乐观的。

            “从特定机器生成的 ID 应该按顺序显示。”

            那么你唯一的选择就是使用序列号。

            哪个似乎与您在以后的约束中所说的不符?

            连接节点 ID 的填充版本以获得整个集群的唯一值。

            【讨论】:

              【解决方案10】:

              使用机器的 MAC 地址作为 MACHINE ID。您可以使用它来编码您的时间戳,即通过 XOR,或者您可以将其附加/添加到生成的序列化代码中。

              【讨论】:

              • 您似乎没有阅读该问题 - 它明确规定只允许十进制数字,并且必须为 12 位或更少。
              猜你喜欢
              • 2010-09-16
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-11-08
              • 2015-05-06
              • 1970-01-01
              相关资源
              最近更新 更多