Question

Comment arrêtez-vous les conditions de course dans MySQL? le problème en question est causé par un algorithme simple:

  1. sélectionnez une ligne dans la table
  2. s'il n'existe pas, insérez-le

puis vous obtenez une ligne en double ou une erreur si vous l'évitez via des clés uniques / primaires.

Normalement, je penserais que les transactions aident ici, mais comme la ligne n’existe pas, la transaction n’aide en réalité (ou manque-t-il quelque chose?).

LOCK TABLE ressemble à un excès, en particulier si le tableau est mis à jour plusieurs fois par seconde.

La seule autre solution à laquelle je puisse penser est GET_LOCK () pour chaque identifiant différent, mais n'y a-t-il pas un meilleur moyen? N'y at-il pas de problèmes d'évolutivité ici aussi? Et aussi, le faire pour chaque table semble un peu contre nature, car cela me semble être un problème très courant dans les bases de données à haute concurrence.

Était-ce utile?

La solution

Ce que vous voulez, c'est TABLES DE VERROUILLAGE

ou si cela semble excessif, que diriez-vous de INSERT IGNORE avec vérifier que la ligne a bien été insérée.

  

Si vous utilisez le mot clé IGNORE, des erreurs   qui se produisent lors de l'exécution de la commande INSERT   déclaration sont traités comme des avertissements   à la place.

Autres conseils

Il me semble que vous devriez avoir un index unique sur votre colonne id. Ainsi, une insertion répétée déclencherait une erreur au lieu d'être à nouveau acceptée aveuglément.

Cela peut être fait en définissant l'id en tant que clé primaire ou en utilisant un index unique.

Je pense que la première question que vous devez vous poser est la suivante: pourquoi avez-vous plusieurs discussions effectuant le même travail exact? Pourquoi devraient-ils insérer exactement la même ligne?

Après avoir répondu à cette question, je pense que le fait d’ignorer les erreurs sera la solution la plus performante, mais mesurez les deux approches (GET_LOCK v / s ignore les erreurs) et constatez par vous-même.

Je ne connais aucun autre moyen. Pourquoi voulez-vous éviter les erreurs? Vous devez encore coder pour le cas où un autre type d'erreur se produit.

Comme le dit staticsan, les transactions aident, mais comme elles sont implicites, si deux insertions sont exécutées par des threads différents, elles se trouveront toutes deux dans une transaction implicite et verront des vues cohérentes de la base de données.

Verrouiller toute la table est vraiment excessif. Pour obtenir l'effet souhaité, vous avez besoin de quelque chose que la littérature appelle "verrous de prédicats". Personne n'a jamais vu ceux-ci sauf imprimé sur le papier sur lequel des études académiques sont publiées. La deuxième meilleure chose à faire est de verrouiller les " chemins d’accès " aux données (dans certains SGBD: "page locks").

Certains systèmes non SQL vous permettent de réaliser les deux opérations (1) et (2) en une seule instruction, ce qui correspond plus ou moins aux conditions de concurrence potentielles résultant de la suspension de votre système d’exécution de votre fil d’exécution entre (1) et (2). , sont entièrement éliminés.

Néanmoins, en l’absence de verrouillage des prédicats, de tels systèmes devront toujours recourir à un système de verrouillage, et plus la "granularité" sera fine (/ "scope") des verrous qu'il prend, mieux vaut la concurrence.

(Et pour conclure: certains SGBD, en particulier ceux pour lesquels vous n'avez pas à payer, n'offrent en effet pas une granularité de verrouillage plus fine que "la totalité de la table".)

Sur le plan technique, une transaction sera utile ici car les autres threads ne verront pas la nouvelle ligne tant que vous ne aurez pas validé la transaction.

Mais dans la pratique, cela ne résout pas le problème - cela le le déplace . Votre application doit maintenant vérifier si le commit échoue et décider quoi faire. Je voudrais normalement annuler ce que vous avez fait et redémarrer la transaction, car maintenant la ligne sera visible. C’est ainsi que le programmeur basé sur les transactions est censé fonctionner.

J'ai rencontré le même problème et cherché un moment sur Internet:)

Enfin, j’ai trouvé une solution semblable à la méthode suivante: créer des objets de système de fichiers dans des répertoires partagés (temporaires) pour ouvrir des fichiers temporaires en toute sécurité:

$exists = $success = false;
do{
 $exists = check();// select a row in the table 
 if (!$exists)
  $success = create_record();
  if ($success){
   $exists = true;
  }else if ($success != ERROR_DUP_ROW){
    log_error("failed to create row not 'coz DUP_ROW!");
    break;
  }else{
    //probably other process has already created the record,
    //so try check again if exists
  }
}while(!$exists)

N'ayez pas peur de la boucle occupée , elle s'exécutera normalement une ou deux fois.

Vous évitez très simplement les lignes en double en ajoutant des index uniques à vos tables. Cela n’a rien à voir avec LOCKS ou TRANSACTIONS.

Est-ce que vous vous inquiétez si une insertion échoue parce que c'est un doublon? Avez-vous besoin d'être averti en cas d'échec? Ou est-ce que tout ce qui compte que la rangée ait été insérée importe peu et par qui ou combien d'insertions en double ont échoué?

Si vous ne vous en souciez pas, alors tout ce dont vous avez besoin est INSERT IGNORE . Il n'est pas nécessaire de penser aux transactions ou aux verrous de table.

InnoDB a le verrouillage de niveau ligne automatiquement, mais cela ne s'applique qu'aux mises à jour et aux suppressions. Vous avez raison, cela ne s'applique pas aux inserts. Vous ne pouvez pas verrouiller ce qui n'existe pas encore!

Vous pouvez explicitement LOCK sur la table entière. Mais si votre but est d'empêcher les doublons, vous le faites mal. Encore une fois, utilisez un index unique.

Si vous devez effectuer un ensemble de modifications et que vous souhaitez obtenir un résultat tout ou rien (ou même un ensemble de résultats tout ou rien dans un résultat tout ou rien plus important), utilisez des transactions et points de sauvegarde. Utilisez ensuite ROLLBACK ou ROLLBACK TO SAVEPOINT * nom_point_sauvegarde * pour annuler les modifications, y compris les suppressions, les mises à jour et .

Les tables

LOCK ne remplacent pas les transactions, mais constituent votre seule option avec les tables MyISAM, qui ne prennent pas en charge les transactions. Vous pouvez également l'utiliser avec des tables InnoDB si le verrouillage au niveau des lignes ne suffit pas. Voir cette page pour plus d'informations sur utilisation de transactions avec des instructions de table de verrouillage.

J'ai un problème similaire. J'ai une table qui, dans la plupart des cas, devrait avoir une valeur unique ticket_id, mais il existe parfois des doublons; Ce n’est pas le meilleur design, mais c’est ce qu’il est.

  1. L'utilisateur A vérifie si le ticket est réservé, ce n'est pas
  2. L'utilisateur B vérifie si le ticket est réservé, ce n'est pas
  3. L'utilisateur B insère un enregistrement "réservé" dans la table pour ce ticket
  4. L'utilisateur A insère un enregistrement "réservé" dans la table pour ce ticket
  5. L'utilisateur B recherche les doublons? Oui, mon disque est-il plus récent? Oui, laissez-le
  6. Utilisateur Un contrôle de doublon? Oui, mon disque est-il plus récent? Non, supprimez-le

L'utilisateur B a réservé le ticket. L'utilisateur A rapporte que le ticket a été pris par quelqu'un d'autre.

Dans mon cas, la clé est que vous avez besoin d'un tie-break, dans mon cas, c'est l'identifiant d'auto-incrémentation de la ligne.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top