Cos'è l'algoritmo Hi / Lo?


464

Cos'è l'algoritmo Hi / Lo?

Ho trovato questo nella documentazione di NHibernate (è un metodo per generare chiavi univoche, sezione 5.1.4.2), ma non ho trovato una buona spiegazione di come funziona.

So che Nhibernate lo gestisce, e non ho bisogno di conoscere l'interno, ma sono solo curioso.

Risposte:


541

L'idea di base è che hai due numeri per creare una chiave primaria: un numero "alto" e un numero "basso". Un client può sostanzialmente aumentare la sequenza "alta", sapendo che può quindi generare in sicurezza chiavi dell'intero intervallo del precedente valore "alto" con la varietà di valori "bassi".

Ad esempio, supponendo di avere una sequenza "alta" con un valore corrente di 35 e il numero "basso" sia compreso tra 0 e 1023. Quindi il client può incrementare la sequenza a 36 (per consentire ad altri client di generare chiavi mentre sta usando 35) e sapere che le chiavi 35/0, 35/1, 35/2, 35/3 ... 35/1023 sono Tutto disponibile.

Può essere molto utile (in particolare con ORM) essere in grado di impostare le chiavi primarie sul lato client, invece di inserire valori senza chiavi primarie e quindi recuperarli sul client. A parte qualsiasi altra cosa, significa che puoi facilmente stabilire relazioni genitore / figlio e avere le chiavi tutte in posizione prima di eseguire qualsiasi inserimento, il che rende più semplice il loro raggruppamento.


14
Stai dicendo che "intervalli bassi" sono coordinati all'interno del client, mentre la "sequenza alta" corrisponde a una sequenza DB?
Chris Noe,

14
I valori hi & lo vengono quindi generalmente composti in un singolo valore intero o come chiave aziendale in due parti?
Chris Noe,

51
come un indirizzo IP quindi - ICANN ti fornisce un numero di 'rete' elevato, quindi hai tutti i numeri di 'host' bassi che desideri, entro il limite dell'intervallo CIDR che ti viene dato.
gbjbaanb,

6
@Adam: Fondamentalmente, niente - è potenzialmente più economico incrementare un valore (la parte "alta") che generare un mazzo di chiavi. (È potenzialmente molto più economico in termini di trasferimento dei dati: puoi "prenotare" un numero enorme di chiavi con una larghezza di banda minima.)
Jon Skeet

4
@Adam: è vero se le chiavi sono solo numeri. Non tanto per i GUID :) Ma sì, nel caso di numeri semplici, qualsiasi "incremento di un importo fisso" atomico farà. Questo è effettivamente ciò che sta facendo hi-lo, se lo pensi come un numero diviso in due sezioni.
Jon Skeet,

157

Oltre alla risposta di Jon:

È usato per essere in grado di lavorare disconnesso. Un client può quindi chiedere al server un numero alto e creare oggetti aumentando il numero lo stesso. Non è necessario contattare il server fino all'esaurimento dell'intervallo lo.


1
Preferisco questo per brevità.
Sviluppatore Marius Žilėnas,

34

Poiché questa è una domanda molto comune, ho scritto questo articolo , su cui si basa questa risposta.

Gli algoritmi hi / lo dividono il dominio delle sequenze in gruppi "hi". Un valore "hi" viene assegnato in modo sincrono. Ad ogni gruppo "hi" viene assegnato un numero massimo di voci "lo", che possono essere assegnate off-line senza preoccuparsi delle voci duplicate simultanee.

  1. Il token "hi" è assegnato dal database e due chiamate simultanee sono garantite per vedere valori consecutivi univoci
  2. Una volta recuperato un token "hi", abbiamo solo bisogno di "incrementSize" (il numero di voci "lo")
  3. L'intervallo degli identificatori è dato dalla seguente formula:

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

    e il valore "lo" sarà compreso nell'intervallo:

    [0, incrementSize)

    applicato dal valore iniziale di:

    [(hi -1) * incrementSize) + 1)
  4. Quando vengono utilizzati tutti i valori "lo", viene recuperato un nuovo valore "hi" e il ciclo continua

Puoi trovare una spiegazione più dettagliata in questo articolo :

E anche questa presentazione visiva è facile da seguire:

inserisci qui la descrizione dell'immagine

Mentre l'ottimizzatore hi / lo va bene per l'ottimizzazione della generazione dell'identificatore, non funziona bene con altri sistemi che inseriscono righe nel nostro database, senza sapere nulla sulla nostra strategia di identificatore.

Hibernate offre l' ottimizzatore pool-lo , che offre i vantaggi della strategia del generatore hi / lo fornendo al contempo l'interoperabilità con altri client di terze parti che non sono a conoscenza di questa strategia di allocazione delle sequenze.

Essendo sia efficiente che interoperabile con altri sistemi, l'ottimizzatore in pool è un candidato molto migliore della strategia di identificatore hi / lo legacy.


Davvero non ti capisco a volte ahahah così: Mentre l'ottimizzatore hi / lo va bene per l'ottimizzazione della generazione dell'identificatore (Ok bene), non gioca bene con altri sistemi (cosa intendi con altri sistemi ?, quali sono i primi quelli?) inserendo le righe nel nostro database (la generazione dell'identificatore non ha usato anche per inserire le righe?), senza sapere nulla sulla nostra strategia di identificazione.
Adelin,

Altri sistemi, come un DBA che tenta di eseguire un'istruzione INSERT. Se legge i dati della sequenza corrente, pensi che sia facile capire il prossimo valore identificativo sapendo che usiamo hilo in questa particolare tabella DB?
Vlad Mihalcea,

Mi scuso se il commento non è adatto alla tua risposta, ma mi chiedevo quale ottimizzatore viene utilizzato per impostazione predefinita? O dipende da DB (sto usando PostgreSQL)? Perché non riesco a capire la relazione tra il valore della sequenza corrente e gli ID generati. Sto usando @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "name") @SequenceGenerator(name="name", sequenceName = "name_seq", allocationSize=100)per i miei ID.
Stefan Golubović,

1
Da Hibernate 5, Pooled è il nuovo Optimizer, non Hi / lo. Consulta questo articolo per maggiori dettagli sull'ottimizzatore in pool.
Vlad Mihalcea,

@VladMihalcea, credo che tu abbia un refuso nel terzo punto, primo frammento di , (hi * incrementSize) + 1)... dovrebbe essere , hi * incrementSize), giusto?
Huiagan,

23

Lo è un allocatore memorizzato nella cache che suddivide lo spazio delle chiavi in ​​grossi blocchi, in genere basato su alcune dimensioni di parole macchina, piuttosto che su intervalli di dimensioni significative (ad esempio, ottenendo 200 chiavi alla volta) che un essere umano potrebbe ragionevolmente scegliere.

L'utilizzo di Hi-Lo tende a sprecare un gran numero di chiavi al riavvio del server e a generare grandi valori di chiavi non amichevoli per l'uomo.

Meglio dell'allocatore Hi-Lo, è l'allocatore "Linear Chunk". Questo utilizza un principio simile basato su una tabella, ma alloca piccoli blocchi di dimensioni convenienti e genera piacevoli valori a misura d'uomo.

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

Per allocare il prossimo, diciamo, 200 chiavi (che vengono quindi mantenute come intervallo nel server e utilizzate secondo necessità):

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

A condizione che sia possibile eseguire il commit di questa transazione (utilizzare i tentativi per gestire la contesa), sono state allocate 200 chiavi e è possibile distribuirle secondo necessità.

Con una dimensione di soli 20 pezzi, questo schema è 10 volte più veloce dell'allocazione da una sequenza Oracle ed è portatile al 100% tra tutti i database. Le prestazioni di allocazione sono equivalenti a hi-lo.

A differenza dell'idea di Ambler, considera lo spazio delle chiavi come una linea numerica lineare contigua.

Questo evita l'impulso per le chiavi composite (che non sono mai state una buona idea) ed evita di sprecare intere lo-word al riavvio del server. Genera valori chiave "amichevoli", a misura d'uomo.

L'idea di Mr Ambler, al confronto, alloca gli alti 16 o 32 bit e genera grandi valori chiave poco amichevoli per l'uomo come incremento delle parole alte.

Confronto di chiavi assegnate:

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

Dal punto di vista del design, la sua soluzione è fondamentalmente più complessa sulla linea numerica (chiavi composite, grandi prodotti hi_word) rispetto a Linear_Chunk senza ottenere vantaggi comparativi.

Il design Hi-Lo è nato nelle fasi iniziali della mappatura e della persistenza OO. Oggigiorno i framework di persistenza come Hibernate offrono allocatori più semplici e migliori come impostazione predefinita.


4
Bel post, ma non stai rispondendo alla domanda.
orbfish

1
+1 per una risposta interessante. Concordo sul fatto che la stragrande maggioranza delle applicazioni non tragga alcun vantaggio da Hi-Lo rispetto all'approccio più semplice; tuttavia penso che Hi-Lo sia più adatto al caso speciale di allocatori multipli in applicazioni altamente concorrenti.
richj,

1
Grazie @richj! Il mio punto è che è possibile utilizzare più allocatori o blocchi di grandi dimensioni con "allocazione di blocchi lineare", ma che - diversamente da Hi / Lo - mantiene una corrispondenza lineare dell'allocatore NEXT_VAL con le chiavi della tabella ed è sintonizzabile. A differenza di HiLo, non è necessaria alcuna moltiplicazione: non è proprio necessario! Il moltiplicatore e l'archiviazione di NEXT_HI rendono HiLo più complesso e interrompono la sintonizzazione, poiché la modifica della dimensione del blocco modificherà arbitrariamente la chiave successiva da emettere. Vedi: literatejava.com/hibernate/…
Thomas W,

2
Sono interessato a più allocatori indipendenti. Con Hi-Lo è ovvio che l'alto valore può essere partizionato in ID allocatore / ID blocco. Non è stato immediatamente ovvio (per me) che lo stesso approccio può essere applicato a Linear Chunk, ma è fondamentalmente lo stesso problema di dividere l'intervallo totale tra allocatori. Adesso ce l'ho. Grazie.
richj,

1
Oh, dopo averci pensato, penso che la colonna SEQ sia associata a un nome di tabella. Ad esempio, esiste un allocatore per la tabella Clienti, uno per la tabella Ordini e così via. Perdonami, sono lento, a volte.
Rock Anthony Johnson,

1

Ho scoperto che l'algoritmo Hi / Lo è perfetto per più database con scenari di replica basati sulla mia esperienza. Immagina questo. hai un server a New York (alias 01) e un altro server a Los Angeles (alias 02) quindi hai una tabella PERSON ... quindi a New York quando viene creata una persona ... usi sempre 01 come valore HI e il valore LO è il secuential successivo. per esempio.

  • 010000010 Jason
  • 010000011 David
  • 010000012 Theo

a Los Angeles usi sempre HI 02. ad esempio:

  • 020000045 Rupert
  • 020000046 Oswald
  • 020000047 Mario

Pertanto, quando si utilizza la replica del database (indipendentemente dalla marca), tutte le chiavi primarie e i dati si combinano facilmente e naturalmente senza preoccuparsi di duplicare chiavi primarie, collisioni, ecc.

Questo è il modo migliore per andare in questo scenario.


Non funziona a Hibernate. HiLo algrotirm ottiene un nuovo valore di sequenza in ogni transazione, quindi gli incrementi del contatore HI in modo convenzionale. Ma nel tuo esempio, HI-counter è sempre costante per un DB.
Dmitry1405,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.