【问题标题】:Shortest possible generated unique ID最短可能生成的唯一 ID
【发布时间】:2018-07-13 22:06:33
【问题描述】:

所以我们可以generate a unique id with str(uuid.uuid4()),长度为 36 个字符。

是否有另一种方法可以生成字符更短的唯一 ID?

编辑:

  • 如果ID可以用作主键那就更好了
  • 粒度应该优于1ms
  • 此代码可以分发,因此我们不能假设时间独立。

【问题讨论】:

  • 对于分布式,只需将生成节点包含在 ID 中

标签: python python-3.x uniqueidentifier


【解决方案1】:

如果这是用作 db 中的主键字段,请考虑仅使用自动递增整数。

str(uuid.uuid4()) 是 36 个字符,但它有四个无用的破折号 (-),并且仅限于 0-9 a-f。

更好的 32 个字符的 uuid4:

>>> uuid.uuid4().hex
'b327fc1b6a2343e48af311343fc3f5a8'

或者只是 b64 编码和切片一些 urandom 字节(由你来保证唯一性):

>>> base64.b64encode(os.urandom(32))[:8]
b'iR4hZqs9'

【讨论】:

  • 必须有人指出 UUID4 在技术上可能会发生冲突。 (虽然机会非常小。)uuid1 将“更加独特”,因为它取决于系统时间。也许这更符合这里的要求。
  • 如果你将一个 UUID4 切片(uuid4 是从 os.urandom 生成的),你只是增加了另一个生成的令牌复制的几率。同样,仍然很小的概率,但令牌的数量是 2**32。
  • 这不会将 uuid 切短,它只是删除了四个 - 字符。为了将 urandom 字节切短,您需要添加代码以检查它尚未使用并在必要时重新生成。
  • 是的,关于切短我说的是你答案的第二部分。我确实相信 uuid4 也是 django 文档建议的 id
  • 补充@BradSolomon 所说的,除非你有一台可以协调全球任何地方生成的每个 ID 的计算机,(忘记这一点)无论你做什么,你总是有可能发生冲突。对此的防御是创建一个大空间,这反过来意味着更长的 ID。如果用户坚持使用较短的 ID,则可以这样做,但总是面临更高的冲突风险。不过,这取决于您需要多少。
【解决方案2】:

TLDR

在大多数情况下,最好在内部处理数字并在外部将它们编码为短 ID。所以这里有一个用于Python3、PowerShell 和VBA 的函数,它将一个int32 转换为一个字母数字ID。像这样使用它:

int32_to_id(225204568)
'F2AXP8'

对于分布式代码,请使用 ULID:https://github.com/mdipierro/ulid

它们更长,但在不同的机器上是独一无二的。

ID 有多短?

它将用 6 个字符编码大约十亿个 ID,因此它尽可能紧凑,同时仍然只使用 non-ambiguous digits and letters

如何获得更短的 ID?

如果您想要更紧凑的 ID/代码/序列号,只需更改 chars="..." 定义即可轻松扩展字符集。例如,如果您允许所有小写和大写字母,您可以在相同的 6 个字符内拥有 560 亿个 ID。添加一些符号(如~!@#$%^&*()_+-=)会为您提供 2080 亿个 ID。

那么您为什么不选择尽可能短的 ID?

我在代码中使用的字符集有一个优势:它生成的 ID 易于复制粘贴(没有符号,所以双击会选择整个 ID),易于阅读且不会出错(没有相似的字符像2Z),而且很容易口头交流(只有大写字母)。只坚持数字是口头交流的最佳选择,但它们并不紧凑。

我确信:给我看代码

Python 3

def int32_to_id(n):
  if n==0: return "0"
  chars="0123456789ACEFHJKLMNPRTUVWXY"
  length=len(chars)
  result=""
  remain=n
  while remain>0:
    pos = remain % length
    remain = remain // length
    result = chars[pos] + result
  return result
  

PowerShell

function int32_to_id($n){
   $chars="0123456789ACEFHJKLMNPRTUVWXY"
   $length=$chars.length
   $result=""; $remain=[int]$n
   do {
      $pos = $remain % $length
      $remain = [int][Math]::Floor($remain / $length)
      $result = $chars[$pos] + $result
   } while ($remain -gt 0)
   $result
}

VBA

Function int32_to_id(n)
    Dim chars$, length, result$, remain, pos
    If n = 0 Then int32_to_id = "0": Exit Function
    chars$ = "0123456789ACEFHJKLMNPRTUVWXY"
    length = Len(chars$)
    result$ = ""
    remain = n
    Do While (remain > 0)
        pos = remain Mod length
        remain = Int(remain / length)
        result$ = Mid(chars$, pos + 1, 1) + result$
    Loop
    int32_to_id = result
End Function

Function id_to_int32(id$)
    Dim chars$, length, result, remain, pos, value, power
    chars$ = "0123456789ACEFHJKLMNPRTUVWXY"
    length = Len(chars$)
    result = 0
    power = 1
    For pos = Len(id$) To 1 Step -1
        result = result + (InStr(chars$, Mid(id$, pos, 1)) - 1) * power
        power = power * length
    Next
    id_to_int32 = result
End Function

Public Sub test_id_to_int32()
    Dim i
    For i = 0 To 28 ^ 3
        If id_to_int32(int32_to_id(i)) <> i Then Debug.Print "Error, i=", i, "int32_to_id(i)", int32_to_id(i), "id_to_int32('" & int32_to_id(i) & "')", id_to_int32(int32_to_id(i))
    Next
    Debug.Print "Done testing"
End Sub

【讨论】:

    【解决方案3】:

    是的。只需使用当前的 UTC 毫秒。这个数字永远不会重复。

    const uniqueID = new Date().getTime();
    

    编辑

    如果您很少要求在同一毫秒内生成多个 ID,则此方法无用,因为此数字的粒度为 1 毫秒。

    【讨论】:

    • 如果你在同一毫秒内画出两个或多个,这个数字会重复。
    • 点了!但是这里没有提到频率。所以我没有考虑到这种可能性。
    • 很公平,我已将问题更新为更具体一些
    • 是的,人们对制作 ID 有很大不同的要求。这在大多数情况下大部分时间都有效,但后来驾车的读者应该知道这些警告。我自己早些时候使用这种方法,认为它是安全的,而且在我的代码增长之前,事情开始发生得更快并且假设被打破了。这就是为什么人们使用巨大的 UUID 的部分原因,所以他们可以忘记这个问题并继续前进,代价是大而丑陋的字符串标识符。
    猜你喜欢
    • 2010-12-11
    • 2015-05-06
    • 2016-07-05
    • 2014-01-04
    • 2011-01-10
    • 2012-02-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多