Что такое алгоритм Hi / Lo?

464

Что такое алгоритм Hi / Lo?

Я нашел это в документации NHibernate (это один из способов генерирования уникальных ключей, раздел 5.1.4.2), но я не нашел хорошего объяснения того, как это работает.

Я знаю, что Nhibernate справляется с этим, и мне не нужно знать изнутри, но мне просто любопытно.

DiegoCofre
источник

Ответы:

541

Основная идея заключается в том, что у вас есть два числа для составления первичного ключа - «высокое» число и «низкое» число. Клиент может в основном увеличивать последовательность «high», зная, что затем он может безопасно генерировать ключи из всего диапазона предыдущего «high» значения с помощью множества «low» значений.

Например, предположим, что у вас есть «высокая» последовательность с текущим значением 35, а «низкое» число находится в диапазоне 0-1023. Затем клиент может увеличить последовательность до 36 (чтобы другие клиенты могли генерировать ключи при использовании 35) и знать, что ключи 35/0, 35/1, 35/2, 35/3 ... 35/1023 все доступно.

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

Джон Скит
источник
14
Вы говорите, что «низкие диапазоны» координируются внутри клиента, в то время как «высокая последовательность» соответствует последовательности БД?
Крис Ноу
14
Значения hi & lo обычно затем объединяются в одно целочисленное значение или как бизнес-ключ из двух частей?
Крис Ноу
51
как, например, IP-адрес - ICANN дает вам высокий «сетевой» номер, тогда у вас будет столько низких «хостовых» номеров, сколько вам нужно, в пределах диапазона CIDR, который вы даете.
gbjbaanb
6
@ Adam: По сути, ничего - просто потенциально дешевле увеличить одно значение («верхнюю» часть), чем генерировать связку ключей. (Это потенциально намного дешевле с точки зрения передачи данных - вы можете «зарезервировать» огромное количество ключей с минимальной пропускной способностью.)
Jon Skeet
4
@ Адам: Это правда, если ключи просто цифры. Не так много для GUID :) Но да, в случае простых чисел подойдет любой атомарный «прирост на фиксированную величину». Это действительно то, что делает хай-лоу, если вы думаете об этом как одно число, разделенное на две части.
Джон Скит
157

В дополнение к ответу Джона:

Он используется, чтобы иметь возможность работать автономно. Затем клиент может запросить у сервера число hi и создать объекты, увеличивающие число lo. Нет необходимости связываться с сервером, пока не будет исчерпан диапазон lo.

Стефан Эггермонт
источник
1
Я предпочитаю это для краткости.
Разработчик Мариус Жиленас
34

Поскольку это очень распространенный вопрос, я написал эту статью , на которой основан этот ответ.

Алгоритмы hi / lo разбивают область последовательностей на группы «hi». «Привет» значение назначается синхронно. Каждой группе «hi» дается максимальное количество записей «lo», которые могут быть назначены в автономном режиме, не беспокоясь о параллельных повторяющихся записях.

  1. Токен «hi» назначается базой данных, и два одновременных вызова гарантированно видят уникальные последовательные значения
  2. После получения токена «hi» нам нужен только «incrementSize» (количество записей «lo»)
  3. Диапазон идентификаторов задается следующей формулой:

    [(hi -1) * incrementSize) + 1, (hi * incrementSize) + 1)

    и значение «lo» будет в диапазоне:

    [0, incrementSize)

    применяется от начального значения:

    [(hi -1) * incrementSize) + 1)
  4. Когда используются все значения «lo», выбирается новое значение «hi» и цикл продолжается

Вы можете найти более подробное объяснение в этой статье :

И за этой визуальной презентацией также легко следить:

введите описание изображения здесь

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

Hibernate предлагает оптимизатор pooled-lo , который предлагает преимущества стратегии генератора hi / lo, а также обеспечивает взаимодействие с другими сторонними клиентами, которые не знают об этой стратегии распределения последовательностей.

Будучи эффективным и совместимым с другими системами, оптимизатор pooled-lo является гораздо лучшим кандидатом, чем устаревшая стратегия идентификаторов hi / lo.

Влад Михалча
источник
Я действительно не понимаю вас иногда хахаха так: хотя hi / lo optimizer хорош для оптимизации генерации идентификаторов (хорошо, хорошо), он не очень хорошо работает с другими системами (что вы подразумеваете под другими системами?, Которые являются первыми из них?) вставка строк в нашу базу данных (не используется ли генерация идентификаторов для вставки строк?), не зная ничего о нашей стратегии идентификаторов.
Аделин
Другие системы, например, администратор БД, пытающийся выполнить оператор INSERT. Если она читает данные текущей последовательности, как вы думаете, легко ли определить следующее значение идентификатора, зная, что мы используем hilo в этой конкретной таблице БД?
Влад Михалча,
Приношу свои извинения, если комментарий не подходит для вашего ответа, но мне было интересно, какой оптимизатор используется по умолчанию? Или это зависит от БД (я использую PostgreSQL)? Потому что я не могу понять связь между текущим значением последовательности и сгенерированными идентификаторами. Я использую @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "name") @SequenceGenerator(name="name", sequenceName = "name_seq", allocationSize=100)для своих идентификаторов.
Стефан Голубович
1
Начиная с Hibernate 5, Pooled - это новый оптимизатор, а не Hi / Lo. Проверьте эту статью для более подробной информации о Pooled Optimizer.
Влад Михалча
@VladMihalcea, я полагаю, у вас есть опечатка в пуле три, первый фрагмент в , (hi * incrementSize) + 1)... это должно быть , hi * incrementSize), верно?
Huiagan
23

Lo - это кэшированный распределитель, который разбивает пространство ключей на большие куски, обычно основанные на некотором размере машинного слова, а не на диапазонах значимых размеров (например, получение 200 ключей за раз), которые может разумно выбрать человек.

Использование Hi-Lo имеет тенденцию тратить большое количество ключей при перезапуске сервера и генерировать большие значения, недружественные для человека.

Лучше, чем распределитель Hi-Lo, является распределителем «Линейный блок». При этом используется аналогичный принцип, основанный на таблицах, но выделяются небольшие фрагменты удобного размера и генерируются приятные для человека значения.

create table KEY_ALLOC (
    SEQ varchar(32) not null,
    NEXT bigint not null,
    primary key (SEQ)
);

Чтобы выделить следующие, скажем, 200 ключей (которые затем хранятся в качестве диапазона на сервере и используются по мере необходимости):

select NEXT from KEY_ALLOC where SEQ=?;
update KEY_ALLOC set NEXT=(old value+200) where SEQ=? and NEXT=(old value);

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

При размере фрагмента всего 20 эта схема в 10 раз быстрее, чем выделение из последовательности Oracle, и на 100% переносима среди всех баз данных. Выделение производительности эквивалентно привет-ло.

В отличие от идеи Амблера, он рассматривает пространство клавиш как непрерывную линейную числовую линию.

Это позволяет избежать стимула для составных ключей (что никогда не было хорошей идеей) и позволяет не тратить целые слова на слова при перезапуске сервера. Он генерирует «дружественные», ключевые человеческие ценности.

Идея г-на Амблера, для сравнения, выделяет старшие 16- или 32-битные значения и генерирует большие значения, недружелюбные к человеку, в качестве приращения высоких слов.

Сравнение выделенных ключей:

Linear_Chunk       Hi_Lo
100                65536
101                65537
102                65538
.. server restart
120                131072
121                131073
122                131073
.. server restart
140                196608

С точки зрения дизайна, его решение принципиально сложнее по числовой линии (составные ключи, большие продукты hi_word), чем Linear_Chunk, но не дает сравнительного преимущества.

Дизайн Hi-Lo возник на ранних этапах OO-картографирования и постоянства. В наши дни каркас персистентности, такой как Hibernate, предлагает более простые и лучшие средства выделения по умолчанию.

Томас В.
источник
4
Хороший пост, но вы не отвечаете на вопрос.
orbfish
1
+1 за интересный ответ. Я согласен, что подавляющее большинство приложений не получают преимущества от Hi-Lo по сравнению с более простым подходом; однако я думаю, что Hi-Lo лучше подходит для особого случая множественных распределителей в приложениях с высокой степенью одновременности.
Ричдж
1
Спасибо @richj! Я хочу сказать, что вы можете использовать несколько распределителей или блоков большого размера с «линейным распределением блоков», но это - в отличие от Hi / Lo - поддерживает линейное соответствие распределителя NEXT_VAL ключам в таблице и настраивается. В отличие от HiLo, умножение не требуется - это просто не нужно! Мультипликатор и хранение NEXT_HI делает HiLo более сложной и перерывы tuneability, так как изменение размера блока будет произвольно изменить следующий ключ , который будет выдан .. См: literatejava.com/hibernate/...
Thomas W
2
Я заинтересован в нескольких независимых распределителей. С Hi-Lo очевидно, что высокое значение может быть разделено на идентификатор распределителя / идентификатор блока. Для меня сразу не было очевидным, что тот же подход может быть применен к Linear Chunk, но это в основном та же проблема разделения общего диапазона между распределителями. Я понял это сейчас. Спасибо.
Ричдж
1
О, подумав об этом, я думаю, что столбец SEQ отображается на имя таблицы. Например, есть распределитель таблицы Customers, один для таблицы Orders и т. Д. Прости меня, я медленно, иногда.
Рок Энтони Джонсон
1

Я обнаружил, что алгоритм Hi / Lo идеально подходит для нескольких баз данных со сценариями репликации, основанными на моем опыте. Представь себе это. у вас есть сервер в Нью-Йорке (псевдоним 01) и другой сервер в Лос-Анджелесе (псевдоним 02), тогда у вас есть таблица PERSON ... так что в Нью-Йорке, когда человек создает ... вы всегда используете 01 в качестве значения HI и значение LO является следующей последовательной. Пример.

  • 010000010 Джейсон
  • 010000011 Дэвид
  • 010000012 Тео

в Лос-Анджелесе вы всегда используете HI 02. например:

  • 020000045 Руперт
  • 020000046 Освальд
  • 020000047 Марио

Таким образом, при использовании репликации базы данных (независимо от марки) все первичные ключи и данные легко и естественно объединяются, не беспокоясь о дублировании первичных ключей, коллизиях и т. Д.

Это лучший способ пойти по этому сценарию.

Тео
источник
Это не работает в Hibernate. HiLo algrotirm получает новое значение последовательности в каждой транзакции, поэтому счетчик HI увеличивается с нуля. Но в вашем примере HI-counter всегда постоянен для одной БД.
Dmitry1405