Необходимость распределенного идентификатора.
Когда бизнес-объем меньше 500 Вт, один mysql может предоставлять услуги, а когда он больше, также можно обрабатывать разделение чтения-записи. Но когда синхронизация ведущий-ведомый не может с этим справиться, ей необходимо использовать подтаблицу и подбазу данных, но после подбазы данных и подтаблицыДля идентификации части данных требуется уникальный идентификатор., самоувеличивающийся идентификатор базы данных, очевидно, не может удовлетворить спрос; специальные заказы, такие как заказы и купоны, также должны быть идентифицированы уникальным идентификатором. В настоящее время очень необходима система, которая может генерировать глобально уникальные идентификаторы. Затем этот глобально уникальный идентификатор называется распределенным идентификатором.
Распределенный идентификатор должен соответствовать этим условиям
- Глобально уникальный: основное требование состоит в том, чтобы идентификатор был глобально уникальным.
- Высокая производительность: высокая доступность и низкая задержка, быстрое создание идентификатора и ответ.
- Высокая доступность: бесконечно близкая к 100% доступности
- Хороший доступ: следуйте принципу заимствования и максимально упростите разработку и реализацию системы.
- Тенденция роста: лучшая тенденция растет, это требование зависит от конкретного бизнес-сценария, как правило, это не строгие требования.
1. UUID
UUIDОтносится к универсальному уникальному идентификатору, переведенному на китайский языкУниверсальный уникальный идентификатор, UUID-цель - сделать все элементы распределенной системы, могут иметь уникальную идентифицирующую информацию. Форма 8-4-4-4-12, всего 36 символов. Очень просто использовать
import java.util.UUID;
public static void main(String[] args) {
String uuid = UUID.randomUUID().toString().replaceAll("-","");
System.out.println(uuid);
}
выходной результат99a7d0925b294a53b2f4db9d5a3fb798
, но UUID не подходит для реальных потребностей бизнеса. Такие строки, как UUID для номера заказа, не имеют никакого значения, и нельзя увидеть никакой полезной информации, связанной с заказом; в то время как для базы данных он используется в качестве идентификатора первичного ключа бизнеса, который является не толькослишком долговсе ещенить, производительность хранилища низкая, а запрос также занимает много времени, поэтому его не рекомендуется использовать в качестве распределенного идентификатора.
优点
: Создание достаточно проще, местное поколение не имеет потребления сети и является уникальным缺点
: Беспорядочные строки, не имеют тенденции к увеличению характеристик, нет особого бизнес-значения. Когда такая длинная строка первичного ключа MySQL не является разумным выбором.
2. Автоинкремент ID на основе базы данных
В качестве распределенного идентификатора можно использовать идентификатор автоинкремента на основе базы данных. Конкретная реализация: для создания идентификатора требуется отдельный экземпляр MySQL. Структура таблицы выглядит следующим образом:
CREATE DATABASE `SoWhat_ID`;
CREATE TABLE SoWhat_ID.SEQUENCE_ID (
`id` bigint(20) unsigned NOT NULL auto_increment,
`value` char(10) NOT NULL default '',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
) ENGINE=MyISAM;
insert into SEQUENCE_ID(value) VALUES ('values');
Когда нам нужен идентификатор, вставляем запись в таблицу, чтобы вернуть идентификатор первичного ключа, но у этого метода есть фатальный недостаток, когда трафик резко возрастает.MySQL本身就是系统的瓶颈
, использование его для реализации распределенных сервисов относительно рискованно и не рекомендуется!
优点
: Простая реализация, монотонно увеличивающийся идентификатор, высокая скорость запросов для числовых типов.
缺点
: существует риск простоя одной точки БД, а не сценариев с высокой степенью параллелизма Кан Чжу.
3. На основе режима кластера базы данных
Как упоминалось ранее, метод одноточечной базы данных нежелателен, поэтому выполните некоторые оптимизации высокой доступности для вышеуказанного метода и замените его кластером в режиме ведущий-ведомый. Если вы опасаетесь, что одна мастер-нода выйдет из строя, использовать ее будет невозможно, поэтому можно сделать кластер в режиме дуал-мастер, то есть два экземпляра Mysql могут независимо выдавать самоувеличивающиеся идентификаторы. Тогда возникнет проблема: самоувеличивающиеся идентификаторы двух экземпляров MySQL начинаются с 1, так что мне делать, если генерируются повторяющиеся идентификаторы?解决方案
:настраиватьНачальное значение и размер шага автоматического увеличения
Конфигурация MySQL_1:
set @@auto_increment_offset = 1; -- 起始值
set @@auto_increment_increment = 2; -- 步长
Конфигурация MySQL_2:
set @@auto_increment_offset = 2; -- 起始值
set @@auto_increment_increment = 2; -- 步长
Саморазведя идентификатор двух экземпляров MySQL:
1、3、5、7、9
2、4、6、8、10
Но что, если и двух все же недостаточно? Чтобы добавить третий экземпляр MySQL, вам необходимо вручную изменить начальное значение и размер шага первого и второго экземпляров MySQL, а также установить начальную позицию идентификатора третьей машины дальше, чем позиция существующего максимального самостоятель- увеличивающийся идентификатор.Однако идентификаторы одного или двух экземпляров MySQL не должны увеличиваться до начального значения идентификатора третьего экземпляра MySQL, в противном случае будут повторяться самоувеличивающиеся идентификаторы, и при необходимости может потребоваться остановить и изменить.
优点
: Решите проблему с одной точкой БД.
缺点
: Это не способствует последующему расширению, и на самом деле нагрузка на одну базу данных сама по себе все еще велика, и она все еще не может соответствовать сценариям с высоким параллелизмом.
4. Режим сегмента номера на основе базы данных
Режим числового сегмента является одной из основных реализаций текущего распределенного генератора идентификаторов.Режим числового сегмента можно понимать как从数据库批量的获取自增ID
, каждый раз, когда из базы данных берется диапазон числовых сегментов. Например, (1,1000] представляет 1000 идентификаторов. Конкретная бизнес-служба будет генерировать автоматически увеличивающийся идентификатор от 1 до 1000 из этого числового сегмента и загружать его в память. Структура таблицы следующая:
CREATE TABLE id_generator (
`id` int(10) NOT NULL,
`max_id` bigint(20) NOT NULL COMMENT '当前最大id',
`step` int(20) NOT NULL COMMENT '号段的步长',
`biz_type` int(20) NOT NULL COMMENT '业务类型',
`version` int(20) NOT NULL COMMENT '版本号',
PRIMARY KEY (`id`)
)
- max_id : текущий самый большой доступный идентификатор
- step : представляет длину сегмента числа
- biz_type : представляет различные типы бизнеса.
- version : это оптимистичная блокировка, и версия обновляется каждый раз, чтобы обеспечить правильность данных во время параллелизма.
id | biz_type | max_id | step | version |
---|---|---|---|---|
1 | 101 | 1000 | 2000 | 0 |
Когда идентификатор сегмента номера партии израсходован, снова запросите новый сегмент номера из базы данных и выполните одну операцию с полем max_id.update
Операция update max_id= max_id + step, если обновление прошло успешно, это означает, что новый сегмент номера успешно получен, а диапазон нового сегмента номера равен (max_id, max_id + step].
update id_generator set max_id = {max_id+step}, version = version + 1
where version = {version} and biz_type = XX
Поскольку мультисервисная сторона может работать одновременно, используется номер версии.乐观锁
Обновление режима обновления, это метод распределенного генерации идентификатора не сильно зависит от базы данных, и нет частого доступа к базе данных, а давление на базу данных намного меньше. Но если вы столкнулись с двойной одиннадцатью или аналогичной активностью или относительно высоким доступом к базе данных.
5. На основе режима Redis
RedisЭто также может быть достигнуто.Принцип заключается в том, что Redis является однопоточным, поэтому мы можем использоватьincr
Команда реализует атомарное автоинкрементирование идентификатора.
127.0.0.1:6379> set seq_id 1 // 初始化自增ID为1
OK
127.0.0.1:6379> incr seq_id // 增加1,并返回递增后的数值
(integer) 2
При реализации Redis необходимо обратить внимание на проблему сохраняемости Redis. В Redis есть два метода сохранения: RDB и AOF.
6. На основе алгоритма Snowflake (Снежинка) режим
Алгоритм SnowFlake — это распределенный алгоритм генерации идентификаторов, открытый исходным кодом Twitter. Его основная идея заключается в следующем:Используйте 64-битный длинный номер в качестве глобального уникального идентификатора.. Он широко используется в распределенных системах, а ID вводит отметку времени, почему он называется алгоритмом снежинки? Я думал, что это хорошо известноВ мире нет пары одинаковых снежинок. Алгоритм снежинки в основном продолжает самоувеличиваться, и в коде есть подробные комментарии.Среди 64 бит 1 бит не используется, а затем 41 бит используется как миллисекунды, 10 бит используются как идентификатор рабочей машины, а 12 бит используются как серийный номер. Например картинка выше:
- Первая часть 1 бит: 0, Это бессмысленно. Потому что, если первый бит в двоичном файле равен 1, все они отрицательные, но все идентификаторы, которые мы генерируем, положительные, поэтому первый бит всегда равен 0.
- Вторая часть составляет 41 бита: он представляет метку времени. Устройство составляет миллисекунды.
41 бита могут представлять номера до 2 ^ 41 - 1, то есть он может идентифицировать 2 ^ 41 - 1 миллисекундное значение, которое выражается как взрослый
69
Время года. - Третья часть состоит из 5 бит: она представляет собой идентификатор компьютерного зала. 5 бит представляют идентификатор машины. Это означает, что он представляет до 2^5 комнат (32 комнаты)
- Четвертая часть состоит из 5 бит: она представляет собой идентификатор машины. Каждый компьютерный зал может представлять собой 2^5 машин (32 машины), или это может быть определено в соответствии с реальной ситуацией в вашей собственной компании.
- Пятая часть состоит из 12 бит: представлен серийный номер, который являетсяодна миллисекундаКоличество последовательности идентификатора, создаваемого одновременно. Наибольшее положительное число, которое 12 битов может представлять 2 ^ 12 - 1 = 4096, что означает, что число, представленное этим 12 битами, может использоваться для различения 4096 различных идентификаторов в пределах одной миллисекунды.
Суммировать: 简单来说,你的某个服务假设要生成一个全局唯一 id,那么就可以发送一个请求给部署了 SnowFlake 算法的系统,由这个 SnowFlake 算法系统来生成唯一 id。
Система алгоритма SnowFlake должна сначала знать машинный зал и машину, на которой он находится, например, id компьютерного зала = 17, а id машины = 12.
Затем, после того, как система алгоритма SnowFlake получит этот запрос, она сначала сгенерирует 64-битный идентификатор с помощью двоичных битовых операций, первый бит из 64 битов не имеет смысла.
Затем 41 бит, вы можете использовать текущую метку времени (в миллисекундах), затем 5 бит, чтобы установить идентификатор компьютерного зала, и 5 бит, чтобы установить идентификатор машины.
Наконец, оцените еще раз, в течение этой миллисекунды на этой машине в текущем компьютерном зале этопервые несколько запросов, добавьте порядковый номер к запросу для создания идентификатора на этот раз как последние 12 бит. Наконец, выходит 64-битный идентификатор, похожий на:Этот алгоритм может обеспечить, чтобы машина в одной и той же миллисекунде генерирует уникальный идентификатор. Он может генерировать множество миллисекундных идентификаторов, но последний 12-битный последовательный номер для различения.
Резюме: использование каждого бита в 64-битном числе для установки разных флагов для различения каждого идентификатора.
Код реализации алгоритма SnowFlake выглядит следующим образом:
/**
* 雪花算法相对来说如果思绪捋顺了实现起来比较简单,前提熟悉位运算。
*/
public class SnowFlake
{
/**
* 开始时间截 (2015-01-01)
*/
private final long twepoch = 1420041600000L;
/**
* 机器id所占的位数
*/
private final long workerIdBits = 5L;
/**
* 数据标识id所占的位数
*/
private final long dataCenterIdBits = 5L;
/**
* 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
*/
private final long maxWorkerId = ~(-1L << workerIdBits);
/**
* 支持的最大机房标识id,结果是31
*/
private final long maxDataCenterId = ~(-1L << dataCenterIdBits);
/**
* 序列在id中占的位数
*/
private final long sequenceBits = 12L;
/**
* 机器ID向左移12位
*/
private final long workerIdShift = sequenceBits;
/**
* 机房标识id向左移17位(12+5)
*/
private final long dataCenterIdShift = sequenceBits + workerIdBits;
/**
* 时间截向左移22位(5+5+12)
*/
private final long timestampLeftShift = sequenceBits + workerIdBits + dataCenterIdBits;
/**
* 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
*/
private final long sequenceMask = ~(-1L << sequenceBits);
/**
* 工作机器ID(0~31)
*/
private volatile long workerId;
/**
* 机房中心ID(0~31)
*/
private volatile long dataCenterId;
/**
* 毫秒内序列(0~4095)
*/
private volatile long sequence = 0L;
/**
* 上次生成ID的时间截
*/
private volatile long lastTimestamp = -1L;
//==============================Constructors=====================================
/**
* 构造函数
*
* @param workerId 工作ID (0~31)
* @param dataCenterId 机房中心ID (0~31)
*/
public SnowFlake(long workerId, long dataCenterId)
{
if (workerId > maxWorkerId || workerId < 0)
{
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
}
if (dataCenterId > maxDataCenterId || dataCenterId < 0)
{
throw new IllegalArgumentException(String.format("dataCenter Id can't be greater than %d or less than 0", maxDataCenterId));
}
this.workerId = workerId;
this.dataCenterId = dataCenterId;
}
// ==============================Methods==========================================
/**
* 获得下一个ID (该方法是线程安全的)
* 如果一个线程反复获取Synchronized锁,那么synchronized锁将变成偏向锁。
*
* @return SnowflakeId
*/
public synchronized long nextId() throws RuntimeException
{
long timestamp = timeGen();
//如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
if (timestamp < lastTimestamp)
{
throw new RuntimeException((String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)));
}
//如果是毫秒级别内是同一时间生成的,则进行毫秒内序列生成
if (lastTimestamp == timestamp)
{
sequence = (sequence + 1) & sequenceMask;
//毫秒内序列溢出,一毫秒内超过了4095个
if (sequence == 0)
{
//阻塞到下一个毫秒,获得新的时间戳
timestamp = tilNextMillis(lastTimestamp);
}
}
else
{
//时间戳改变,毫秒内序列重置
sequence = 0L;
}
//上次生成ID的时间截
lastTimestamp = timestamp;
//移位并通过或运算拼到一起组成64位的ID
return ((timestamp - twepoch) << timestampLeftShift)
| (dataCenterId << dataCenterIdShift)
| (workerId << workerIdShift)
| sequence;
}
/**
* 阻塞到下一个毫秒,直到获得新的时间戳
* @param lastTimestamp 上次生成ID的时间截
* @return 当前时间戳
*/
private long tilNextMillis(long lastTimestamp)
{
long timestamp = timeGen();
while (timestamp <= lastTimestamp)
{
timestamp = timeGen();
}
return timestamp;
}
/**
* 返回以毫秒为单位的当前时间
* @return 当前时间(毫秒)
*/
private long timeGen()
{
return System.currentTimeMillis();
}
}
Преимущества алгоритма SnowFlake:
- Высокая производительность и высокая доступность: не зависит от базы данных при генерации и полностью генерируется в памяти.
- Большая пропускная способность: в секунду могут генерироваться миллионы самоувеличивающихся идентификаторов.
- Самоинкремент ID: хранится в базе данных, эффективность индексации высокая.
Недостатки алгоритма SnowFlake:
- Зависит от согласованности с системным временем. Если системное время отозвано или изменено, это может вызвать конфликты идентификаторов или дублирование.
На практике у нас не так много компьютерных залов, мы можем улучшить алгоритм и оптимизировать 10-битный идентификатор машины в бизнес-таблицу или бизнес, связанный с нашей системой.
7. Генератор жидкости Baidu
Адрес проекта на GitHub:GitHub.com/Baidu/UID-a…Блок Uid-генератора является разработкой технологии Baidu, основанной на алгоритме Snowflake, исходный алгоритм отличается тем, что снежинка, uid-генератор поддерживает пользовательский штамп, количество битов каждой части идентификатора рабочей машины и серийного номера, а также uid-генератор, используемый для генерации рабочего идентификатора политики, определяемой пользователем.
uid-generator необходимо использовать вместе с базой данных, и необходимо добавить таблицу WORKER_NODE. Когда приложение запускается, оно вставляет часть данных в таблицу базы данных.Самоувеличивающийся идентификатор, возвращаемый после успешной вставки, представляет собой данные workId машины, состоящие из хоста и порта.Как видно из рисунка выше, временная часть UidGenerator составляет всего 28 бит, а это означает, что по умолчанию UidGenerator выдерживает только 8,5 лет (2^28-1/86400/365). Конечно, в соответствии с потребностями вашего бизнеса, UidGenerator может соответствующим образом настроить дельта-секунды, идентификатор рабочего узла и последовательность занятых битов.
Далее проанализируйте реализацию Baidu UidGenerator. Следует отметить, что UidGenerator предоставляется двумя способами: иDefaultUidGenerator
иCachedUidGenerator
. Сначала мы анализируем более легкий для пониманияDefaultUidGenerator
.
DefaultUidGenerator
delta secondsЭто значение относится к разнице во времени между текущим временем и временем EPOCH.второй. Время эпохи – это время, когда служба распределенной идентификации, созданная встроенным UidGenerator, впервые подключается к сети. Ее можно настроить и необходимо настроить в соответствии с вашим онлайн-временем, поскольку время эпохи по умолчанию — 20 09 – 2016. Если его не настроить, то это будет пустая трата.Доступно несколько лет.
worker idДалее поговорим о том, как UidGenerator присваивает идентификаторы воркеров.Чтобы построить UidGenerator, вам нужно создать таблицу:
UidGenerator
Когда экземпляр, использующий его для создания распределенного идентификатора, запускается, строка данных вставляется в эту таблицу, а полученное значение идентификатора является значением, которое будет присвоено рабочему идентификатору. Поскольку workerId по умолчанию равен 22 битам, количество перезапусков всех экземпляров, которые интегрируют UidGenerator для создания распределенных идентификаторов, не может превышать 4194303 раза (т. е. 2^22-1), иначе будет выдано исключение.
Основной код этой логики исходит из DisposableWorkerIdAssigner.java, конечно, вы также можете реализоватьWorkerIdAssigner.java
Интерфейс, сгенерированный пользователем workerId.sequenceОсновной код выглядит следующим образом, несколько ключевых моментов реализации:
- синхронизированная гарантия безопасности потока.
- Если у времени есть какой-либо обратный вызов, вызовите исключение напрямую.
- Если текущее время и последнее время совпадают во второй раз, то последовательность увеличивается. Если самоинкремент превышает 2^13-1 за ту же секунду, то -- будет вращаться и ждать следующей секунды (getNextSecond).
- Если это новая секунда, то последовательность начинается с 0.
/**
* Get UID
*
* @return UID
* @throws UidGenerateException in the case: Clock moved backwards; Exceeds the max timestamp
*/
protected synchronized long nextId() {
long currentSecond = getCurrentSecond();
// Clock moved backwards, refuse to generate uid
if (currentSecond < lastSecond) {
long refusedSeconds = lastSecond - currentSecond;
throw new UidGenerateException("Clock moved backwards. Refusing for %d seconds", refusedSeconds);
}
// At the same second, increase sequence
if (currentSecond == lastSecond) {
sequence = (sequence + 1) & bitsAllocator.getMaxSequence();
// Exceed the max sequence, we wait the next second to generate uid
if (sequence == 0) {
currentSecond = getNextSecond(lastSecond);
}
// At the different second, sequence restart from zero
} else {
sequence = 0L;
}
lastSecond = currentSecond;
// Allocate bits for UID
return bitsAllocator.allocate(currentSecond - epochSeconds, workerId, sequence);
}
СуммироватьБлагодаря реализации DefaultUidGenerator видно, что его обработка обратного вызова часов относительно проста и груба. Кроме того, если вы используете метод DefaultUidGenerator UidGenerator для генерации распределенных идентификаторов, вы должны настроить количество битов, занимаемых каждым полем, в соответствии с ситуацией и характеристиками вашего бизнеса:
<property name="timeBits" value="28"/>
<property name="workerBits" value="22"/>
<property name="seqBits" value="13"/>
<property name="epochStr" value="2016-09-20"/>
CachedUidGenerator
CachedUidGenerator
даUidGenerator
Достигнуты важные улучшения. Его основное преимуществоRingBuffer
Как показано на рисунке ниже, это, по сути, массив, и каждый элемент массива называется Slot. UidGenerator разработал два кольцевых буфера, сохраненный уникальный идентификатор и сохраненный флаг. Размер кольцевого буфера равен 2 ^ n, n должно быть целым положительным числом:Для получения более подробной информации вы можете прочитать исходный код Git, который можно напрямую передать черезSpringBootКомплексное использование разработки.
8. Лист
Leaf разработан Meituan, адрес github:GitHub.com/Mehtuan-DI Ах…, Leaf поддерживает как режим числового сегмента, так и режим алгоритма снежинки.переключиться на использование.
Режим сегмента номера
Сначала импортируйте исходный код https://github.com/Meituan-Dianping/Leaf и создайте таблицу leaf_alloc.
DROP TABLE IF EXISTS `leaf_alloc`;
CREATE TABLE `leaf_alloc` (
`biz_tag` varchar(128) NOT NULL DEFAULT '' COMMENT '业务key',
`max_id` bigint(20) NOT NULL DEFAULT '1' COMMENT '当前已经分配了的最大id',
`step` int(11) NOT NULL COMMENT '初始步长,也是动态调整的最小步长',
`description` varchar(256) DEFAULT NULL COMMENT '业务key的描述',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '数据库维护的更新时间',
PRIMARY KEY (`biz_tag`)
) ENGINE=InnoDB;
Затем откройте в проекте режим числового сегмента, настройте соответствующую информацию базы данных и закройте режим снежинки.
leaf.name=com.sankuai.leaf.opensource.test
leaf.segment.enable=true
leaf.jdbc.url=jdbc:mysql://localhost:3306/leaf_test?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8
leaf.jdbc.username=root
leaf.jdbc.password=root
leaf.snowflake.enable=false
#leaf.snowflake.zk.address=
#leaf.snowflake.port=
Запускается проект LeafServerApplication, запускающий модуль leaf-server. Режим сегмента номера для получения тестового URL распределенного идентификатора автоинкремента: http://localhost:8080/api/segment/get/leaf-segment-test Режим сегмента контрольного номера: http://localhost:8080/cache
режим снежинки
Режим снежинки в Leaf основан наZooKeeper
, В отличие от исходного алгоритма снежинки, который в основном заключается в генерации workId, workId в Leaf генерируется на основе последовательного идентификатора ZooKeeper.Когда каждое приложение использует Leaf-snowflake, последовательный идентификатор будет генерироваться в Zookeeper при его запуске. Это эквивалентно машине, соответствующей последовательному узлу, то есть workId.
leaf.snowflake.enable=true
leaf.snowflake.zk.address=127.0.0.1
leaf.snowflake.port=2181
Режим Snowflake получает тестовый URL распределенного идентификатора автоинкремента: http://localhost:8080/api/snowflake/get/test
9. Тиньид
TinyidРазработано Didi, адрес Github:github.com/didi/tinyid
TinyID - это сервис генератора ID, который предоставляет два способа получить COND-API и клиенты Java. Если вы используете клиент Java, чтобы получить способ, официальные претензии, которые вы можете добраться до 1 кВт QPS (более 10 миллионов QPS Single Express, когда java) клиент. Несомненно
Учебник по ТиньидуПринцип очень прост.Из данных в таблице базы данных вы можете в основном догадаться, что он неразделим, что является классическим режимом сегмента, который почти такой же, как листовой принцип Meituan. Схематическая диаграмма показана ниже.В качестве примера того же bizType каждый тиниид-сервер назначается своему сегменту.Например, первый тиниид-сервер назначается (1000, 2000), а второй тиниид-сервер назначен на (2000, 3000), третий сервер tinyid назначен на (3000, 4000]:В качестве примера возьмем первый tinyid-сервер, когда его сегмент использует 20% (исходный код ядра: segmentId.setLoadingId(segmentId.getCurrentId().get() + idInfo.getStep() * Constants.LOADING_PERCENT / 100); значение LOADING_PERCENT равно 20), то есть порог loadId установлен на 20%. Например, текущий id 10000 и размер шага 10000, тогда loadingId=12000. Тогда при присвоении распределенному ID запроса 12001 (или после перезапуска), то есть при превышении loadingId, будет возвращен специальный код: new Result(ResultCode.LOADING, id); tinyid-сервер будет асинхронным согласно код ответа ResultCode.LOADING Назначьте следующий сегмент (4000, 5000) и т. д. В частности, используйтеСсылаться на