Эта статья написанаyanglbmeНачалось вДокументы технического сообщества GitHub, текущие звезды превысили 30k.
адрес проекта:GitHub.com/ohohgenerate/advpress…
вопросы интервью
Как работать с первичным ключом id после подбазы данных и подтаблицы?
Психологический анализ интервьюера
Фактически, это проблема, с которой вы должны столкнуться после подбазы данных и подтаблицы, то есть как сгенерировать идентификатор? Потому что если он разделен на несколько таблиц, каждая таблица накапливается из 1, это должно быть неправильно, вам нуженглобально уникальныйid для поддержки. Так что это все проблемы, которые вы должны учитывать в своей реальной производственной среде.
Анализ вопросов интервью.
Реализация на основе базы данных
Идентификатор автоинкремента базы данных
Это означает, что каждый раз, когда вы получаете идентификатор в своей системе, вы вставляете часть данных, не имеющих бизнес-значения, в таблицу библиотеки, а затем получаете идентификатор, который автоматически увеличивается базой данных. Получив этот идентификатор, запишите его в соответствующую подтаблицу подбазы данных.
Преимущество этой программы в том, что она удобна и проста, и ею может пользоваться любой желающий;Недостатком является то, что генерируется одна библиотекаСамоинкрементный id, если будет высокий параллелизм, будут узкие места; если вы просто хотите улучшить, то открывайте сервис специально, этот сервис каждый раз будет получать текущий максимальный id, а затем самостоятельно увеличивать несколько id, один раз вернуть пакет идентификаторов, а затем изменить текущее максимальное значение идентификатора на значение после увеличения нескольких идентификаторов; нов любом случае основан на одной базе данных.
подходящая сцена: есть две причины для вашей подбазы данных и подтаблицы: либо параллелизм в одной базе данных слишком высок, либо объем данных в одной базе данных слишком велик; если это не выПараллелизм невелик, но объем данных слишком великВ результате расширения подбаз данных и подтаблиц вы можете использовать это решение, поскольку максимальный параллелизм в секунду может составлять не более нескольких сотен, поэтому вы можете использовать отдельную библиотеку и таблицу для генерации самоинкрементных первичных ключей.
Установите последовательность базы данных или размер поля автоинкремента таблицы
Горизонтальное масштабирование можно выполнить, задав последовательность базы данных или размер шага автоинкремента поля таблицы.
Например, теперь есть 8 сервисных узлов, и каждый сервисный узел использует функцию последовательности для генерации идентификаторов.Начальные идентификаторы каждой последовательности разные и увеличиваются в свою очередь с размером шага 8.
подходящая сцена: это решение относительно просто реализовать, и оно также может достигать целей производительности, когда пользователи предотвращают создание повторяющихся идентификаторов. Однако сервисные узлы фиксированы и размер шага тоже фиксированный, в будущем добавить сервисные узлы будет сложно.
UUID
Преимущество в том, что он генерируется локально, а не на основе базы данных, недостаток в том, что UUID слишком длинный и занимает много места.Низкая производительность в качестве первичного ключаЧто еще более важно, UUID не упорядочены, что приведет к слишком большому количеству случайных операций записи при записи индекса дерева B+ (последовательные идентификаторы могут генерировать частичные последовательные записи), а также потому, что они не могут быть записаны при записи. требуется операция вставки. Весь узел дерева B+ будет прочитан в память, и весь узел будет записан обратно на диск после вставки записи. Эта операция требует большого количества места для записи. , производительность значительно падает.
Подходящий сценарий: Если вы хотите случайным образом сгенерировать имя файла, номер и т. д., вы можете использовать UUID, но UUID нельзя использовать в качестве первичного ключа.
UUID.randomUUID().toString().replace(“-”, “”) -> sfsdf23423rr234sfdaf
Получить текущее время системы
Это для получения текущего времени, но проблема в том,Когда параллелизм высок, например несколько тысяч одновременно в секунду,будут повторы, это точно неуместно. В общем, не думайте об этом.
Подходящие сценарии: Как правило, если вы используете это решение, вы будете комбинировать текущее время со многими другими бизнес-полями в качестве идентификатора.Если вы считаете, что это приемлемо в бизнесе, то это также возможно. Вы можете комбинировать другие значения бизнес-полей с текущим временем, чтобы сформировать глобально уникальный номер.
алгоритм снежинки
Алгоритм снежинки — это открытый алгоритм генерации распределенного идентификатора твиттера, реализованный на языке Scala, использующий 64-битный идентификатор, 1 бит не используется, 41 бит используется как количество миллисекунд, а 10 бит В качестве рабочей машины используются .id, 12 бит в качестве серийного номера.
- 1 бит: Нет, почему? Поскольку первый бит в двоичном коде отрицательный, если он равен 1, но все идентификаторы, которые мы генерируем, положительны, поэтому первый бит всегда равен 0.
- 41 бит: указывает отметку времени в миллисекундах. 41 бит может представлять числа до
2^41 - 1
, то есть можно определить2^41 - 1
Значение миллисекунды, переведенное во взрослое значение, составляет 69 лет. - 10 бит: запишите идентификатор рабочей машины, что означает, что эту службу можно развернуть на 2^10 машинах, то есть на 1024 машинах. Но в 10 битах 5 бит представляют собой идентификатор машинного зала, а 5 бит представляют собой идентификатор машины. означает наиболее
2^5
компьютерные залы (32 компьютерных зала), каждый компьютерный зал может представлять2^5
машин (32 машины). - 12 бит: используется для записи различных идентификаторов, сгенерированных в течение одной миллисекунды.Наибольшее положительное целое число, которое может представлять 12 бит, равно
2^12 - 1 = 4096
, то есть его можно отличить по числу, представленному этим 12-битнымв течение той же миллисекунды4096 различных идентификаторов .
0 | 0001100 10100010 10111110 10001001 01011100 00 | 10001 | 1 1001 | 0000 00000000
public class IdWorker {
private long workerId;
private long datacenterId;
private long sequence;
public IdWorker(long workerId, long datacenterId, long sequence) {
// sanity check for workerId
// 这儿不就检查了一下,要求就是你传递进来的机房id和机器id不能超过32,不能小于0
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));
}
System.out.printf(
"worker starting. timestamp left shift %d, datacenter id bits %d, worker id bits %d, sequence bits %d, workerid %d",
timestampLeftShift, datacenterIdBits, workerIdBits, sequenceBits, workerId);
this.workerId = workerId;
this.datacenterId = datacenterId;
this.sequence = sequence;
}
private long twepoch = 1288834974657L;
private long workerIdBits = 5L;
private long datacenterIdBits = 5L;
// 这个是二进制运算,就是 5 bit最多只能有31个数字,也就是说机器id最多只能是32以内
private long maxWorkerId = -1L ^ (-1L << workerIdBits);
// 这个是一个意思,就是 5 bit最多只能有31个数字,机房id最多只能是32以内
private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
private long sequenceBits = 12L;
private long workerIdShift = sequenceBits;
private long datacenterIdShift = sequenceBits + workerIdBits;
private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
private long sequenceMask = -1L ^ (-1L << sequenceBits);
private long lastTimestamp = -1L;
public long getWorkerId() {
return workerId;
}
public long getDatacenterId() {
return datacenterId;
}
public long getTimestamp() {
return System.currentTimeMillis();
}
public synchronized long nextId() {
// 这儿就是获取当前时间戳,单位是毫秒
long timestamp = timeGen();
if (timestamp < lastTimestamp) {
System.err.printf("clock is moving backwards. Rejecting requests until %d.", lastTimestamp);
throw new RuntimeException(String.format(
"Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
}
if (lastTimestamp == timestamp) {
// 这个意思是说一个毫秒内最多只能有4096个数字
// 无论你传递多少进来,这个位运算保证始终就是在4096这个范围内,避免你自己传递个sequence超过了4096这个范围
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0;
}
// 这儿记录一下最近一次生成id的时间戳,单位是毫秒
lastTimestamp = timestamp;
// 这儿就是将时间戳左移,放到 41 bit那儿;
// 将机房 id左移放到 5 bit那儿;
// 将机器id左移放到5 bit那儿;将序号放最后12 bit;
// 最后拼接起来成一个 64 bit的二进制数字,转换成 10 进制就是个 long 型
return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift)
| (workerId << workerIdShift) | sequence;
}
private long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
private long timeGen() {
return System.currentTimeMillis();
}
// ---------------测试---------------
public static void main(String[] args) {
IdWorker worker = new IdWorker(1, 1, 1);
for (int i = 0; i < 30; i++) {
System.out.println(worker.nextId());
}
}
}
Как вы это говорите, может быть, это означает, то есть 41 бит - это временная метка в текущей единице миллисекунды, вот что это значит; тогда 5 бит - это то, что вы передали вмашинное отделениеid (но максимум может быть только в пределах 32), а остальные 5 бит передаются вамимашинаid (но максимум может быть только в пределах 32), оставшийся 12-битный серийный номер, то есть если в последний раз вы генерировали id в пределах одной миллисекунды, то для вас будет накапливаться последовательность, вплоть до 4096 серийных номера .
Таким образом, вы используете этот класс инструментов для создания службы самостоятельно, а затем инициализируете такую вещь для каждой машины в каждой компьютерной комнате.Серийный номер машины в этой компьютерной комнате в начале равен 0. Затем каждый раз, когда вы получаете запрос о том, что машина в компьютерной комнате хочет сгенерировать идентификатор, вы найдете соответствующего работника для генерации.
Используя этот алгоритм снежинки, вы можете развивать свои собственные услуги компании.Даже для id машинного зала и id машины, 5 бит + 5 бит зарезервированы для вас в любом случае, и вы можете заменить его другими вещами с бизнес-значением .
Этот алгоритм снежинки относительно надежен, поэтому, если вы действительно занимаетесь распределенной генерацией id, если это высокий параллелизм, то у него должна быть более высокая производительность, в общем этого достаточно для десятков тысяч одновременных сценариев в секунду.
Добро пожаловать в мою общедоступную учетную запись WeChat «Сообщество открытого исходного кода Doocs». Оригинальные технические статьи будут опубликованы как можно скорее.