【问题标题】:Is python uuid1 sequential as timestamps?python uuid1 是顺序的时间戳吗?
【发布时间】:2012-01-03 14:37:04
【问题描述】:

Python docs 声明 uuid1 使用当前时间来形成 uuid 值。但我找不到确保 UUID1 是连续的参考。

>>> import uuid
>>> u1 = uuid.uuid1()
>>> u2 = uuid.uuid1()
>>> u1 < u2
True
>>> 

【问题讨论】:

  • 不幸的是,他们不是。没有顺序的标准 UUID,因此如果您想要单调性,您必须推出自己的非标准 UUID。
  • @MischaArefiev 更好的方法是根据其用途使用标准 UUID(跨各种计算机和数据源/接收器工作的唯一标识符)。然后添加一条针对其目的的单独信息:顺序/顺序。通常一个序列是通过一个递增的数字(1、2、3,...)或时间戳(UTC time zone2014-08-12T16:55:29.074Z 中的 ISO 8601 字符串或来自 epoch 的计数)来完成的。

标签: python uuid


【解决方案1】:

但并非总是如此:

>>> def test(n):
...     old = uuid.uuid1()
...     print old
...     for x in range(n):
...             new = uuid.uuid1()
...             if old >= new:
...                     print "OOops"
...                     break
...             old = new
...     print new
>>> test(1000000)
fd4ae687-3619-11e1-8801-c82a1450e52f
OOops
00000035-361a-11e1-bc9f-c82a1450e52f

【讨论】:

  • +1。没错,第一个字符上升得太快了,每隔几分钟左右就从零开始。
  • 我注意到这取决于您的处理器/环境,我无法在我的服务器上复制它,但在我的个人电脑上很容易。
【解决方案2】:

UUID 不连续

不,标准 UUID 不是意味着是连续的。

显然,有人尝试使用 GUID(Microsoft 对 UUID 的改动)使它们按顺序排列,以帮助提高某些数据库场景中的性能。但顺序性并不是 UUID 的意图http://en.wikipedia.org/wiki/Globally_unique_identifier

MAC 是最后一个,而不是第一个

不,在标准 UUID 中,MAC 地址不是第一个组成部分。 MAC 地址是版本 1 UUID 中的 last 组件。 http://en.wikipedia.org/wiki/Universally_unique_identifier

不要假设 UUID 的类型

各种版本的 UUID 旨在相互兼容。因此,期望您始终拥有版本 1 UUID 可能是不合理的。其他程序员可能会使用其他版本。

规格

通过IETF 阅读UUID 规范RFC 4122。只有十几页。

【讨论】:

  • 我猜你说 MAC 地址是最后一个是对的。我很惊讶人们更多地投票支持另一个答案,而快速测试会知道 MAC 地址至少不是第一个。
  • @MrCooL 或者,比进行测试更容易,阅读the spec
【解决方案3】:

来自the python UUID docs

根据主机 ID、序列号和当前时间生成 UUID。如果没有给出node,getnode() 用于获取硬件地址。如果给出clock_seq,则作为序号;否则选择一个随机的 14 位序列号。

由此,我推断MAC地址是第一位的,然后是(可能是随机的)序列号,然后是当前时间。所以我不希望这些保证单调增加,即使对于同一机器/进程生成的 UUID。

【讨论】:

【解决方案4】:

我从 http://doanduyhai.wordpress.com/2012/07/05/apache-cassandra-tricks-and-traps/ 偶然发现了 Cassandra/Python 中的一个可能答案

词典 TimeUUID 排序

在所有基本类型中,Cassandra 支持类型 1(基于时间和服务器)和类型 4(随机)的 UUID 值。

UUID(唯一通用标识符)的主要用途是在潜在的分布式环境中获取真正唯一的标识符。

Cassandra 确实支持版本 1 UUID。它通过结合计算机的 MAC 地址和自公历开始以来的 100 纳秒间隔数为您提供唯一标识符。

如您所见,精度仅为 100 纳秒,但幸运的是它与时钟序列混合以增加随机性。此外,MAC 地址也用于计算 UUID,因此您不太可能在一个机器集群上遇到冲突,除非您需要处理非常大量的数据(不要忘记,不是每个人都是 Twitter 或 Facebook) .

UUID 最相关的用例之一,尤其是 TimeUUID,是将其用作列键。由于 Cassandra 列键已排序,因此我们可以利用此功能为我们的列族进行自然排序。

Hector 客户端提供的默认 com.eaio.uuid.UUID 的问题是它不容易使用。作为 ID,您可能需要将此值从服务器带到视图层,这就是问题所在。

基本上,com.eaio.uuid.UUID 会覆盖 toString() 以提供 UUID 的字符串表示形式。但是这种字符串格式不能按字典顺序排序……

以下是一些连续生成的TimeUUID:

8e4cab00-c481-11e1-983b-20cf309ff6dc at some t1
2b6e3160-c482-11e1-addf-20cf309ff6dc at some t2 with t2 > t1

“2b6e3160-c482-11e1-addf-20cf309ff6dc”.compareTo(“8e4cab00-c481-11e1-983b-20cf309ff6dc”) 给出 -6 表示 “2b6e3160-c482-11e1-addf-20cf309ff6dc” 小于/在 “8e4cab00-c481-11e1-983b-20cf309ff6dc”之前> 这是不正确的。

TimeUUID当前文字显示拆分如下:

time_low – time_mid – time_high_and_version – variant_and_sequence – node

如果我们从 time_high_and_version 开始重新排序,我们可以按字典顺序对其进行排序:

time_high_and_version – time_mid – time_low – variant_and_sequence – node

实用程序类如下:

public static String reorderTimeUUId(String originalTimeUUID)
    {
        StringTokenizer tokens = new StringTokenizer(originalTimeUUID, "-");
        if (tokens.countTokens() == 5)
        {
            String time_low = tokens.nextToken();
            String time_mid = tokens.nextToken();
            String time_high_and_version = tokens.nextToken();
            String variant_and_sequence = tokens.nextToken();
            String node = tokens.nextToken();

            return time_high_and_version + '-' + time_mid + '-' + time_low + '-' + variant_and_sequence + '-' + node;

        }

        return originalTimeUUID;
    }

TimeUUID 变为:

11e1-c481-8e4cab00-983b-20cf309ff6dc
11e1-c482-2b6e3160-addf-20cf309ff6dc

现在我们得到:

"11e1-c481-8e4cab00-983b-20cf309ff6dc".compareTo("11e1-c482-2b6e3160-addf-20cf309ff6dc") = -1

【讨论】:

    【解决方案5】:

    uuid.uuid1() 的无参数使用会产生非顺序结果 (see answer by @basil-bourque),但如果您设置 clock_seqnode 参数,它可以很容易地使其顺序化(因为在这种情况下 uuid1 使用 python 实现保证在当前进程中有unique and sequentialtimestamp部分UUID):

    import time
    
    from uuid import uuid1, getnode
    from random import getrandbits
    
    _my_clock_seq = getrandbits(14)
    _my_node = getnode()
    
    
    def sequential_uuid(node=None):
        return uuid1(node=node, clock_seq=_my_clock_seq)
    
    
    def alt_sequential_uuid(clock_seq=None):
        return uuid1(node=_my_node, clock_seq=clock_seq)
    
    
    
    if __name__ == '__main__':
        from itertools import count
        old_n = uuid1()  # "Native"
        old_s = sequential_uuid()  # Sequential
    
        native_conflict_index = None
    
        t_0 = time.time()
    
        for x in count():
            new_n = uuid1()
            new_s = sequential_uuid()
    
            if old_n > new_n and not native_conflict_index:
                native_conflict_index = x
    
            if old_s >= new_s:
                print("OOops: non-sequential results for `sequential_uuid()`")
                break
    
            if (x >= 10*0x3fff and time.time() - t_0 > 30) or (native_conflict_index and x > 2*native_conflict_index):
                print('No issues for `sequential_uuid()`')
                break
    
            old_n = new_n
            old_s = new_s
    
        print(f'Conflicts for `uuid.uuid1()`: {bool(native_conflict_index)}')
        print(f"Tries: {x}")
    
    

    多进程问题

    但是如果您在同一台机器上运行一些并行进程,那么:

    • node 默认为 uuid.get_node() 对所有进程都是相同的;
    • clock_seq 在某些进程中出现相同的几率很小(几率为 1/16384)

    这可能会导致冲突!这是使用的普遍关注点 uuid.uuid1 在同一台机器上的并行进程中,除非您可以从 Python3.7 访问 SafeUUID

    如果您确保还将node 设置为运行此代码的每个并行进程的唯一值,则不应发生冲突。

    即使您使用的是 SafeUUID,并设置了唯一的 node,如果它们是在不同的进程中生成的,仍然可能具有非顺序 ID。

    如果一些与锁相关的开销是可接受的,那么您可以将clock_seq 存储在一些外部原子存储中(例如在"locked" 文件中)并在每次调用时递增它:这允许node 具有相同的值在所有并行进程上,也将使 id-s 连续。对于所有并行进程都是使用multiprocessing 创建的子进程的情况:clock_seq 可以使用multiprocessing.Value“共享”

    【讨论】:

      猜你喜欢
      • 2017-03-09
      • 2020-03-27
      • 1970-01-01
      • 2011-06-07
      • 1970-01-01
      • 1970-01-01
      • 2018-12-22
      • 1970-01-01
      • 2021-01-05
      相关资源
      最近更新 更多