2010-03-12 7 views
2

Le problème est le suivant:Comment assurer la cohérence des données dans cette situation concurrente?

  • J'ai plusieurs threads concurrents (100+) qui ont besoin d'accéder à la table d'une base de données
  • Chaque thread passera un String name - où ce nom existe dans la table, la base de données devrait retourne l'identifiant de la ligne, où le nom n'existe pas déjà, le nom doit être inséré et l'identifiant retourné.
  • Il ne peut y avoir qu'une seule instance de name dans la base de données - ie. nom doit être unique

Comment puis-je faire en sorte que l'un de fil n'insérez pas name1 en même temps que deux fils essaie également d'insérer name1? En d'autres termes, comment puis-je garantir l'unicité de name dans un environnement simultané? Cela doit également être aussi efficace que possible - cela peut être un sérieux goulot d'étranglement. J'utilise MySQL et Java.

Merci

Répondre

7

En supposant qu'il existe une contrainte unique sur la colonne name, chaque insert va acquérir un verrou. Tout thread qui tente de l'insérer une deuxième fois simultanément attendra jusqu'à ce que le 1er insert réussisse ou échoue (tx commit ou rolls back).

Si la première transaction réussit, la deuxième transaction échouera avec une violation de clé unique. Alors vous savez qu'il existe déjà.

S'il y a une insertion par transaction, c'est OK. S'il y a plus d'une insertion par transaction, vous risquez de créer un blocage.

Chaque thread passera un nom de chaîne - où ce nom existe dans la table, la base de données doit renvoyer l'identifiant pour la ligne, où le nom ne existe déjà, le nom doit être inséré et l'identifiant retourné.

Donc, dans l'ensemble, l'algo est comme ceci:

1 read row with name 
    2.1 if found, return row id 
    2.2 if not found, attempt to insert 
     2.2.1 if insert succeeds, return new row id 
     2.2.2 if insert fails with unique constraint violation 
      2.2.2.1 read row with name 
      2.2.2.2 read should succeed this time, so return row id 

Parce qu'il peut y avoir une haute affirmation sur l'index unique, le insert peut bloquer pendant un certain temps. Dans ce cas, la transaction peut expirer. Faites un test de stress et réglez la configuration jusqu'à ce qu'elle fonctionne correctement avec votre charge.

En outre, vous devez vérifier si vous obtenez une exception de de violation de contrainte unique ou une autre exception.

Et encore, cela ne fonctionne que s'il y a un insert par transaction, sinon il peut deadlock .


, vous pouvez également essayer de lire la ligne à l'étape 1 avec « select * for update ». Dans ce cas, il attend qu'une insertion simultanée soit validée ou réussie. Cela peut réduire légèrement la quantité d'erreur à l'étape 2.2.2 en raison de la contention sur l'index.

+0

merci, bonne réponse - encore une chose, puis-je détecter cette exception de contrainte unique en sql? ou dois-je le laisser revenir en arrière pour être détecté en Java? – MalcomTucker

+0

oh, et une autre question - ai-je raison de penser que mysql mettra en cache les résultats des requêtes, donc en théorie si un nom a déjà été inséré et retourné une fois, je devrais pouvoir utiliser le cache pour retourner les noms existants? – MalcomTucker

+0

et désolé, encore une question - je suppose que l'algo que vous avez décrit ci-dessus peut tout être fait en SQL, mais dois-je gérer manuellement les verrous de table, ou mysql le fera pour moi? merci pour le conseil :) – MalcomTucker

3

Créez une contrainte unique sur la colonne de nom dans la base de données.

+0

En pratique, c'est un peu plus compliqué ... – ewernli

2

Ajoutez une contrainte unique pour la colonne de nom.

+0

Plus détecte correctement le problème de contrainte unique de l'exception. Cela peut être non trivial en fonction des informations renvoyées par le pilote JDBC dans l'exception. Spring le fait dans son module JDBC afin de jeter un coup d'œil à ce code si vous êtes coincé. –

+0

En pratique, c'est un peu plus compliqué ... – ewernli