Dağıtılmış sıra numarası üretimi?


104

Geçmişte genellikle veritabanı dizilerini kullanarak sıra numarası oluşturmayı uyguladım .

örneğin, Postgres SERİ türünü kullanarak http://www.neilconway.org/docs/sequences/

Veritabanının olmadığı büyük dağıtık sistemler için sıra numaralarının nasıl üretileceğini merak ediyorum. Birden fazla müşteri için iş parçacığı güvenli bir şekilde sıra numarası oluşturmaya yönelik en iyi uygulama deneyimi veya önerisi olan var mı ?


Bu soru eski, ancak lütfen yeni cevabıma bakın stackoverflow.com/questions/2671858/…
Jesper M

Nextval.org'u nasıl kullanıyorsunuz? Web sitesi biraz garip ve ne hakkında olduğunu bilmiyorum. Bu bir Unix komutu mu? Veya biraz bulut hizmeti?
diegosasw

Yanıtlar:


117

Tamam, bu çok eski bir soru, şimdi ilk gördüğüm.

Belirli bir kritere göre (tipik olarak oluşturma zamanı) (isteğe bağlı olarak) gevşek bir şekilde sıralanabilen sıra numaraları ve benzersiz kimlikler arasında ayrım yapmanız gerekir . Gerçek sıra numaraları, diğer tüm çalışanların yaptıklarına dair bilgi anlamına gelir ve bu nedenle ortak durumu gerektirir. Bunu dağıtılmış, yüksek ölçekli bir şekilde yapmanın kolay bir yolu yoktur. Ağ yayınları, her çalışan için pencereli aralıklar ve benzersiz çalışan kimlikleri için dağıtılmış karma tablolar gibi şeylere bakabilirsiniz , ancak bu çok fazla iştir.

Benzersiz kimlikler başka bir konudur, merkezi olmayan bir şekilde benzersiz kimlikler oluşturmanın birkaç iyi yolu vardır:

a) Twitter'ın Kar Tanesi Kimliği ağ hizmetini kullanabilirsiniz . Kar tanesi bir:

  • Ağ hizmeti, yani benzersiz bir kimlik almak için bir ağ araması yaparsınız;
  • üretim zamanına göre sıralanan 64 bit benzersiz kimlikler üreten;
  • ve hizmet son derece ölçeklenebilir ve (potansiyel olarak) yüksek düzeyde kullanılabilir; her bir örnek saniyede binlerce kimlik oluşturabilir ve LAN / WAN'ınızda birden çok örnek çalıştırabilirsiniz;
  • Scala'da yazılmıştır, JVM'de çalışır.

b) UUID'lerin ve Snowflake'in kimliklerinin nasıl yapıldığından türetilen bir yaklaşımı kullanarak istemcilerin kendileri üzerinde benzersiz kimlikler üretebilirsiniz . Birden fazla seçenek var, ancak şu satırlarda bir şey var:

  • En önemli 40 veya daha fazla bit: Bir zaman damgası; kimliğin oluşturulma zamanı. (Kimlikleri oluşturma zamanına göre sıralanabilir hale getirmek için zaman damgası için en önemli bitleri kullanıyoruz.)

  • Sonraki 14 ya da öylesine bit: Oluşturulan her yeni kimlik için her jeneratörün birer birer arttığı, jeneratör başına bir sayaç . Bu, aynı anda (aynı zaman damgaları) oluşturulan kimliklerin çakışmamasını sağlar.

  • Son 10 veya daha fazla bit: Her bir oluşturucu için benzersiz bir değer.Bunu kullanarak, jeneratörler arasında herhangi bir senkronizasyon yapmamıza gerek yoktur (ki bu son derece zordur), çünkü tüm jeneratörler bu değer nedeniyle çakışmayan ID'ler üretirler.

c) Yalnızca bir zaman damgası ve rastgele bir değer kullanarak istemcilerde kimlikler oluşturabilirsiniz . Bu, tüm jeneratörleri tanıma ve her jeneratöre benzersiz bir değer atama ihtiyacını ortadan kaldırır. Diğer taraftan, bu tür kimliklerin küresel olarak benzersiz olması garanti edilmez , yalnızca çok büyük olasılıkla benzersizdir. (Çarpışmak için, bir veya daha fazla üreticinin aynı anda aynı rastgele değeri yaratması gerekir.) Şu satırlar boyunca bir şey:

  • En önemli 32 bit: Zaman damgası, kimliğin oluşturulma zamanı.
  • En az önemli 32 bit: 32 bitlik rastgelelik, her kimlik için yeniden oluşturuldu.

d) Kolay çıkış yolu, UUID'leri / GUID'leri kullanın .


Cassandra sayaçları destekler ( cassandra.apache.org/doc/cql3/CQL.html#counters ), yine de bazı sınırlamalar vardır.
Piyush Kansal

sıra numaraları, bitmap dizini için konum ayarlamak kolaydır, ancak benzersiz kimlik bazen çok uzun (64 bit veya 128 bit), bir bitmap dizin konumuna benzersiz kimlik eşlemesi nasıl olabilir? Teşekkürler.
brucenan

2
#b seçeneğini gerçekten beğendim ..... yüksek ölçeğe izin verebilir ve çok fazla eşzamanlılık sorununa neden olmaz
puneet

2
twitter/snowflakeartık
korunmuyor

Eğer seçenek B'nin Apache2 Lisanslı uygulanmasını istiyorsanız, check out bitbucket.org/pythagorasio/common-libraries/src/master/... dağıtılmış-dizisi-id-jeneratör:: Ayrıca maven io.pythagoras.common mal alabilirsiniz 1,0 .0
Wpigott

16

Şimdi daha fazla seçenek var.

Bu soru "eski" olsa da, buraya geldim, bu yüzden bildiğim seçenekleri (şimdiye kadar) bırakmanın yararlı olabileceğini düşünüyorum:

  • Hazelcast'i deneyebilirsin . 1.9 sürümünde java.util.concurrent.AtomicLong'un Dağıtılmış bir uygulamasını içerir.
  • Zookeeper'ı da kullanabilirsiniz . Sıra düğümleri oluşturmak için yöntemler sağlar (düğümlerin sürüm numaralarını kullanmayı tercih etsem de znode adlarına eklenir). Yine de buna dikkat edin: Sıranızda eksik sayılar istemiyorsanız, istediğiniz şey olmayabilir.

Şerefe


3
Zookeeper benim kullandığım seçeneklerdi, bunun iyi bir açıklaması ve başladığım posta listesinde bunun yazımı var
Jon

Jon, bu konuya işaret ettiğin için teşekkürler, tam da düşündüğüm çözüm türü buydu. BTW, MAX_INT sınırlamasının üstesinden gelmek için kod yaptınız mı?
Paolo

15

Her bir düğümün benzersiz bir kimliğine sahip olabilirsiniz (her halükarda sahip olabilirsiniz) ve ardından bunu sıra numarasının başına ekleyebilirsiniz.

Örneğin, düğüm 1 001-00001 001-00002 001-00003 vb. Dizisi üretir ve düğüm 5 005-00001 005-00002'yi oluşturur

Benzersiz :-)

Alternatif olarak, bir tür merkezi sistem istiyorsanız, sıra sunucunuzun bloklar halinde dağıtılmasını düşünebilirsiniz. Bu, genel giderleri önemli ölçüde azaltır. Örneğin, atanması gereken her bir kimlik için merkezi sunucudan yeni bir kimlik istemek yerine, merkezi sunucudan 10.000 blokluk kimlik talep edersiniz ve ardından, tükendiğinizde yalnızca başka bir ağ isteği yapmanız gerekir.


1
Toplu kimlik oluşturma hakkındaki düşüncenizi beğendim, ancak herhangi bir gerçek zamanlı hesaplama olasılığını sınırlıyor.
ishan

Benzer bir mekanizma uyguladım. Buna ek olarak, bir dizi diziyi önbelleğe alan istemcilere ek olarak, dizi bloklarını önbelleğe alan birkaç sunucu-ana bilgisayar ekledim. Bir (tek) ana jeneratör, bazı yüksek düzeyde kullanılabilir depolamada veya yalnızca sunucu ana bilgisayar filosundan erişilebilen tek ana ana bilgisayarda tutulur. Sunucuyu önbelleğe alma, aynı zamanda, tek yöneticinin bir anlığına durması durumunda daha fazla çalışma süresinde bize yardımcı olacaktır.
Janakiram

11

Redisson ile yapılabilir . Dağıtılmış ve ölçeklenebilir sürümünü uygular AtomicLong. İşte örnek:

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

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

8

Gerçekten küresel olarak sıralı olması ve sadece benzersiz olmaması gerekiyorsa, bu sayıları dağıtmak için tek ve basit bir hizmet oluşturmayı düşünürdüm.

Dağıtılmış sistemler, etkileşim içinde olan çok sayıda küçük hizmete dayanır ve bu basit görev türü için gerçekten ihtiyacınız var mı yoksa başka bir karmaşık, dağıtılmış çözümden gerçekten faydalanır mısınız?


3
... ve bu hizmeti çalıştıran sunucu çöktüğünde ne olur?
Navin

Birine başka birine başlamasını söyleyen bir uyarı var mı? Bazen bu çok iyi olacaktır. Sanırım yanıt "işleri perspektifte tut" demeye çalışıyor. Mükemmel dağıtılmış çözümün kendi dezavantajları vardır ve bazen daha basit, daha iyidir.
nic ferrier

6

Birkaç strateji var; ama bildiğim hiçbiri gerçekten dağıtılamaz ve gerçek bir sıra veremez.

  1. bir merkezi numara üreticisine sahip olun. büyük bir veritabanı olması gerekmez. memcachedhızlı bir atom sayacı vardır, çoğu durumda tüm kümeniz için yeterince hızlıdır.
  2. her düğüm için bir tam sayı aralığı ayırın ( Steven Schlanskter'ın cevabı gibi )
  3. rastgele sayılar veya UUID'ler kullanın
  4. Düğümün kimliğiyle birlikte bir miktar veri kullanın ve hepsini hash hale getirin (veya hmac yapın )

şahsen, UUID'lere eğilirim veya çoğunlukla bitişik bir alana sahip olmak istiyorsam memcached.


5

Neden bir (iş parçacığı güvenli) UUID oluşturucu kullanmıyorsunuz?

Muhtemelen bunu genişletmeliyim.

UUID'lerin küresel olarak benzersiz olması garanti edilir (benzersizliğin yalnızca yüksek olasılıklı olduğu rastgele sayılara dayalı olanlardan kaçınırsanız).

Kaç UUID jeneratörü kullanırsanız kullanın, "dağıtılmış" gereksiniminiz her bir UUID'nin genel benzersizliği ile karşılanır.

"İş parçacığı güvenli" gereksiniminiz "iş parçacığı güvenli" UUID oluşturucuları seçilerek karşılanabilir.

"Sıra numarası" gereksiniminizin, her UUID'nin garantili küresel benzersizliği tarafından karşılandığı varsayılır.

Pek çok veritabanı sıra numarası uygulamasının (örneğin Oracle), sıra numaralarının monoton olarak artmasını veya (çift) artan sıra numaralarını ("bağlantı" bazında) garanti etmediğini unutmayın. Bunun nedeni, bağlantı başına temelinde "önbelleğe alınmış" bloklara ardışık bir dizi numarası tahsis edilmesidir. Bu, küresel benzersizliği garanti eder ve yeterli hızı korur. Ancak, gerçekte tahsis edilen sıra numaraları (zamanla), birden fazla bağlantı tarafından tahsis edildiğinde karıştırılabilir!


1
UUID'ler çalışırken, onlarla ilgili sorun, en sonunda üretilen anahtarları indekslemeniz gerekirse, onları nasıl sakladığınıza dikkat etmeniz gerektiğidir. Ayrıca tipik olarak monoton olarak artırılmış bir diziden çok daha fazla yer kaplarlar. Bunları MySQL ile depolama hakkında bir tartışma için percona.com/blog/2014/12/19/store-uuid-optimized-way adresine bakın .
Pavel

2

Dağıtılmış kimlik üretimi Redis ve Lua ile arşivlenebilir. Uygulama Github'da mevcuttur . Dağıtılmış ve k-sıralanabilir benzersiz kimlikler üretir.


2

Bunun eski bir soru olduğunu biliyorum ama biz de aynı ihtiyaçla karşı karşıyaydık ve ihtiyacımızı karşılayacak çözümü bulamadık. İhtiyacımız benzersiz bir kimlik dizisi (0,1,2,3 ... n) elde etmekti ve bu nedenle kar tanesi yardımcı olmadı. Redis kullanarak kimlikleri oluşturmak için kendi sistemimizi yarattık. Redis tek iş parçacıklıdır, bu nedenle liste / kuyruk mekanizması bize her zaman bir seferde 1 pop verir.

Yaptığımız şey, bir kimlik tamponu oluşturuyoruz, Başlangıçta kuyruk, istendiğinde gönderilmeye hazır 0 ila 20 kimliği olacak. Birden fazla istemci bir id talep edebilir ve redis her seferinde 1 id açacaktır, soldan her poptan sonra, sağ tarafa BUFFER + currentId ekleriz, bu da tampon listesinin devam etmesini sağlar. Burada uygulama


0

Yarı benzersiz, sıralı olmayan 64 bit uzunluğunda sayılar üretebilen basit bir hizmet yazdım. Yedeklilik ve ölçeklenebilirlik için birden çok makineye dağıtılabilir. Mesajlaşma için ZeroMQ kullanır. Nasıl çalıştığı hakkında daha fazla bilgi için github sayfasına bakın: zUID


0

Bir veritabanı kullanarak, tek bir çekirdekle saniyede 1.000+ artıma ulaşabilirsiniz. Oldukça kolaydır. Bu numarayı oluşturmak için arka uç olarak kendi veritabanını kullanabilirsiniz (DDD açısından kendi toplamı olması gerektiği gibi).

Benzer bir sorun yaşadım. Birkaç bölümüm vardı ve her biri için bir ofset sayacı almak istedim. Bunun gibi bir şey uyguladım:

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

Ardından aşağıdaki ifadeyi yürütün:

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

Uygulamanız size izin veriyorsa, hemen bir blok tahsis edebilirsiniz (bu benim durumumdu).

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

Daha fazla verime ihtiyacınız varsa ve önceden ofsetleri tahsis edemiyorsanız, gerçek zamanlı işlem için Flink kullanarak kendi hizmetinizi uygulayabilirsiniz. Bölüm başına yaklaşık 100K artış elde edebildim.

Umarım yardımcı olur!


0

Sorun şuna benzer: Her lun / birimin istemci tarafında çalışan başlatıcılar tarafından benzersiz bir şekilde tanımlanabilmesi gereken iscsi dünyasında. İscsi standardı, ilk birkaç bitin Depolama sağlayıcı / üretici bilgilerini temsil etmesi gerektiğini ve geri kalanın monoton bir şekilde arttığını söyler.

Benzer şekilde, dağıtılmış düğüm sistemindeki ilk bitler düğüm kimliğini temsil etmek için kullanılabilir ve geri kalanı monoton olarak artabilir.


1
lütfen biraz daha ayrıntı ekleyin
Ved Prakash

0

İyi bir çözüm, uzun zamana dayalı bir nesil kullanmaktır. Dağıtılmış bir veritabanının desteği ile yapılabilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.