Postgres, MVCC et verrouillage

8

J'ai une série d'instructions SQL qui ressemblent à ceci:

BEGIN;
SELECT counter FROM table WHERE id=X FOR UPDATE;
REALLY COMPLEX QUERY;
UPDATE table SET counter=Y WHERE id=X;
END;

Je voudrais empêcher le compteur d'être lu pendant que je recalcule sa valeur, mais selon les documents Postgres "Les verrous au niveau des lignes n'affectent pas l'interrogation des données; ils bloquent uniquement les écrivains sur la même ligne."

Des questions:

  1. Quel est l'intérêt d'un verrou de ligne "exclusif" s'il n'empêche pas les lectures? Est-ce uniquement pour empêcher d'autres transactions de prendre des verrous de partage?
  2. Si je lis la ligne avec SELECT ... FOR SHARE, cela a-t-il le même effet qu'un verrou "exclusif"?
  3. Est-il possible de désactiver MVCC pour une table / schéma / base de données et d'autoriser les écritures sur place?
Kevin
la source

Réponses:

5

à 1) Toute autre session lira les données modifiées par votre transaction telles qu'elles étaient avant votre instruction "BEGIN" tant que votre transaction n'a pas été validée. Dès votre transaction validée, il lira la nouvelle valeur du compteur. Le fait est que d'autres n'ont pas à attendre et verront toujours une base de données cohérente.

à 2), 3) Pourquoi ne pas l'essayer avec "ACCESS EXCLUSIVE"? (voir http://www.postgresql.org/docs/current/static/explicit-locking.html )

EDIT: Si vous n'aimez pas verrouiller la table entière avec un verrou "ACCESS EXCLUSIVE", vous pouvez également utiliser un "Advisory Lock" (voir section 13.3.4 dans le lien ci-dessus).

jp
la source
1

Si les lectures que vous souhaitez bloquer sont des exécutions simultanées de la même transaction uniquement avec des données différentes, utilisez une instruction UPDATE avec clause de retour.

Consultez les documents sur l'instruction UPDATE. Pour répondre à votre question sur le point d'un verrou de ligne exclusif, c'est pour empêcher l'incohérence des données des mises à jour simultanées. Les lecteurs bénéficient à tout moment d'une vue cohérente de la base de données.

Ketema
la source
1

Dans l'exemple de code que vous avez fourni, toutes les lectures de cette ligne verront l'ancienne valeur jusqu'à ce que la transaction soit validée.

1) Considérez que l'exemple de code est exécuté deux fois simultanément, avec la même valeur de X. Si l'instance A s'est exécutée, select ... for updateelle a verrouillé cette ligne jusqu'à ce qu'elle soit validée. L'instance B, faisant de même, bloquera la tentative de prise du verrou de la même manière. Ce n'est que lorsque A s'engage, que B peut continuer. B obtiendra alors la valeur A laissée dans sa dernière mise à jour.

Si cela Ydépend de la valeur select ... for updatelue par la requête, ou d'une autre lecture de celle-ci dans la partie `` complexe '', alors cela aura le même effet que si elles étaient exécutées en série - vous n'obtenez pas la condition de concurrence où l'un des résultats sera mis au rebut.

Si Yest purement le résultat de requêtes complexes au milieu, alors l'exécuter en parallèle sans select ... for updatequ'ils fassent tous les deux la même mise à jour.

2) for sharepermettra à d'autres sélections sur cette ligne de continuer, mais provoquera tout autre essai select ... for updateou update ...blocage jusqu'à ce que ce soit fait. Si l'exemple de code était exécuté deux fois, simultanément, ils se bloqueraient lors de l'accès à l' updateinstruction, provoquant l'interruption de l'un d'entre eux.

3) Non. Cela est dangereux, car un lecteur pourrait lire un champ à moitié mis à jour. (Ou lisez le reste d'un champ mis à jour après le début de la lecture.) Il peut également rompre la cohérence, montrant au lecteur une valeur mise à jour après le début de sa transaction.

MaHuJa
la source