8

我发现了许多关于生成 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。我之所以在这里问这个问题,是因为我真的很想尝试在其中合并人类可读的年/月,或者编码一些关于源机器的信息,或者两者兼而有之。

我希望有人可以帮助在这些软要求上做出妥协……或者解释为什么在其他要求的情况下它们都不可能。

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

更新:

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

4

10 回答 10

4

“我之所以在 SO 上问这个问题,是因为我真的很想尝试在其中加入人类可读的年/月,或者编码一些关于源机器的信息,或者两者兼而有之。”

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

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

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

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

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

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

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

像这样的东西...

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

这就是您避免碰撞所需的全部内容。如果您添加一个位置数字,那么当您到达 11 个位置时,您会被搞砸。

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

于 2009-12-23T16:55:42.143 回答
3

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

于 2009-12-23T16:30:44.623 回答
3

怎么样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. 如果您需要序列或机器 IDyyy中的额外数字,您可以在此处将年份压缩为三位数的年份(例如 009)。xxxx

这两个都符合您要求的时间戳/机器 ID。

我们都喜欢具体的代码:

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
于 2009-12-23T16:32:48.173 回答
1

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

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

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

于 2009-12-24T00:50:26.243 回答
0

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

于 2009-12-23T16:35:04.173 回答
0

想法一:

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.

~~

想法二:

嗯嗯嗯嗯嗯嗯

在哪里

mmmm is four digit code unique to the machine
nnnnnnnn is a sequential number.
于 2009-12-23T16:35:35.927 回答
0

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

于 2009-12-23T16:36:12.067 回答
0

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

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

于 2009-12-23T16:37:49.363 回答
0

“单台机器不会比每 5 分钟左右更频繁地生成 ID”

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

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

“从特定机器生成的 ID 应该是连续的。”

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

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

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

于 2009-12-23T17:24:09.997 回答
-1

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

于 2014-05-14T01:54:16.160 回答