Как создать глобально уникальный идентификатор в распределенном сценарии?

Java задняя часть распределенный
Как создать глобально уникальный идентификатор в распределенном сценарии?

В распределенной системе есть некоторые сценарии, в которых необходимо использовать глобальный уникальный идентификатор, который может быть связан с бизнес-сценариями, такими как серийные номера платежей, или не иметь отношения к бизнес-сценариям.Например, глобальный уникальный идентификатор требуется после суб- базу данных и подтаблицу или использовать в качестве номеров версий транзакций, отслеживания распределенных ссылок и т. д. Хороший глобальный уникальный идентификатор должен иметь следующие характеристики:

  • Глобально уникальный: это основное требование, которое нельзя повторять;
  • Инкрементный: в некоторых особых сценариях он должен быть увеличен, например, номер версии транзакции, а идентификатор, сгенерированный позже, должен быть больше, чем предыдущий идентификатор; в некоторых сценариях увеличение лучше, чем отсутствие увеличения, поскольку увеличение полезно для производительности. индексов базы данных;
  • Высокая доступность: если это система или служба, которая генерирует уникальный идентификатор, должно быть большое количество вызовов, поэтому очень важно обеспечить ее высокую доступность;
  • Информационная безопасность: если идентификатор является непрерывным, им легко злонамеренно манипулировать или получить утечку.Например, если номер заказа является непрерывным, легко увидеть приблизительное количество заказов в день;
  • Кроме того, учитывая давление при хранении, внутренний диаметр, конечно же, должен быть как можно короче.

Итак, каковы решения для создания уникальных идентификаторов в распределенных сценариях?

Генерируется из базы данных

Давайте сначала поговорим о самом простом решении, используя самовозрастающую последовательность генерации базы данных: база данных генерирует уникальный первичный ключ и предоставляет его другим системам через сервисы; если это небольшая система, то общий объем данных и concurrency не очень велики, такого плана достаточно.

Если генерация идентификатора каждый раз может вызвать нагрузку на базу данных, вы можете рассмотреть возможность создания N идентификаторов за один раз и поместить их в кеш.Если идентификаторы в кеше исчерпаны, следующая партия идентификаторов будет сгенерирована через базу данных.

  • преимущество:Самый простой для понимания и самый простой в реализации.
  • недостаток:Также совершенно очевидно, что реализация каждой базы данных отличается, и более проблематично, если базу данных необходимо мигрировать; самая большая проблема - это проблема производительности.По оценкам, когда параллелизм достигает определенного уровня, этот метод будет быть трудным для удовлетворения требований к производительности; идентификатор несет слишком мало информации и может играть только роль идентификации, а самоувеличивающийся идентификатор также является непрерывным.

Генерируется из других компонентов/программ/промежуточного ПО

Используйте Redis/MongoDB/zookeeper для генерации: Redis использует incr и increby; ObjectId MongoDB; zk использует версию данных znode; все они могут генерировать глобальный уникальный идентификационный код.

Давайте используем ObjectId MongoDB в качестве примера:

{"_id": ObjectId("5d47ca7528021724ac19f745")}

ObjectId MongoDB занимает в общей сложности 12 байтов, из которых:

  • Версии до 3.2 (включая 3.2):4 байта временной метки + 3 байта идентификатора машины + 2 байта идентификатора процесса + 3 байта случайного счетчика
  • Версии после 3.2:4 байта временной метки + 5 байтов случайного значения + 3 байта прямого счетчика

Будь то старая версия или новая версия, ObjectId MongoDB может, по крайней мере, гарантировать уникальность внутри кластера.Мы можем создать службу, которая генерирует глобально уникальный идентификатор, и использовать MongoDB для генерации ObjectId и предоставления услуг внешнему миру. (каждый языковой драйвер MongoDB реализует алгоритм генерации ObjectId).

  • преимущество:Производительность выше, чем база данных, можно использовать развертывание кластера, идентификатор имеет некоторые значения, такие как метка времени;
  • недостаток:Как и база данных, необходимо вводить соответствующие компоненты/программное обеспечение, что увеличивает сложность системы; самое главное, оба этих решения означают, что система (сервис), генерирующая глобально уникальный идентификатор, станет единой точкой в ​​системе. Архитектура программного обеспечения В , само по себе означает риск, если есть проблема с этой службой, то все системы, которые зависят от этой службы, рухнут.

UUID

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

  • Версия 1:UUID на основе времени получается по отметке времени + случайное число + MAC-адрес; если приложение используется непосредственно в локальной сети, вместо MAC-адреса можно использовать IP-адрес; высокая уникальность (утечка MAC-адреса также является проблемой безопасности) ).
  • Версия 2:UUID, безопасный для DCE, замените первые 4 позиции метки времени в версии 1 на UID или GID POSIX; очень уникальный.
  • Версия 3:UUID на основе имени (MD5), полученный путем вычисления хэша MD5 имени и пространства имен; уникальный в пределах определенного диапазона.
  • Версия 4:Случайный UUID, UUID генерируется на основе случайного или псевдослучайного числа, есть определенная вероятность повторения.
  • Версия 5:UUID на основе имени (SHA1), аналогичный версии 3, за исключением того, что значение хеш-функции вычисляется с использованием алгоритма SHA1, оно уникально в пределах определенного диапазона.

public class CreateUUID {
 public static void main(String[] args) {
  String uuid = UUID.randomUUID().toString();
  System.out.println("uuid : " + uuid);

  uuid = UUID.randomUUID().toString().replaceAll("-","");
  System.out.println("uuid : " + uuid);
 }
}

  • преимущество:Локальная генерация, отсутствие сетевого потребления, отсутствие необходимости в сторонних компонентах (нет единой точки риска), относительно простая генерация и хорошая производительность.
  • недостаток:Большая длина не способствует хранению, и нет сортировки, что также относительно повлияет на производительность (например, механизм MySQL InnoDB, если UUID используется в качестве первичного ключа базы данных, его беспорядок приведет к частому изменению местоположения данных) .

Snowflake

Если вы хотите, чтобы идентификаторы генерировались локально, но не были такими неупорядоченными, как UUID, рассмотрите возможность использования алгоритма Snowflake (открытый исходный код Twitter).

Идентификатор, сгенерированный алгоритмом SnowFlake, представляет собой 64-битное целое число, в том числе:

  • 1 бит:Не используется, фиксируется на 0;
  • 41 бит:Временная метка (миллисекунды), диапазон значений: от 0 до 2 в 41-й степени - 1, если перевести во взрослый возраст, это около 69 лет;
  • 10 бит:Идентификатор машины; 5-значный идентификатор машинного зала + 5-значный идентификатор машины; (при относительно небольшом количестве сервисных кластеров его можно настроить вручную, а при большом масштабе службы можно использовать сторонние компоненты для автоматического конфигурация, такая как Leaf-snowflake от Meituan, которая настраивается через постоянные последовательные узлы Zookeeper, используются в качестве идентификаторов машин)
  • 12 бит:Порядковый номер, используемый для записи различных идентификаторов, сгенерированных в течение одной миллисекунды.

В Java идентификатор, сгенерированный алгоритмом SnowFlake, можно просто хранить в long.

  • преимущество:Локальная генерация, отсутствие сетевого потребления, отсутствие необходимости в сторонних компонентах (отсутствует одноточечный риск), уникальность в определенном диапазоне (в основном удовлетворяющая большинству сценариев), хорошая производительность и увеличение по отметке времени (растущий тренд);
  • недостаток:Если полагаться на часы машины, существует риск дублирования идентификаторов, сгенерированных одной и той же машиной, если время установлено назад.
image
image

Кроме того, есть много отличных интернет-компаний, которые также предоставляют решения или платформы для создания уникальных идентификаторов, например Leaf с открытым исходным кодом от Meituan, UidGenerator с открытым исходным кодом от Baidu и так далее.


@Resource
private UidGenerator uidGenerator;

@Test
public void testSerialGenerate() {
    // Generate UID
    long uid = uidGenerator.getUID();
    System.out.println(uidGenerator.parseUID(uid));
}

Дядя, который знает код | Текст [Оригинал]


敬请关注会点代码的大叔
Пожалуйста, обратите внимание на дядю, который знает код