Является ли python uuid1 последовательным в качестве меток времени? - PullRequest
12 голосов
/ 03 января 2012

Python docs утверждает, что uuid1 использует текущее время для формирования значения uuid. Но я не смог найти ссылку, которая гарантирует, что UUID1 является последовательным.

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

Ответы [ 5 ]

15 голосов
/ 03 января 2012

Но не всегда:

>>> 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
10 голосов
/ 06 февраля 2012

UUID не последовательный

Нет, стандартные UUID не предназначены для последовательного.

По-видимому, были предприняты некоторые попытки с GUID (поворот Microsoft на UUID), чтобы сделать их последовательными, чтобы помочь с производительностью в определенных сценариях базы данных.Но , будучи последовательным, не является целью UUID.http://en.wikipedia.org/wiki/Globally_unique_identifier

MAC - последний, а не первый

Нет, в стандартных UUID MAC-адрес является , а не первым компонентом.MAC-адрес является последним компонентом в UUID версии 1.http://en.wikipedia.org/wiki/Universally_unique_identifier

Не предполагайте, какой тип UUID

Различные версии UUID должны быть совместимы друг с другом.Поэтому ожидать, что у вас всегда есть UUID версии 1, может быть неоправданно.Другие программисты могут использовать другие версии.

Спецификация

Считать спецификацию UUID, RFC 4122 , по IETF .Всего дюжина страниц.

5 голосов
/ 03 января 2012

С the Python UUID docs :

Генерирует UUID из идентификатора хоста, порядкового номера и текущего времени.Если узел не указан, getnode () используется для получения аппаратного адреса.Если указано clock_seq, оно используется в качестве порядкового номера;в противном случае выбирается случайный 14-битный порядковый номер.

Из этого я делаю вывод, что сначала MAC-адрес, затем (возможно, случайный) порядковый номер, а затем текущее время.Поэтому я не ожидаю, что они будут гарантированно увеличиваться монотонно даже для идентификаторов UUID, генерируемых одним и тем же компьютером / процессом.

4 голосов
/ 08 июля 2013

Я наткнулся на вероятный ответ в Cassandra / Python от http://doanduyhai.wordpress.com/2012/07/05/apache-cassandra-tricks-and-traps/

Заказ по лексикографическому времениUUID

Cassandra обеспечивает, среди всех примитивных типов, поддержку значений UUID типа 1 (на основе времени и на сервере) и типа 4 (случайных).

Основное использование UUID (уникальный универсальный идентификатор) - получение действительно уникального идентификатора в потенциально распределенной среде.

Cassandra поддерживает UUID версии 1. Он дает вам уникальный идентификатор, объединяя MAC-адрес компьютера и число интервалов в 100 наносекунд с начала григорианского календаря.

Как видите, точность составляет всего 100 наносекунд, но, к счастью, она смешана с тактовой последовательностью для добавления случайности. Кроме того, MAC-адрес также используется для вычисления UUID, поэтому очень маловероятно, что вы столкнетесь с коллизией на одном кластере компьютеров, если только вам не нужно обрабатывать действительно очень большой объем данных (не забывайте, что не все являются Twitter или Facebook) .

Одним из наиболее подходящих вариантов использования UUID, особенно TimeUUID, является его использование в качестве ключа столбца. Поскольку ключи столбцов Cassandra отсортированы, мы можем воспользоваться этой возможностью, чтобы иметь естественный порядок для наших семейств столбцов.

Проблема со стандартным com.eaio.uuid.UUID, предоставленным клиентом Hector, заключается в том, что с ним нелегко работать. В качестве идентификатора вам может понадобиться перенести это значение с сервера на уровень представления, и это все получится.

По сути, 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
1 голос
/ 16 мая 2019

Использование аргумента uuid.uuid1() без аргументов дает непоследовательные результаты ( см. Ответ по @ basil-bourque ), но его можно легко сделать последовательным, если вы установите clock_seq или node аргументы (потому чтов этом случае uuid1 использует реализацию Python, которая гарантирует уникальную и последовательную timestamp часть 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 в параллельных процессах на одной машине, если у вас нет доступа к SafeUUID из Python3.7.

Если вы также убедитесь, что для node установлено значениеуникальное значение для каждого параллельного процесса, который выполняет этот код, тогда конфликты не должны возникать.

Даже если вы используете SafeUUID и устанавливаете уникальное node, все равно возможно иметь непоследовательные идентификаторы, если они генерируютсяв разных процессах.

Если допустимы некоторые накладные расходы, связанные с блокировкой, то вы можете сохранить clock_seq в некотором внешнем атомарном хранилище (например, в «заблокированном» файле) и увеличить его накаждый вызов: это позволяет иметь одинаковое значение для node во всех параллельных процессах, а также делает идентификаторы последовательными.Для случаев, когда все параллельные процессы являются подпроцессами, созданными с использованием multiprocessing: clock_seq может быть «разделен» с использованием multiprocessing.Value

...