Hi / Loアルゴリズムとは何ですか?


464

Hi / Loアルゴリズムとは何ですか?

これはNHibernateのドキュメントで見つかりました(一意のキーを生成する1つの方法、セクション5.1.4.2です)が、それがどのように機能するかについての適切な説明は見つかりませんでした。

私はNhibernateがそれを処理することを知っており、内部を知る必要はありませんが、興味があるだけです。

回答:


541

基本的な考え方は、主キーを構成するために「高い」番号と「低い」番号の2つの番号があるということです。クライアントは基本的に「高」シーケンスをインクリメントできます。これにより、さまざまな「低」値を持つ以前の「高」値の範囲全体からキーを安全に生成できることがわかります。

たとえば、現在の値が35である「高い」シーケンスがあり、「低い」数が0〜1023の範囲にあるとします。次に、クライアントはシーケンスを36に増やし(他のクライアントが35を使用しているときにキーを生成できるようにするため)、キー35 / 0、35 / 1、35 / 2、35 / 3 ... 35/1023がすべて利用可能。

主キーなしで値を挿入してからクライアントにフェッチする代わりに、クライアント側で主キーを設定できると(特にORMで)非常に便利です。別に何か他のものから、それはあなたが簡単に親/子関係を作り、あなたが行う前に、すべての場所に鍵を持つことができることを意味任意の単純それらをバッチ処理になり挿入し、。


14
「高範囲」はDBシーケンスに対応するのに対し、「低範囲」はクライアント内で調整されるということですか。
Chris Noe、

14
hi&loの値は通常、単一の整数値に構成されるのですか、それとも2部構成のビジネスキーとして構成されるのですか?
Chris Noe、

51
IPアドレスのように-ICANNは高い「ネットワーク」番号を提供し、指定されたCIDR範囲の制限内で、好きなだけ低い「ホスト」番号を持ちます。
gbjbaanb 09

6
@Adam:基本的には何もありません。1つの値(「高い」部分)を増やす方が、大量のキーを生成するよりも安くなる可能性があります。(データ転送の点ではるかに安価になる可能性があります-最小限の帯域幅で膨大な数のキーを「予約」できます。)
Jon Skeet

4
@Adam:キーが数字だけの場合も同様です。GUIDの場合はそれほどではありません:)しかし、はい。単純な数値の場合、アトミックな「固定量の増分」で十分です。これは、1つの数値を2つのセクションに分割したものと考えると、hi-loが実際に行っていることです。
Jon Skeet、2010

157

ジョンの答えに加えて:

切断された状態で作業できるようにするために使用されます。その後、クライアントはサーバーに高数値を要求し、低数値自体を増やすオブジェクトを作成できます。lo範囲が使い果たされるまで、サーバーに接続する必要はありません。


1
簡潔にするために、これを好みます。
開発者MariusŽilėnas19年

34

これは非常に一般的な質問であるため、この回答に基づいてこの記事を作成しました。

hi / loアルゴリズムは、シーケンスドメインを「hi」グループに分割します。「hi」の値は同期して割り当てられます。すべての「hi」グループには、最大数の「lo」エントリが与えられます。これは、同時に重複するエントリを心配することなくオフラインで割り当てることができます。

  1. 「hi」トークンはデータベースによって割り当てられ、2つの同時呼び出しが一意の連続値を参照することが保証されています
  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はプールされたloオプティマイザーを提供します。これはhi / loジェネレーター戦略の利点を提供すると同時に、このシーケンス割り当て戦略を認識していない他のサードパーティクライアントとの相互運用性を提供します。

他のシステムと効率的で相互運用可能なため、pooled-loオプティマイザーは、従来のhi / lo識別子戦略よりもはるかに優れた候補です。


hi / loオプティマイザーは識別子の生成を最適化するのに適していますが(Ok good)、他のシステムではうまく機能しません(他のシステムとはどういう意味ですか?) ones?)行をデータベースに挿入する(識別子の生成は行の挿入にも使用されませんか?)、識別子の戦略について何も知らない。
アデリン2017

INSERTステートメントを実行しようとしているDBAなどの他のシステム。彼女が現在のシーケンスデータを読み取った場合、この特定のDBテーブルでhiloを使用していることを知っていれば、次の識別子の値を理解するのは簡単だと思いますか?
Vlad Mihalcea 2017

コメントがあなたの答えに適していない場合は申し訳ありませんが、デフォルトでどのオプティマイザが使用されているのかと思っていましたか?それともDBに依存していますか(私はPostgreSQLを使用しています)?現在のシーケンス値と生成されたIDの関係を理解できないためです。@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "name") @SequenceGenerator(name="name", sequenceName = "name_seq", allocationSize=100)IDに使用しています。
StefanGolubović19年

1
Hibernate 5以降、Pooledは新しいオプティマイザーであり、Hi / loではありません。チェックアウト詳細については、この記事をプールされたオプティマイザについて。
Vlad Mihalcea

@VladMihalcea、私はあなたが箇条書き3の誤植を持っていると信じています、最初のスニペット, (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のこのスキームは、Oracleシーケンスから割り当てるよりも10倍高速で、すべてのデータベース間で100%移植可能です。アロケーション性能はハイローと同等です。

アンブラーのアイデアとは異なり、キースペースは連続した線形のナンバーラインとして扱われます。

これにより、複合キーの推進力(実際には決して良い考えではありませんでした)が回避され、サーバーの再起動時に単語全体が無駄になることが回避されます。「友好的な」人間規模のキー値を生成します。

対照的に、アンブラー氏のアイデアは、上位の16ビットまたは32ビットを割り当て、hi-wordsが増加するにつれて、人間に不便な大きなキー値を生成します。

割り当てられたキーの比較:

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

設計的には、彼のソリューションは、Linear_Chunkよりも基本的に数直線(複合キー、大きなhi_word製品)で複雑ですが、比較上の利点はありません。

Hi-Lo設計は、OOマッピングと永続化の初期に発生しました。最近のHibernateなどの永続化フレームワークは、デフォルトとしてよりシンプルで優れたアロケータを提供しています。


4
いい投稿ですが、質問に答えていません。
orbfish 2014年

1
興味深い答えは+1です。アプリケーションの大部分は、シンプルなアプローチよりもHi-Loを利用してもメリットがないことに同意します。ただし、Hi-Loは、並行性の高いアプリケーションで複数のアロケーターを使用する特殊なケースに適していると思います。
richj 2014年

1
@richjに感謝!私のポイントは、「線形ブロック割り当て」で複数のアロケーターまたは大きなブロックサイズを使用できることですが、それは、Hi / Loとは異なり、アロケーターNEXT_VALとテーブル内のキーの線形対応を維持し、調整可能です。HiLoとは異なり、乗算は必要ありません。乗算は必要ありません。NEXT_HIの乗数&ストレージは、任意に発行される次のキーを変更しますブロックサイズを変更するので、ハイローより複雑&休憩同調可能になります。..を参照してください:literatejava.com/hibernate/...
トーマス・W

2
複数の独立したアロケータに興味があります。Hi-Loでは、高い値をアロケータID /ブロックIDに分割できることは明らかです。同じアプローチをリニアチャンクに適用できることはすぐにはわかりませんでしたが、アロケーター間の合計範囲を分割することは基本的に同じ問題です。私は今それを持っています。ありがとう。
richj

1
ああ、それについて考えた後、SEQ列はテーブル名にマップすると思います。たとえば、Customersテーブル、Ordersテーブルのアロケーターなどがあります。私を許して、私は時々遅いです。
ロックアンソニージョンソン

1

Hi / Loアルゴリズムは、私の経験に基づく複製シナリオを持つ複数のデータベースに最適であることがわかりました。これを想像してみてください。ニューヨーク(エイリアス01)にサーバーがあり、ロサンゼルス(エイリアス02)に別のサーバーがある場合、PERSONテーブルがあります...したがって、ニューヨークでは、ユーザーが作成されると、HI値として常に01を使用します。そしてLO値は次の秘密です。例。

  • 010000010ジェイソン
  • 010000011デビッド
  • 010000012テオ

ロサンゼルスでは、常にHI 02を使用します。次に例を示します。

  • 020000045ルパート
  • 020000046オズワルド
  • 020000047マリオ

したがって、データベースレプリケーションを使用すると(どのブランドでも)、主キーの重複や衝突などを心配することなく、すべての主キーとデータが簡単かつ自然に結合されます。

これは、このシナリオでの最善の方法です。


Hibernateでは機能しません。HiLo algrotirmは各トランザクションでシーケンスの新しい値を取得するため、HIカウンターはそれに応じて増分します。しかし、あなたの例では、HIカウンターは常に1つのDBに対して一定です。
Dmitry1405
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.