83 votes

Puis-je faire des transactions et des verrous dans CouchDB?

J'ai besoin de faire des transactions (begin, commit ou rollback), serrures (select for update). Comment puis-je le faire dans un modèle de document db?

Edit:

Le cas est la suivante:

  • Je veux lancer un site d'enchères.
  • Et je pense que la façon de l'achat direct.
  • Dans un achat direct que j'ai pour décrémenter le champ de la quantité de l'élément d'enregistrement, mais seulement si la quantité est supérieure à zéro. C'est pourquoi j'ai besoin de serrures et de transactions.
  • Je ne sais pas comment l'aborder sans serrures et/ou des transactions.

Puis-je résoudre ce avec CouchDB?

151voto

MrKurt Points 3951

Pas de. CouchDB utilise un "accès concurrentiel optimiste" modèle. En termes simples, cela signifie que vous envoyer une version de document avec la mise à jour, et CouchDB rejette le changement si l'actuelle version du document ne correspond pas à ce que vous avez envoyés.

C'est trompeusement simple, vraiment. Vous pouvez recadrer beaucoup de la transaction normale en fonction des scénarios pour CouchDB. Vous avez besoin de faire en sorte de jetez votre SGBDR domaine de connaissances lors de l'apprentissage de CouchDB. Il est utile d'aborder les problèmes à partir d'un niveau plus élevé, plutôt que d'essayer de moule Canapé pour un SQL.

Garder la trace de l'inventaire

Le problème que vous avez décrit est principalement une question d'inventaire. Si vous avez un document décrivant un élément, et il comprend un champ "quantité disponible", vous pouvez gérer des problèmes de concurrence, comme ceci:

  1. Récupérer le document, de prendre note de l' _rev bien que CouchDB envoie le long de
  2. Décrémenter le champ de la quantité, si elle est supérieure à zéro
  3. Envoyer le document mis à jour en arrière, à l'aide de l' _rev de la propriété
  4. Si l' _rev correspond au numéro actuellement stocké être fait!
  5. Si il y a un conflit (lorsqu' _rev ne correspond pas), récupérer la dernière version du document

Dans ce cas, il y a deux scénarios de panne à penser. Si la plus récente version du document a une quantité de 0, vous la gérer comme vous le feriez dans un SGBDR et de signaler à l'utilisateur qu'ils ne peuvent pas acheter ce qu'ils voulaient acheter. Si la plus récente version du document a une quantité supérieure à 0, il vous suffit de répéter l'opération avec les données mises à jour, et recommencer au début. Cela vous oblige à faire un peu plus de travail que d'un SGBDR serait, et pourrait obtenir un peu ennuyeux si il y a de fréquentes, des mises à jour contradictoires.

Maintenant, la réponse que je viens de faire suppose que vous allez faire les choses dans CouchDB de la même manière que vous le feriez dans un SGBDR. J'ai peut-être l'approche de ce problème un peu différemment:

J'aimerais commencer avec un "produit de référence" document qui comprend tous les descripteurs de données (nom, photo, description, prix, etc). Ensuite, j'ai ajouter un "inventaire ticket" document pour chaque instance spécifique, avec des champs pour l' product_key et claimed_by. Si vous vendez un modèle de marteau, et 20 d'entre eux pour vendre, vous pourriez avoir des documents avec les touches hammer-1, hammer-2, etc, pour représenter chaque marteau.

Alors, j'aimerais créer une vue qui me donne une liste de marteaux, avec une fonction de réduction qui me permet de voir un "total". Ces sont complètement à côté de la manchette, mais devrait vous donner une idée de ce que d'un point de vue travail.

Carte

function(doc) 
{ 
    if (doc.type == 'inventory_ticket' && doc.claimed_by == null ) { 
    	emit(doc.product_key, { 'inventory_ticket' :doc.id, '_rev' : doc._rev }); 
    } 
}

Cela me donne une liste de "billets", par la clé de produit. Je pourrais prendre un groupe de ces quand quelqu'un veut acheter un marteau, puis itérer à travers l'envoi de mises à jour (à l'aide de l' id et _rev) jusqu'à ce que j'ai réussi à réclamer un (auparavant demandé des billets résultera en une erreur de mise à jour).

Réduire

function (keys, values, combine) {
    return values.length;
}

Cette fonction de réduction retourne simplement le nombre total de non réclamés inventory_ticket articles, de sorte que vous pouvez dire combien de "marteaux" sont disponibles à l'achat.

Mises en garde

Cette solution représente environ 3,5 minutes du total de la pensée pour le problème que vous avez présenté. Il peut y avoir de meilleures façons de le faire! Cela dit, il n'réduire considérablement les mises à jour conflictuelles, et les coupes vers le bas sur la nécessité de répondre à un conflit avec une nouvelle mise à jour. En vertu de ce modèle, vous n'aurez pas à plusieurs utilisateurs de tenter de modifier les données dans le primaire, l'entrée d'un produit. Au pire, vous aurez de multiples utilisateurs tentant de réclamer un billet unique, et si vous avez saisi plusieurs de ceux de votre point de vue, il vous suffit de passer à côté du billet et essayez de nouveau.

27voto

kerrr Points 2015

L'expansion sur MrKurt de réponse. Pour beaucoup de scénarios, vous n'avez pas besoin d'avoir du stock de billets rachetés dans l'ordre. Au lieu de sélectionner le premier billet, vous pouvez choisir au hasard parmi le reste des billets. En raison du grand nombre de billets et un grand nombre de requêtes simultanées, vous obtiendrez beaucoup plus réduite de la contention sur ces billets, par rapport à tout le monde d'essayer d'obtenir le premier billet.

23voto

ordnungswidrig Points 2088

Un modèle de conception pour la détente des transactions est de créer une "tension" dans le système. Pour le populaire exemple de cas d'utilisation d'un compte bancaire de la transaction, vous devez vous assurer de mettre à jour le total pour les deux comptes impliqués:

  • Créer un document d'opération de "transfert de 10 USD à partir d'un compte 11223 de compte 88733". Cela crée une tension dans le système.
  • Pour régler la tension de numérisation de tous les documents de la transaction et de
    • Si le compte n'est pas mis à jour encore mettre à jour le compte de la source (-10 USD)
    • Si le compte de la source a été mis à jour mais le document ne permet pas de montrer ensuite mettre à jour le document d'opération (par exemple, définir le drapeau "sourcedone" dans le document)
    • Si le compte n'est pas mis à jour encore mettre à jour le compte cible (+10 USD)
    • Si le compte a été mis à jour mais le document ne permet pas de montrer ensuite mettre à jour le document d'opération
    • Si les deux accouts ont été mis à jour, vous pouvez supprimer l'opération de document ou de le garder pour l'audit.

Le balayage de la tension doit être fait dans un backend pour tous les "tensions " documents" pour les garder le temps de la tension dans le système. Dans l'exemple ci-dessus, il y aura un court laps de temps prévu incohérence lorsque le premier compte a été mis à jour mais le deuxième n'est pas encore été mis à jour. Ceci doit être pris en compte de la même façon que vous allez traiter avec cohérence éventuelle si votre Couchdb est distribué.

Une autre application possible permet d'éviter la nécessité pour les transactions complètement: il suffit de stocker la tension de documents et d'évaluer l'état de votre système en évaluant tous les intervenants de la tension du document. Dans l'exemple ci-dessus, cela signifie que le total pour un compte est déterminée comme la somme des valeurs dans les documents de transaction où ce compte est associé. Dans Couchdb, vous pouvez modéliser cette très joliment comme un map/reduce vue.

6voto

wallacer Points 4012

Comme une réponse à l'OP du problème, Canapé, c'est probablement pas le meilleur choix ici. À l'aide de points de vue est une excellente façon de garder une trace de l'inventaire, mais de serrage à 0 est plus ou moins impossible. Le problème étant la condition de la course lorsque vous lisez le résultat de vue, décider que vous êtes ok pour utiliser un "marteau-1", et puis écrire une doc pour l'utiliser. Le problème est qu'il n'y a pas atomique moyen de seulement écrire de la doc pour utiliser le marteau si le résultat de la vue est qu'il existe > 0 marteau-1. Si 100 utilisateurs toutes les requêtes de la vue en même temps et voir 1 marteau-1, ils peuvent écrire une doc d'utiliser un marteau 1, résultant en -99 marteau-1. Dans la pratique, la condition de la course sera assez petite, vraiment petite, si votre base de données est en cours d'exécution localhost. Mais une fois la mise à l'échelle, et avoir un site serveur de base de données ou d'un cluster, le problème sera beaucoup plus visible. Peu importe, il est inacceptable d'avoir une condition de course de ce genre dans une critique de l'argent du système.

Une mise à jour de MrKurt de réponse (il peut juste être daté, ou il peut n'avoir pas été au courant de certaines CouchDB fonctionnalités)

Une vue est une bonne façon de gérer les choses comme soldes / inventaires de CouchDB.

Vous n'avez pas besoin d'émettre la carte d'identité du document et les rev en vue. Vous obtenez à la fois de ceux gratuitement lorsque vous récupérez l'affichage des résultats. Émettant, en particulier dans un format détaillé comme un dictionnaire - ne feront que croître votre point de vue trop grandes.

D'un simple point de vue pour le suivi de l'inventaire des soldes devrait ressembler à ceci (également sur le dessus de ma tête)

function( doc )
{
    if( doc.InventoryChange != undefined ) {
        for( product_key in doc.InventoryChange ) {
            emit( product_key, 1 );
        }
    }
}

Et la fonction de réduction est encore plus simple

_sum

Il utilise un construit en fonction de réduction qui vient à la somme des valeurs de toutes les lignes avec des touches correspondantes.

Dans cette vue, tout le cod peut avoir un membre "InventoryChange" que les cartes de product_key à un changement dans l'inventaire total. c'est à dire.

{
    "_id": "abc123",
    "InventoryChange": {
         "hammer_1234": 10,
         "saw_4321": 25
     }
}

Serait ajouter 10 hammer_1234 et 25 saw_4321.

{
    "_id": "def456",
    "InventoryChange": {
        "hammer_1234": -5
    }
}

Il serait graver 5 marteaux de l'inventaire.

Avec ce modèle, vous n'êtes jamais à mettre à jour les données, uniquement ajout. Cela signifie qu'il y a aucune possibilité pour les conflits de mise à jour. Toutes les questions entourant les transactions de mise à jour des données s'en aller :)

Une autre bonne chose à propos de ce modèle est que TOUT document dans la base de données peut à la fois ajouter et de soustraire des éléments de l'inventaire. Ces documents peuvent avoir toutes sortes d'autres données. Vous pourriez avoir une "Expédition" document avec un tas de données sur la date et l'heure de réception, d'entrepôt, de la réception d'un employé, etc. et tant que le doc définit un InventoryChange, il va mise à jour de l'inventaire. Que pourrait une "Vente" du doc, et un "DamagedItem" doc etc. Chaque document, ils lisent très clairement. Et le point de vue des poignées de tout le travail dur.

2voto

Evan Points 9261

En fait, vous pouvez en quelque sorte. Regarder le HTTP API de Document et faites défiler jusqu'à la rubrique "Modifier Plusieurs Documents Avec une Seule Demande".

Fondamentalement, vous pouvez créer/mettre à jour/supprimer un tas de documents en une seule requête post *URI /{dbname}/_bulk_docs* et ils vont tous réussir ou d'échec. Le document ne garde que ce comportement peut changer dans le futur, même si.

EDIT: Comme prévu, à partir de la version 0.9 vrac docs ne fonctionne plus de cette façon.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X