SQL atomique d'incrémentation et de verrouillage des stratégies - est-ce sûr?
J'ai une question à propos de SQL et de verrouillage des stratégies. Comme exemple, supposons que j'ai un compteur de vue pour les images sur mon site. Si j'ai une procédure stockée ou similaire à effectuer les déclarations suivantes:
START TRANSACTION;
UPDATE images SET counter=counter+1 WHERE image_id=some_parameter;
COMMIT;
Supposer que le compteur pour un image_id a une valeur de " 0 " à l'instant t0. Si deux sessions de mise à jour de la même image, compteur, s1 et s2, de démarrer simultanément à t0, est-il possible que ces deux sessions à la fois de lire la valeur "0", l'augmenter à '1' et les deux essayer de mettre à jour le compteur à '1', de sorte que le compteur d'obtenir la valeur '1' au lieu de '2'?
s1: begin
s1: begin
s1: read counter for image_id=15, get 0, store in temp1
s2: read counter for image_id=15, get 0, store in temp2
s1: write counter for image_id=15 to (temp1+1), which is 1
s2: write counter for image_id=15 to (temp2+1), which is also 1
s1: commit, ok
s2: commit, ok
Résultat final: pas la valeur " 1 " pour image_id=15, doit avoir été 2.
Mes questions sont:
- Ce scénario est-il possible?
- Si oui, le niveau d'isolation de transaction question?
- Est-il un outil de résolution de conflits qui permettrait de détecter un tel conflit comme une erreur?
- Peut-on utiliser n'importe syntaxe particulière afin d'éviter un problème (quelque chose comme Comparer Et Swap (CAS) ou explicite verrouillage des techniques)?
Je suis intéressé par une réponse générale, mais si on n'y fait, je suis intéressé par MySql et InnoDB-des réponses précises, car je suis en train d'utiliser cette technique pour mettre en œuvre des séquences sur InnoDB.
EDIT:
Le scénario suivant pourrait également être possible, le même comportement. Je suis en supposant que nous sommes dans le niveau d'isolation READ_COMMITED ou plus, de sorte que le s2 obtient la valeur depuis le début de la transaction, même si s1 déjà écrit " 1 " pour le compteur.
s1: begin
s1: begin
s1: read counter for image_id=15, get 0, store in temp1
s1: write counter for image_id=15 to (temp1+1), which is 1
s2: read counter for image_id=15, get 0 (since another tx), store in temp2
s2: write counter for image_id=15 to (temp2+1), which is also 1
s1: commit, ok
s2: commit, ok
Vous devez vous connecter pour publier un commentaire.
UPDATE
requête place un verrou de mise à jour sur les pages ou documents qu'il lit.Quand une décision est prise quant à la mise à jour de l'enregistrement, le verrouillage est levée ou promotion pour le verrouillage exclusif.
Cela signifie que, dans ce scénario:
s2
va attendre jusqu'à ce ques1
décide d'écrire le compteur ou pas, et ce scénario est en fait impossible.Il sera présent:
Noter que dans
InnoDB
,DML
requêtes de ne pas lever les verrous de mise à jour de fiches de lecture.Cela signifie que dans le cas d'un full table scan, les enregistrements qui ont été lus, mais a décidé de ne pas mettre à jour, restera verrouillé jusqu'à la fin de l'opération et ne peut pas être mis à jour à partir d'une autre transaction.
MVCC
quiPostgreSQL
utilise il n'y a pas de notion de verrouillage à tous. Au lieu de cela, plusieurs versions d'un enregistrement sont stockées, à l'aide de l'identifiant de transaction comme un marqueur. Le verrouillage se produit uniquement lorsque vous tentez de modifier une version dans les limbes.UPDATE
serrures pour lire les enregistrements, pas sur le niveau d'isolation de transaction (même si c'estREAD UNCOMMITTED
). Je parleSQL Server
maintenant.UPDATE
interroger sur son propre ou pour unUPDATE
requête enveloppé dans une opération comme c'est écrit dans le début de l'OP question? (Même question que @Despertar ci-dessus).Si le verrouillage n'est pas fait correctement, il est certainement possible d'obtenir ce type de condition de course, et le défaut de verrouillage de mode (mode read committed) ne lui permettent. Dans ce mode, le lit ne placer un verrou partagé sur le dossier, afin qu'ils puissent voir les 0, l'incrémenter et écrire 1 à la base de données.
Afin d'éviter cette condition de course, vous devez définir un verrou exclusif sur l'opération de lecture. 'Serializable" et "Repeatable Read' simultanéité des modes de faire, et pour une opération sur une seule ligne, ils sont à peu près équivalent.
Pour le rendre complètement atomique, vous devez:
Vous pouvez également forcer un verrou exclusif sur la lecture avec un HOLDLOCK (T-SQL), ou l'équivalent de l'indice, en fonction de votre dialecte SQL.
Une seule requête de mise à jour va le faire automatiquement, mais vous ne pouvez pas diviser l'opération (peut-être à la lecture de la valeur et de la retourner au client) sans s'assurer que le lit de prendre un verrou exclusif. Vous aurez besoin d'obtenir la valeur atomiquement afin de mettre en œuvre une séquence, de sorte que la mise à jour par lui-même est probablement pas tout à fait tout ce que vous devez. Même avec l'atome de mise à jour, vous avez encore une course à condition de lire la valeur après la mise à jour. La lecture reste à prendre place au sein d'une transaction (stockage de ce qu'il a obtenu dans une variable) et émettre un verrou exclusif au cours de la lecture.
Noter que pour ce faire sans la création d'un point chaud de votre base de données doit avoir un soutien adéquat pour autonome (imbriqué) transactions à l'intérieur d'une procédure stockée. Notez que, parfois, "imbriquées" est utilisé pour désigner le chaînage des transactions ou des points de sauvegarde, de sorte que le terme peut être un peu déroutant. J'ai édité ce à consulter les transactions autonomes.
Sans les transactions autonomes vos serrures sont héritées par les parents de la transaction, ce qui peut faire reculer l'ensemble du lot. Cela signifie qu'ils seront tenus jusqu'à ce que le parent validation de transaction, qui peuvent transformer votre séquence dans un endroit chaud que serialises toutes les transactions à l'aide de cette séquence. Rien d'autre d'essayer d'utiliser la séquence de bloc jusqu'à ce que le parent validation de transaction.
IIRC Oracle prend en charge les transactions autonomes mais DB/2 n'a pas jusqu'à assez récemment et SQL Server n'a pas. Sur le dessus de ma tête, je ne sais pas si InnoDB supporte, mais Gris et Reuter aller à une certaine longueur sur la façon dont ils sont difficiles à mettre en œuvre. Dans la pratique, je pense qu'il est tout à fait probable qu'il ne pourrait pas. YMMV.
UPDATE
requête, il n'est pas possible d'obtenir cette condition de concurrence dans les principaux systèmes qui prennent en charge les transactions, peu importe le niveau d'isolation de transaction est utilisé.s1
détient le verrou exclusif,s2
ne peut pas mettre à jour.UPDATE
requête ne pas placer des verrous partagés. Il lit toutes les données à l'aide de verrous de mise à jour.UPDATE
prend en chargeRETURNING
clauseSQL Server 2005
et au-dessus.