Production de numéros de séquence distribués?

j'ai généralement mis en œuvre sequence number generation en utilisant des séquences de base de données dans le passé.

p.ex. utilisant le type de série http://www.neilconway.org/docs/sequences /

je suis curieux cependant comme la façon de générer des numéros de séquence pour les grands systèmes distribués où il n'y a pas de base de données. Est-ce que quelqu'un a une expérience ou des suggestions d'une meilleure pratique pour atteindre le numéro de séquence génération d'une manière thread safe pour plusieurs clients?

82
demandé sur Lorenzo Belli 2010-04-20 04:52:50

13 réponses

OK, c'est une très vieille question, que je vois pour la première fois maintenant.

vous devez faire la différence entre les numéros de séquence et qui sont (facultativement) librement sortables par un critère spécifique (généralement le temps de génération). Les numéros de séquence vrais impliquent la connaissance de ce que tous les autres travailleurs ont fait, et en tant que tels exigent état partagé. Il n'y a pas de moyen facile de faire cela dans un distribué, à grande échelle. Vous pourriez regarder des choses comme les émissions réseau, les plages fendues pour chaque travailleur, et distribué des tables de hachage pour les travailleurs uniques IDs , mais c'est beaucoup de travail.

les identifiants uniques sont une autre question, il existe plusieurs bonnes façons de générer des identifiants uniques de manière décentralisée:

a) vous pouvez utiliser le service de réseau D'identification de flocons de neige de Twitter . Flocon De Neige est un:

  • Service en réseau, c.-à-d. que vous faites un appel réseau pour obtenir un numéro D'identification unique;
  • qui produit des identifiants uniques 64 bits qui sont commandés par génération;
  • et le service est hautement évolutif et (potentiellement) très disponible; chaque instance peut générer plusieurs milliers D'identifiants par seconde, et vous pouvez exécuter plusieurs instances sur votre LAN/WAN;
  • écrit en Scala, il court sur le JVM.

b) vous pouvez générer les identificateurs uniques sur les clients eux-mêmes, en utilisant une approche dérivé de comment UUIDs et Snowflake's IDs sont faites. il y a plusieurs options, mais quelque chose comme:

  • Le plus important 40 bits: Un horodatage; l'heure de la génération de l'ID. (Nous utilisons le plus bits significatifs pour l'horodatage pour rendre les IDs tri-able par génération de temps.)

  • les 14 bits suivants: un compteur par générateur, que chaque générateur augmente d'un pour chaque nouvelle ID générée. Cela garantit que les identifiants générés au même moment (mêmes horodateurs) ne se chevauchent pas.

  • les 10 derniers bits environ: une valeur unique pour chaque générateur. En utilisant ceci, nous n'avons pas besoin de faire de synchronisation entre les générateurs (ce qui est extrêmement difficile), car tous les générateurs produisent des ID qui ne se chevauchent pas à cause de cette valeur.

c) vous pouvez générer les ID sur les clients, en utilisant juste un timestamp et une valeur aléatoire. cela évite le besoin de connaître tous les générateurs, et attribuer à chaque générateur une valeur unique. D'un autre côté, ces identifiants ne sont pas garanti à être globalement unique, ils sont seulement très fortement probable d'être unique. (Pour entrer en collision, un ou plusieurs générateurs devraient créer la même valeur aléatoire au même moment. Quelque chose le long des lignes de:

  • Le plus important 32 bits: Timestamp l'heure de la génération de l'ID.
  • Le moins significatif de 32 bits: 32-bits du hasard, de la a généré à nouveau pour chaque ID.

d) la voie de La facilité, utiliser les Uuid / Guid .

105
répondu Jesper Mortensen 2014-02-18 22:54:14

vous pouvez demander à chaque noeud d'avoir un ID unique (que vous pouvez avoir de toute façon) et ensuite le prepend au numéro de séquence.

par exemple, le noeud 1 génère la séquence 001-00001 001-00002 001-00003 etc. et le noeud 5 génère 005-00001 005-00002

Uniques :-)

alternativement si vous voulez une sorte de système centralisé, vous pourriez envisager que votre serveur de séquence donne en blocs. Cela réduit les frais généraux considérablement. Par exemple, au lieu de demander un nouvel ID à partir du serveur central pour chaque ID qui doit être assigné, vous demandez des ID en blocs de 10.000 À partir du serveur central et vous n'avez qu'à faire une autre requête réseau lorsque vous êtes à court.

15
répondu Steven Schlansker 2010-04-20 01:08:34

maintenant il y a plus d'options.

tu cette question est "vieille", je suis arrivé ici, donc je pense qu'il pourrait être utile de laisser les options que je connais (jusqu'à présent):

  • Vous pouvez essayer de Hazelcast . Dans sa version 1.9, il inclut une implémentation distribuée de java.util.simultané.AtomicLong
  • vous pouvez également utiliser Zookeeper . Il fournit des méthodes pour créer des noeuds de séquence (ajouté aux noms de znode, je préfère utiliser les numéros de version des noeuds). Attention avec celui-ci tu: si vous ne voulez pas de nombres manqués dans votre séquence, il peut ne pas être ce que vous voulez.

Cheers

13
répondu Paolo 2010-10-04 19:13:45

Il peut être fait avec Redisson . Il implémente la version distribuée et évolutive de AtomicLong . Voici un exemple:

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

Redisson redisson = Redisson.create(config);
RAtomicLong atomicLong = redisson.getAtomicLong("anyAtomicLong");
atomicLong.incrementAndGet();
9
répondu Nikita Koksharov 2017-03-19 18:56:09

si elle doit vraiment être globalement séquentielle, et pas simplement unique, alors je envisagerais de créer un seul, simple service pour distribuer ces numéros.

systèmes distribués comptent sur beaucoup de petits services en interaction, et pour ce genre simple de tâche, avez-vous vraiment besoin ou seriez-vous vraiment bénéficier d'un autre complexe, solution distribuée?

6
répondu wsorenson 2010-04-20 01:22:35

il y a quelques stratégies, mais aucune que je sache ne peut être vraiment distribuée et donner une véritable séquence.

  1. ont un générateur de numéro central. ça n'a pas besoin d'être une grosse base de données. memcached a un compteur atomique rapide, dans la grande majorité des cas, il est assez rapide pour votre cluster entier.
  2. séparer une plage entière pour chaque noeud (comme réponse de Steven Schlanskter )
  3. utilisation nombres aléatoires ou uides
  4. utilisez un morceau de données, avec L'ID du noeud, et hachez tout (ou hmac it)

personnellement, je me pencherais vers les UUIDs, ou memcached si je veux avoir un espace principalement contigu.

6
répondu Javier 2017-05-23 12:10:27

pourquoi ne pas utiliser un générateur UUID (thread safe)?

je devrais probablement développer cela.

UUIDs sont garantis pour être globalement unique (si vous évitez ceux basés sur des nombres aléatoires, où l'unicité est tout simplement très probable).

votre exigence "distribuée" est satisfaite, quel que soit le nombre de générateurs UUID que vous utilisez, par l'unicité globale de chaque UUID.

votre exigence de" thread safe " peut être satisfait en choisissant "thread safe" générateurs UUID.

votre" numéro de séquence " est supposé être satisfait par l'unicité globale garantie de chaque UUID.

noter que de nombreuses implémentations de numéros de séquence de bases de données (par exemple Oracle) ne garantissent pas soit une augmentation monotone, soit (même) une augmentation des numéros de séquence (sur la base d'une "connexion"). C'est parce qu'un consécutives lot de numéros de séquence est attribué dans le "cache" de blocs sur par connexion. Cela garantit l'unicité globale et maintient une vitesse adéquate. Mais les numéros de séquence réellement attribués (au fil du temps) peuvent être confondus quand ils sont attribués par des connexions multiples!

3
répondu Phil 2010-04-20 03:16:44

je sais qu'il s'agit d'une vieille question, mais nous étions également confrontés au même besoin et n'avons pas été en mesure de trouver la solution qui répond à notre besoin. Notre exigence était d'obtenir une séquence unique (0,1,2,3...n) d'identifiants et donc de flocon de neige n'a pas aidé. Nous avons créé notre propre système pour générer les identificateurs à L'aide de Redis. Redis est simple threaded donc son mécanisme liste / file d'attente nous donnerait toujours 1 pop à la fois.

ce que nous faisons est, nous créons un tampon d'ids, initialement, la queue aura 0 à 20 pièces d'identité prêtes à être envoyées sur demande. Plusieurs clients peuvent demander un id et redis affichera 1 id à la fois, après chaque pop à partir de la gauche, nous insérons BUFFER + currentId à droite, ce qui maintient la liste de buffer en marche. Mise en œuvre ici

2
répondu Zohair 2018-09-07 17:31:45

Distribué ID de génération peut être archivé avec le Redis et Lua. La mise en œuvre disponibles dans Github . Il produit un ids unique distribué et K-sortable.

1
répondu SANN3 2017-04-05 09:50:38

j'ai écrit un service simple qui peut générer des nombres semi-uniques non séquentiels de 64 bits. Il peut être déployé sur plusieurs machines pour la redondance et l'évolutivité. Il utilise ZeroMQ pour la messagerie. Pour plus d'informations sur son fonctionnement, consultez la page de github: zUID

0
répondu Majid Azimi 2014-07-14 07:05:33

en utilisant une base de données, vous pouvez atteindre 1.000 incréments+ par seconde avec un seul noyau. Il est assez facile. Vous pouvez utiliser sa propre base de données comme backend pour générer ce nombre (car il devrait être son propre agrégat, en termes de DDD).

j'ai eu ce qui semble un problème similaire. J'avais plusieurs partitions et je voulais obtenir un compteur offset pour chacune d'elles. J'ai mis en œuvre quelque chose comme ceci:

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

a ensuite exécuté la déclaration suivante:

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

si votre application vous le permet, vous pouvez attribuer un bloc immédiatement (c'était mon cas).

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

si vous avez besoin de plus de débit un ne peut pas allouer des offsets à l'avance, vous pouvez mettre en œuvre votre propre service en utilisant Flink pour le traitement en temps réel. J'ai pu obtenir environ 100k incréments par partition.

Espère que cela aide!

0
répondu user2108278 2016-11-25 09:18:20

Le problème est similaire à: Dans le monde iscsi, où chaque lun / volume doit être identifiable de façon unique par les initiateurs tournant du côté du client. La norme iscsi dit que les premiers bits doivent représenter les informations du fournisseur de stockage/fabricant, et le reste augmente de manière monotone.

de la même façon, on peut utiliser les bits initiaux dans le système distribué de noeuds pour représenter le nodeID et le reste peut être monotoniquement croissant.

0
répondu user1860223 2017-08-23 03:54:52

une solution qui est décent est d'utiliser une génération basée sur le temps long. Cela peut être fait avec l'appui d'une base de données distribuée.

0
répondu refuess 2018-03-27 13:31:00