Що таке алгоритм Привіт / Ло?


464

Що таке алгоритм Привіт / Ло?

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

Я знаю, що Nhibernate справляється з цим, і мені не потрібно знати всередині, але мені просто цікаво.

Відповіді:


540

Основна ідея полягає у тому, що у вас є два числа для складання первинного ключа - "високого" числа та "низького" числа. Клієнт може в основному збільшити послідовність "високої", знаючи, що потім може безпечно генерувати ключі з усього діапазону попереднього "високого" значення з різноманітністю "низьких" значень.

Наприклад, припустимо, що у вас є "висока" послідовність із поточним значенням 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: По суті, нічого - просто потенційно дешевше збільшити одне значення ("високу" частину), ніж генерувати купу ключів. (Це потенційно набагато дешевше з точки зору передачі даних - ви можете "зарезервувати" величезну кількість клавіш з мінімальною пропускною здатністю.)
Джон Скіт

4
@Adam: Це правда, якщо ключі - це просто цифри. Не так багато для GUIDs :) Але так, у випадку простих чисел будь-яке атомне «збільшення на фіксовану суму» зробить. Це ефективно те, що робить привіт-ло, якщо ви думаєте про це як одне число, розділене на два розділи.
Джон Скіт

157

Окрім відповіді Йона:

Він використовується, щоб мати можливість працювати відключеним. Потім клієнт може запитати у сервера високий номер та створювати об'єкти, збільшуючи номер lo. Не потрібно звертатися до сервера, поки не буде використаний діапазон lo.


1
Я віддаю перевагу цьому для стислості.
розробник Marius Žilėnas

34

Оскільки це дуже поширене питання, я написав цю статтю , на якій ґрунтується ця відповідь.

Алгоритми hi / lo розбивають домен послідовностей на групи "привіт". Значення "привіт" призначається синхронно. Кожній групі "привіт" надається максимальна кількість записів "lo", які можуть бути призначені в режимі офлайн, не турбуючись про одночасні повторювані записи.

  1. База даних присвоює маркер "привіт", і два одночасні дзвінки гарантовано бачать унікальні послідовні значення
  2. Після отримання знака "привіт" нам потрібен лише "incrementSize" (кількість записів "lo")
  3. Діапазон ідентифікаторів задається наступною формулою:

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

    і значення "lo" буде в діапазоні:

    [0, incrementSize)

    застосовується від початкового значення:

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

Більш детальне пояснення ви можете знайти в цій статті :

І цю візуальну презентацію також легко прослідкувати:

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

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

Hibernate пропонує оптимізатор об'єднаного ло-ло , який пропонує переваги стратегії генератора hi / lo, а також забезпечує взаємодію з іншими сторонніми клієнтами, які не знають про цю стратегію розподілу послідовностей.

Будучи одночасно ефективним та сумісним з іншими системами, оптимізатор, що об'єднався, є набагато кращим кандидатом, ніж застаріла стратегія ідентифікаторів hi / lo.


Я насправді не розумію, що ти іноді ха-ха. Так: Хоча оптимізатор привіт / ло відмінно підходить для оптимізації генерації ідентифікаторів (гаразд), він не грає добре з іншими системами (що ви маєте на увазі під іншими системами? Які перші ті, що вставляють рядки в нашу базу даних (чи не створюється генерація ідентифікаторів для вставки рядків?), не знаючи нічого про нашу стратегію ідентифікаторів.
Аделін

Інші системи, такі як DBA, що намагаються запустити оператор INSERT. Якщо вона читає дані поточної послідовності, чи вважаєте ви, що легко визначити наступне значення ідентифікатора, знаючи, що ми використовуємо hilo в цій конкретній таблиці БД?
Влад Міхалча

Вибачте, якщо коментар не підходить для вашої відповіді, але мені було цікаво, який оптимізатор використовується за замовчуванням? Або це залежить від БД (я використовую PostgreSQL)? Тому що я не можу з’ясувати співвідношення між поточним значенням послідовності та згенерованими ідентифікаторами. Я використовую @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "name") @SequenceGenerator(name="name", sequenceName = "name_seq", allocationSize=100)для своїх посвідчень особи.
Стефан Голубович

1
Починаючи з режиму hibernate 5, Pooled є новим оптимізатором, а не привіт / ло. Перегляньте цю статтю, щоб отримати докладнішу інформацію про об'єднаний оптимізатор.
Влад

@VladMihalcea, я вважаю, у вас помилка друку в кулі третьою, перший фрагмент у , (hi * incrementSize) + 1)... це має бути , hi * incrementSize), правда?
Хуяган

23

Ло - це кешований розподільник, який розбиває простір клавіш на великі шматки, як правило, виходячи з певного розміру машинного слова, а не за значущим розміром (наприклад, отримання 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% портативною серед усіх баз даних. Ефективність розподілу рівнозначна привіт-ло.

На відміну від ідеї Амблера, він розглядає простір клавіш як суміжну лінійну числову лінію.

Це дозволяє уникнути поштовху для складених ключів (які ніколи насправді не були гарною ідеєю) і уникає витрачати цілі lo-слова при перезапуску сервера. Це породжує "дружні", людські ключові цінності.

Ідея містера Амблера, для порівняння, виділяє високі 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 виникла на початку картографування та стійкості ОО. У ці дні стійкі рамки, такі як сплячий режим, пропонують простіші та кращі розподільники за замовчуванням.


4
Гарний пост, але ви не відповідаєте на питання.
orbfish

1
+1 для цікавої відповіді. Я згоден, що переважна більшість додатків не отримує переваги від Hi-Lo над простим підходом; однак, я думаю, що Hi-Lo краще підходить для особливих випадків декількох розподільників у дуже сумісних програмах.
richj

1
Дякую @richj! Моя думка полягає в тому, що ви можете використовувати декілька розподільників або великих розмірів блоків з "лінійним розподілом блоку", але це - на відміну від Hi / Lo - підтримує лінійну відповідність розподільника NEXT_VAL ключам таблиці і піддається налаштуванню. На відміну від HiLo, множення не потрібно - це просто не потрібно! Помножувач і зберігання NEXT_HI робить HiLo складнішим і порушує настроюваність, оскільки зміна розміру блоку може довільно змінити наступний ключ, який буде видано. Див.: Literatejava.com/hibernate/…
Thomas W

2
Мене цікавлять кілька незалежних розподільників. З Hi-Lo очевидно, що високе значення можна розділити на ідентифікатор розподільника / ідентифікатор блоку. Не відразу було очевидно (для мене), що той самий підхід може бути застосований до лінійного куска, але це в основному та сама проблема поділу загального діапазону між розподільниками. У мене це зараз. Дякую.
richj

1
О, подумавши про це, я думаю, що стовпець SEQ позначає ім’я таблиці. Наприклад, є розподільник таблиці «Клієнти», одна для таблиці «Замовлення» тощо. Пробачте, іноді я повільний.
Рок Ентоні Джонсон

1

Я виявив, що алгоритм Привіт / Ло ідеально підходить для декількох баз даних із сценаріями реплікації, що базується на моєму досвіді. Уявіть це. у вас є сервер у Нью-Йорку (псевдонім 01) та інший сервер у Лос-Анджелесі (псевдонім 02), тоді у вас є таблиця ОСОБИСТОСТІ ... так у Нью-Йорку, коли людина створюється ... ви завжди використовуєте 01 як значення HI а значення LO - наступна секвенціальна. пор-приклад.

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

у Лос-Анджелесі ви завжди використовуєте HI 02. Наприклад:

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

Тож, коли ви використовуєте реплікацію бази даних (незалежно від того, яка марка), всі первинні ключі та дані поєднуються легко і природно, не турбуючись про повторювані первинні ключі, колізії тощо.

Це найкращий шлях для цього сценарію.


Це не працює в сплячому режимі. HiLo algrotirm отримує нове значення послідовності в кожній транзакції, тому приріст HI-лічильника відбувається відповідно. Але у вашому прикладі HI-лічильник завжди постійний для однієї БД.
Дмитро1405
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.