Apa algoritma Hi / Lo?

464

Apa algoritma Hi / Lo?

Saya telah menemukan ini di dokumentasi NHibernate (ini adalah salah satu metode untuk menghasilkan kunci unik, bagian 5.1.4.2), tetapi saya belum menemukan penjelasan yang baik tentang cara kerjanya.

Saya tahu bahwa Nhibernate menanganinya, dan saya tidak perlu tahu bagian dalamnya, tetapi saya hanya ingin tahu.

DiegoCofre
sumber

Jawaban:

541

Ide dasarnya adalah Anda memiliki dua angka untuk membuat kunci utama - angka "tinggi" dan angka "rendah". Seorang klien pada dasarnya dapat meningkatkan urutan "tinggi", mengetahui bahwa ia kemudian dapat dengan aman menghasilkan kunci dari seluruh rentang nilai "tinggi" sebelumnya dengan berbagai nilai "rendah".

Misalnya, seandainya Anda memiliki urutan "tinggi" dengan nilai saat ini 35, dan angka "rendah" ada di kisaran 0-1023. Kemudian klien dapat menambah urutan ke 36 (untuk klien lain agar dapat menghasilkan kunci saat menggunakan 35) dan tahu bahwa kunci 35/0, 35/1, 35/2, 35/3 ... 35/1023 adalah semua tersedia.

Ini bisa sangat berguna (terutama dengan ORM) untuk dapat mengatur kunci utama di sisi klien, daripada memasukkan nilai tanpa kunci primer dan kemudian mengambilnya kembali ke klien. Selain dari hal lain, itu berarti Anda dapat dengan mudah membuat hubungan orang tua / anak dan memiliki kunci semuanya sebelum Anda melakukan sisipan apa pun , yang membuat mengelompokkannya menjadi lebih mudah.

Jon Skeet
sumber
14
Apakah Anda mengatakan bahwa "rentang rendah" dikoordinasikan dalam klien, sedangkan "urutan tinggi" sesuai dengan urutan DB?
Chris Noe
14
Apakah nilai hi & lo biasanya dikomposisikan menjadi nilai integer tunggal, atau sebagai kunci bisnis dua bagian?
Chris Noe
51
seperti alamat IP saat itu - ICANN memberi Anda nomor 'jaringan' yang tinggi, maka Anda memiliki nomor 'host' sebanyak yang Anda suka, dalam batas rentang CIDR yang Anda berikan.
gbjbaanb
6
@ Adam: Pada dasarnya, tidak ada - hanya berpotensi lebih murah untuk menambah satu nilai (bagian "tinggi") daripada menghasilkan banyak kunci. (Ini berpotensi jauh lebih murah dalam hal transfer data - Anda dapat "memesan" sejumlah besar kunci dengan bandwidth minimal.)
Jon Skeet
4
@ Adam: Itu benar jika kuncinya hanya angka. Tidak terlalu banyak untuk GUID :) Tapi ya, dalam kasus nomor sederhana, setiap atom "kenaikan jumlah tetap" akan dilakukan. Itu efektif apa yang hi-lo lakukan, jika Anda menganggapnya sebagai satu angka dibagi menjadi dua bagian.
Jon Skeet
157

Selain jawaban Jon:

Ini digunakan untuk dapat bekerja terputus. Seorang klien kemudian dapat meminta server untuk nomor hi dan membuat objek meningkatkan nomor lo itu sendiri. Tidak perlu menghubungi server sampai rentang lo habis.

Stephan Eggermont
sumber
1
Saya lebih suka ini untuk singkatnya.
Pengembang Marius Žilėnas
34

Karena ini adalah pertanyaan yang sangat umum, saya menulis artikel ini , yang menjadi dasar jawaban ini.

Algoritma hi / lo membagi domain urutan menjadi grup "hi". Nilai "hi" ditetapkan secara sinkron. Setiap grup "hi" diberi jumlah entri "lo" maksimum, yang dapat ditetapkan secara off-line tanpa khawatir tentang entri duplikat bersamaan.

  1. Token "hi" ditetapkan oleh basis data, dan dua panggilan bersamaan dijamin untuk melihat nilai berturut-turut yang unik
  2. Setelah "hi" token diambil, kita hanya perlu "incrementSize" (jumlah entri "lo")
  3. Rentang pengidentifikasi diberikan oleh rumus berikut:

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

    dan nilai "lo" akan berada dalam kisaran:

    [0, incrementSize)

    sedang diterapkan dari nilai awal:

    [(hi -1) * incrementSize) + 1)
  4. Ketika semua nilai "lo" digunakan, nilai "hi" baru diambil dan siklus berlanjut

Anda dapat menemukan penjelasan yang lebih rinci dalam artikel ini :

Dan presentasi visual ini juga mudah diikuti:

masukkan deskripsi gambar di sini

Meskipun optimizer hi / lo baik-baik saja untuk mengoptimalkan generasi pengidentifikasi, itu tidak cocok dengan sistem lain yang memasukkan baris ke dalam basis data kami, tanpa mengetahui apa pun tentang strategi pengidentifikasi kami.

Hibernate menawarkan optimizer pooled-lo , yang menawarkan keuntungan dari strategi generator hi / lo sambil juga memberikan interoperabilitas dengan klien pihak ketiga lainnya yang tidak mengetahui strategi alokasi urutan ini.

Menjadi efisien dan interoperable dengan sistem lain, optimizer pooled-lo adalah kandidat yang jauh lebih baik daripada strategi pengidentifikasi legacy hi / lo.

Vlad Mihalcea
sumber
Saya benar-benar tidak mengerti Anda kadang-kadang hahaha jadi: Sementara hi / lo optimizer baik-baik saja untuk mengoptimalkan generasi pengenal (Ok bagus), itu tidak cocok dengan sistem lain (apa yang Anda maksud dengan sistem lain?, Yang merupakan yang pertama yang?) menyisipkan baris ke dalam basis data kami (Bukankah generasi pengenal juga digunakan untuk menyisipkan baris?), tanpa mengetahui apa pun tentang strategi pengenal kami.
Adelin
Sistem lain, seperti DBA yang mencoba menjalankan pernyataan INSERT. Jika dia membaca data urutan saat ini, apakah menurut Anda mudah untuk mengetahui nilai pengidentifikasi berikutnya mengetahui kita menggunakan hilo dalam tabel DB khusus ini?
Vlad Mihalcea
Saya minta maaf jika komentarnya tidak cocok untuk jawaban Anda, tetapi saya bertanya-tanya apa yang digunakan pengoptimal? Atau apakah itu tergantung pada DB (Saya menggunakan PostgreSQL)? Karena saya tidak dapat menemukan hubungan antara nilai urutan saat ini dan ID yang dihasilkan. Saya menggunakan @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "name") @SequenceGenerator(name="name", sequenceName = "name_seq", allocationSize=100)ID saya.
Stefan Golubović
1
Sejak Hibernate 5, Pooled adalah Pengoptimal baru, bukan Hi / lo. Lihat artikel ini untuk detail lebih lanjut tentang Pooled Optimizer.
Vlad Mihalcea
@VladMihalcea, saya percaya Anda memiliki kesalahan ketik di peluru tiga, potongan pertama di , (hi * incrementSize) + 1)... seharusnya , hi * incrementSize), kan?
Huiagan
23

Lo adalah pengalokasi yang di-cache yang membagi ruang-ruang kunci menjadi potongan-potongan besar, biasanya didasarkan pada beberapa ukuran kata mesin, daripada rentang ukuran yang bermakna (misalnya mendapatkan 200 kunci sekaligus) yang mungkin bisa dipilih manusia dengan bijaksana.

Penggunaan Hi-Lo cenderung membuang sejumlah besar kunci pada restart server, dan menghasilkan nilai kunci besar yang tidak ramah manusia.

Lebih baik daripada pengalokasi Hi-Lo, adalah pengalokasi "Linear Chunk". Ini menggunakan prinsip berbasis tabel yang serupa tetapi mengalokasikan potongan kecil, berukuran nyaman & menghasilkan nilai ramah manusia yang bagus.

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

Untuk mengalokasikan berikutnya, katakanlah, 200 kunci (yang kemudian disimpan sebagai rentang di server & digunakan sesuai kebutuhan):

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

Asalkan Anda dapat melakukan transaksi ini (gunakan coba lagi untuk menangani pertikaian), Anda telah mengalokasikan 200 kunci & dapat mengeluarkannya sesuai kebutuhan.

Dengan ukuran chunk hanya 20, skema ini 10x lebih cepat daripada mengalokasikan dari urutan Oracle, dan 100% portabel di antara semua database. Kinerja alokasi setara dengan hi-lo.

Tidak seperti ide Ambler, ini memperlakukan ruang kunci sebagai garis angka linier yang berdekatan.

Ini menghindari dorongan untuk kunci komposit (yang tidak pernah benar-benar ide yang baik) dan menghindari pemborosan seluruh kata-kata ketika server restart. Ini menghasilkan nilai-nilai kunci "ramah" skala manusia.

Gagasan Mr Ambler, sebagai perbandingan, mengalokasikan bit 16- atau 32-bit yang tinggi, dan menghasilkan nilai-nilai kunci besar yang tidak ramah manusia sebagai kenaikan hi-words.

Perbandingan kunci yang dialokasikan:

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

Dari segi desain, solusinya secara mendasar lebih kompleks pada garis bilangan (kunci komposit, produk hi_word besar) daripada Linear_Chunk tanpa mencapai manfaat komparatif.

Desain Hi-Lo muncul lebih awal dalam pemetaan OO dan kegigihan. Saat ini kerangka kerja ketekunan seperti Hibernate menawarkan pengalokasi yang lebih sederhana dan lebih baik sebagai default.

Thomas W
sumber
4
Pos yang bagus, tetapi Anda tidak menjawab pertanyaan itu.
orbfish
1
+1 untuk jawaban yang menarik. Saya setuju bahwa sebagian besar aplikasi tidak mendapatkan keuntungan dari Hi-Lo atas pendekatan yang lebih sederhana; namun saya pikir Hi-Lo lebih cocok untuk kasus khusus beberapa pengalokasi dalam aplikasi yang sangat bersamaan.
richj
1
Terima kasih @richj! Maksud saya adalah bahwa Anda dapat menggunakan beberapa pengalokasi atau ukuran blok besar dengan "alokasi blok linier", tetapi itu - tidak seperti Hi / Lo - ia mempertahankan korespondensi linear dari pengalokasi NEXT_VAL ke kunci dalam tabel, dan dapat disesuaikan. Tidak seperti HiLo, tidak ada multiplikasi yang diperlukan - hanya saja tidak perlu! Pengganda & penyimpanan NEXT_HI membuat HiLo lebih rumit & merusak tuneability, karena mengubah ukuran blok akan secara sewenang-wenang mengubah kunci berikutnya yang akan dikeluarkan .. Lihat: literatejava.com/hibernate/…
Thomas W
2
Saya tertarik pada banyak pengalokasi independen. Dengan Hi-Lo jelas bahwa nilai tinggi dapat dipartisi menjadi ID pengalokasi / blok ID. Tidak segera jelas (bagi saya) bahwa pendekatan yang sama dapat diterapkan pada Linear Chunk, tetapi pada dasarnya masalah yang sama yaitu membagi rentang total antara pengalokasi. Saya sudah mendapatkannya sekarang. Terima kasih.
richj
1
Oh, setelah memikirkannya, saya pikir kolom SEQ memetakan ke nama tabel. Misalnya, ada pengalokasi tabel Pelanggan, satu untuk tabel Pesanan, dan sebagainya. Maafkan saya, saya lambat, kadang-kadang.
Rock Anthony Johnson
1

Saya menemukan algoritma Hi / Lo sangat cocok untuk banyak database dengan skenario replikasi berdasarkan pengalaman saya. Bayangkan ini. Anda memiliki server di New York (alias 01) dan server lain di Los Angeles (alias 02) maka Anda memiliki tabel PERSON ... jadi di New York ketika seseorang membuat ... Anda selalu menggunakan 01 sebagai nilai HI dan nilai LO adalah rahasia kedua. misalnya por.

  • 010000010 Jason
  • 010000011 David
  • 010000012 Theo

di Los Angeles Anda selalu menggunakan HI 02. misalnya:

  • 020000045 Rupert
  • 020000046 Oswald
  • 020000047 Mario

Jadi, ketika Anda menggunakan replikasi basis data (apa pun mereknya), semua kunci primer dan data digabungkan dengan mudah dan alami tanpa harus khawatir tentang duplikat kunci primer, collision, dll.

Ini adalah cara terbaik untuk masuk dalam skenario ini.

Theo
sumber
Itu tidak berfungsi di Hibernate. HiLo algrotirm mendapatkan nilai baru dari urutan dalam setiap transaksi, jadi HI-counter bertambah secara akronal. Tetapi dalam contoh Anda, HI-counter selalu konstan untuk satu DB.
Dmitry1405