Pembangkitan nomor urut terdistribusi?


103

Saya biasanya telah menerapkan pembuatan nomor urut menggunakan urutan database di masa lalu.

misalnya Menggunakan Postgres SERIAL ketik http://www.neilconway.org/docs/sequences/

Saya penasaran bagaimana cara menghasilkan nomor urut untuk sistem terdistribusi besar di mana tidak ada database. Apakah ada yang punya pengalaman atau saran tentang praktik terbaik untuk mencapai pembuatan nomor urut dengan cara yang aman untuk banyak klien?


Pertanyaan ini sudah lama, tapi tolong lihat jawaban baru saya stackoverflow.com/questions/2671858/…
Jesper M

Bagaimana Anda menggunakan nextval.org? Situs webnya agak aneh dan saya tidak tahu tentang apa. Apakah ini beberapa perintah Unix? Atau layanan cloud?
diegosasw

Jawaban:


116

Oke, ini pertanyaan yang sangat lama, yang pertama kali saya lihat sekarang.

Anda harus membedakan antara nomor urut dan ID unik yang (opsional) dapat diurutkan secara longgar berdasarkan kriteria tertentu (biasanya waktu pembuatan). Nomor urut yang benar menyiratkan pengetahuan tentang apa yang telah dilakukan semua pekerja lain, dan karena itu memerlukan status bersama. Tidak ada cara mudah untuk melakukan ini dengan cara yang terdistribusi dan berskala tinggi. Anda dapat melihat hal-hal seperti siaran jaringan, rentang berjendela untuk setiap pekerja, dan tabel hash terdistribusi untuk ID pekerja unik , tetapi ini membutuhkan banyak pekerjaan.

ID unik adalah masalah lain, ada beberapa cara bagus untuk menghasilkan ID unik dengan cara yang terdesentralisasi:

a) Anda dapat menggunakan layanan jaringan ID Snowflake Twitter . Kepingan salju adalah:

  • Layanan jaringan, yaitu Anda membuat panggilan jaringan untuk mendapatkan ID unik;
  • yang menghasilkan 64 bit ID unik yang diurutkan berdasarkan waktu pembuatan;
  • dan layanan sangat skalabel dan (berpotensi) sangat tersedia; setiap instans dapat menghasilkan ribuan ID per detik, dan Anda dapat menjalankan beberapa instans di LAN / WAN;
  • ditulis dalam Scala, berjalan di JVM.

b) Anda bisa membuat ID unik pada klien itu sendiri, menggunakan pendekatan yang diturunkan dari bagaimana UUID dan ID Snowflake dibuat. Ada beberapa opsi, tetapi sesuatu di sepanjang baris:

  • 40 bit paling signifikan atau lebih: Stempel waktu; waktu pembuatan ID. (Kami menggunakan bit paling signifikan untuk stempel waktu agar ID dapat diurutkan berdasarkan waktu pembuatan.)

  • 14 bit berikutnya atau lebih: Penghitung per generator, yang setiap generator bertambah satu untuk setiap ID baru yang dihasilkan. Ini memastikan bahwa ID yang dibuat pada saat yang sama (stempel waktu yang sama) tidak tumpang tindih.

  • 10 atau lebih bit terakhir: Nilai unik untuk setiap generator. Dengan menggunakan ini, kita tidak perlu melakukan sinkronisasi apa pun antar generator (yang sangat sulit), karena semua generator menghasilkan ID yang tidak tumpang tindih karena nilai ini.

c) Anda dapat membuat ID pada klien, hanya menggunakan stempel waktu dan nilai acak. Hal ini menghindari kebutuhan untuk mengetahui semua generator, dan memberikan nilai unik pada setiap generator. Di sisi lain, ID semacam itu tidak dijamin unik secara global, mereka sangat mungkin unik. (Untuk bertabrakan, satu atau lebih generator harus membuat nilai acak yang sama pada saat yang sama.) Sesuatu di sepanjang baris:

  • 32 bit paling signifikan: Stempel waktu, waktu pembuatan ID.
  • 32 bit paling tidak signifikan: 32 bit keacakan, dihasilkan lagi untuk setiap ID.

d) Jalan keluar yang mudah, gunakan UUIDs / GUIDs .


Cassandra mendukung penghitung ( cassandra.apache.org/doc/cql3/CQL.html#counters ), namun ada beberapa batasan.
Piyush Kansal

nomor urut mudah untuk mengatur posisi untuk indeks bitmap, tetapi id unik terkadang terlalu panjang (64bit atau 128bit), bagaimana bisa memetakan id unik ke posisi indeks bitmap? Terima kasih.
brucenan

2
benar-benar menyukai opsi #b ..... itu dapat memungkinkan untuk skala tinggi dan tidak menyebabkan banyak masalah konkurensi
puneet

2
twitter/snowflaketidak lagi dipertahankan
Navin

Jika Anda menginginkan implementasi Berlisensi Apache2 dari opsi B, lihat bitbucket.org/pythagorasio/common-libraries/src/master/… Anda juga bisa mendapatkannya dari maven io.pythagoras.common: didistribusikan-sequence-id-generator: 1.0 .0
Wpigott

16

Sekarang ada lebih banyak opsi.

Meskipun pertanyaan ini "lama", saya sampai di sini, jadi menurut saya mungkin berguna untuk meninggalkan opsi yang saya ketahui (sejauh ini):

  • Anda bisa mencoba Hazelcast . Dalam rilis 1.9 itu termasuk implementasi Terdistribusi java.util.concurrent.AtomicLong
  • Anda juga dapat menggunakan Zookeeper . Ini menyediakan metode untuk membuat node urutan (ditambahkan ke nama znode, meskipun saya lebih suka menggunakan nomor versi node). Berhati-hatilah dengan yang ini: jika Anda tidak ingin nomor yang terlewat dalam urutan Anda, itu mungkin bukan yang Anda inginkan.

Bersulang


3
Zookeeper adalah opsi yang saya gunakan
Jon

Jon, terima kasih telah menunjuk ke utas itu, itulah jenis solusi yang saya pikirkan. BTW, apakah Anda membuat kode untuk mengatasi batasan MAX_INT?
Paolo

15

Anda dapat memiliki setiap node memiliki ID unik (yang mungkin Anda miliki) dan kemudian menambahkannya ke nomor urut.

Misalnya, node 1 menghasilkan urutan 001-00001 001-00002 001-00003 dll. Dan node 5 menghasilkan 005-00001 005-00002

Unik :-)

Bergantian jika Anda menginginkan semacam sistem terpusat, Anda dapat mempertimbangkan agar server urutan Anda memberikan blok. Ini mengurangi biaya overhead secara signifikan. Misalnya, alih-alih meminta ID baru dari server pusat untuk setiap ID yang harus ditetapkan, Anda meminta ID dalam blok 10.000 dari server pusat dan kemudian hanya perlu melakukan permintaan jaringan lain saat kehabisan.


1
Saya suka maksud Anda tentang generasi id batch, tetapi itu hanya membatasi kemungkinan perhitungan waktu nyata.
ishan

Saya telah menerapkan mekanisme serupa. Dalam hal itu, selain klien yang menyimpan blok urutan, saya telah menambahkan beberapa server-host yang menyimpan cache blok urutan. Generator master (tunggal) dipertahankan di beberapa penyimpanan yang sangat tersedia atau host master tunggal, yang hanya dapat diakses oleh armada host server. Caching server juga akan membantu kami dalam lebih banyak waktu kerja meskipun master tunggal turun sesaat.
Janakiram

11

Itu bisa dilakukan dengan Redisson . Ini mengimplementasikan versi terdistribusi dan skalabel AtomicLong. Berikut contohnya:

Config config = new Config();
config.addAddress("some.server.com:8291");

Redisson redisson = Redisson.create(config);
RAtomicLong atomicLong = redisson.getAtomicLong("anyAtomicLong");
atomicLong.incrementAndGet();

8

Jika itu benar-benar harus berurutan secara global, dan tidak hanya unik, maka saya akan mempertimbangkan untuk membuat satu layanan sederhana untuk mengeluarkan nomor-nomor ini.

Sistem terdistribusi bergantung pada banyak layanan kecil yang berinteraksi, dan untuk jenis tugas sederhana ini, apakah Anda benar-benar membutuhkan atau apakah Anda benar-benar mendapat manfaat dari solusi terdistribusi yang kompleks lainnya?


3
... dan apa yang terjadi ketika server yang menjalankan layanan itu mati?
Navin

Punya peringatan yang memberitahu seseorang untuk memulai yang lain? Terkadang itu akan baik-baik saja. Saya pikir jawabannya mencoba untuk mengatakan "pertahankan segala sesuatunya dalam perspektif". Solusi terdistribusi yang sempurna memiliki kekurangannya sendiri dan terkadang lebih sederhana lebih baik.
nic ferrier

6

Ada beberapa strategi; tetapi tidak ada yang saya tahu dapat benar-benar didistribusikan dan memberikan urutan yang sebenarnya.

  1. memiliki generator nomor pusat. tidak harus database yang besar. memcachedmemiliki penghitung atom yang cepat, dalam sebagian besar kasus, ini cukup cepat untuk seluruh cluster Anda.
  2. pisahkan rentang integer untuk setiap node (seperti jawaban Steven Schlanskter )
  3. gunakan nomor acak atau UUID
  4. menggunakan beberapa bagian data, bersama dengan ID node, dan hash semuanya (atau hmac itu)

Secara pribadi, saya akan condong ke UUID, atau memcache jika saya ingin memiliki ruang yang sebagian besar bersebelahan.


5

Mengapa tidak menggunakan generator UUID (thread safe)?

Saya mungkin harus memperluas ini.

UUID dijamin unik secara global (jika Anda menghindari yang didasarkan pada nomor acak, di mana keunikan sangat mungkin terjadi).

Persyaratan "terdistribusi" Anda terpenuhi, terlepas dari berapa banyak generator UUID yang Anda gunakan, oleh keunikan global setiap UUID.

Persyaratan "thread safe" Anda dapat dipenuhi dengan memilih generator UUID "thread safe".

Persyaratan "nomor urut" Anda diasumsikan dipenuhi oleh keunikan global yang dijamin dari setiap UUID.

Perhatikan bahwa banyak implementasi nomor urut database (misalnya Oracle) tidak menjamin peningkatan secara monoton, atau (bahkan) peningkatan nomor urut (pada setiap basis "koneksi"). Ini karena kumpulan nomor urut yang berurutan dialokasikan dalam blok "cache" pada setiap koneksi dasar. Ini menjamin keunikan global dan mempertahankan kecepatan yang memadai. Tetapi nomor urut yang benar-benar dialokasikan (dari waktu ke waktu) dapat campur aduk ketika ada yang dialokasikan oleh banyak koneksi!


1
Sementara UUID bekerja, masalahnya adalah Anda harus berhati-hati dalam menyimpannya jika pada akhirnya Anda perlu mengindeks kunci yang dihasilkan. Mereka juga biasanya akan mengambil lebih banyak ruang daripada urutan yang ditingkatkan secara monoton. Lihat percona.com/blog/2014/12/19/store-uuid-optimized-way untuk diskusi tentang menyimpannya dengan MySQL.
Pavel

2

Pembuatan ID terdistribusi dapat diarsipkan dengan Redis dan Lua. Implementasinya tersedia di Github . Ini menghasilkan id unik terdistribusi dan k-sortable.


2

Saya tahu ini adalah pertanyaan lama tetapi kami juga menghadapi kebutuhan yang sama dan tidak dapat menemukan solusi yang memenuhi kebutuhan kami. Persyaratan kami adalah mendapatkan urutan unik (0,1,2,3 ... n) id dan karenanya kepingan salju tidak membantu. Kami membuat sistem kami sendiri untuk menghasilkan id menggunakan Redis. Redis adalah single threaded maka mekanisme daftar / antriannya akan selalu memberi kita 1 pop pada satu waktu.

Yang kami lakukan adalah, Kami membuat buffer dari id, Awalnya, antrian akan memiliki 0 hingga 20 id yang siap dikirim saat diminta. Beberapa klien dapat meminta id dan redis akan memunculkan 1 id sekaligus, Setelah setiap pop dari kiri, kami memasukkan BUFFER + currentId ke kanan, Yang membuat daftar buffer tetap berjalan. Implementasinya di sini


0

Saya telah menulis layanan sederhana yang dapat menghasilkan nomor panjang 64 bit semi-unik non-sekuensial. Ini dapat diterapkan pada banyak mesin untuk redundansi dan skalabilitas. Ini menggunakan ZeroMQ untuk perpesanan. Untuk informasi lebih lanjut tentang cara kerjanya, lihat halaman github: zUID


0

Menggunakan database, Anda dapat mencapai 1.000+ kenaikan per detik dengan satu inti. Sangat mudah. Anda dapat menggunakan database-nya sendiri sebagai backend untuk menghasilkan nomor tersebut (sebagaimana seharusnya merupakan agregatnya sendiri, dalam istilah DDD).

Saya memiliki masalah yang tampaknya serupa. Saya memiliki beberapa partisi dan saya ingin mendapatkan counter offset untuk masing-masing partisi. Saya menerapkan sesuatu seperti ini:

CREATE DATABASE example;
USE example;
CREATE TABLE offsets (partition INTEGER, offset LONG, PRIMARY KEY (partition));
INSERT offsets VALUES (1,0);

Kemudian dieksekusi pernyataan berikut:

SELECT @offset := offset from offsets WHERE partition=1 FOR UPDATE;
UPDATE offsets set offset=@offset+1 WHERE partition=1;

Jika aplikasi Anda memungkinkan Anda, Anda dapat mengalokasikan satu blok sekaligus (itu kasus saya).

SELECT @offset := offset from offsets WHERE partition=1 FOR UPDATE;
UPDATE offsets set offset=@offset+100 WHERE partition=1;

Jika Anda membutuhkan throughput lebih lanjut dan tidak dapat mengalokasikan offset terlebih dahulu, Anda dapat mengimplementasikan layanan Anda sendiri menggunakan Flink untuk pemrosesan waktu nyata. Saya bisa mendapatkan sekitar 100 ribu peningkatan per partisi.

Semoga membantu!


0

Masalahnya mirip dengan: Di dunia iscsi, di mana setiap lun / volume harus dapat diidentifikasi secara unik oleh pemrakarsa yang berjalan di sisi klien. Standar iscsi mengatakan bahwa beberapa bit pertama harus mewakili penyedia Penyimpanan / informasi pabrikan, dan sisanya meningkat secara monoton.

Demikian pula, seseorang dapat menggunakan bit awal dalam sistem node terdistribusi untuk mewakili nodeID dan sisanya dapat meningkat secara monoton.


1
tolong tambahkan beberapa detail lagi
Ved Prakash

0

Salah satu solusi yang layak adalah dengan menggunakan generasi berbasis waktu lama. Ini dapat dilakukan dengan dukungan dari database terdistribusi.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.