Fantastiques écluses de conseil et où ils vivent

PostgreSQL dispose d'un mécanisme très pratique pour les verrous consultatifs , ce sont également des verrous consultatifs . Chez Tensor, nous les utilisons à de nombreux endroits du système, mais peu de gens comprennent en détail comment ils fonctionnent exactement et quels problèmes peuvent être obtenus s'ils sont maltraités .



En savoir plus sur les serrures

Avantages des verrous de recommandation


La différence fondamentale entre ce mécanisme et les verrous «ordinaires» de table / page / enregistrement est qu'il existe plusieurs fonctionnalités clés.

Verrouillage ID personnalisé


Les verrous «normaux» dans PG sont toujours liés à un objet de base de données spécifique (table, enregistrement, page de données) et au processus desservant la connexion. Les verrous consultatifs sont également un processus, mais au lieu d'un objet réel, un identifiant abstrait peut être défini comme (bigint) ou comme (entier, entier) .

Soit dit en passant, attacher chaque verrou à un processus signifie qu'en le nommant via pg_terminate_backend(pid)ou en complétant correctement la connexion du côté client, vous pouvez vous débarrasser de tous les verrous imposés par lui.

CAS Check for Lock Capture


CAS est une comparaison et un ensemble , c'est -à- dire que la vérification de la capacité de capture et la capture du verrou lui-même ont lieu comme une seule opération atomique, et évidemment personne ne peut «se caler» entre eux.

Autrement dit, si vous faites d'abord une demande de vérification à pg_locks , regardez le résultat, puis décidez de verrouiller ou non, alors personne ne peut garantir qu'entre ces opérations, personne n'arrivera à prendre l'objet dont vous avez besoin. Mais si vous utilisez pg_try_advisory_lock , alors vous recevrez immédiatement ce verrou, ou la fonction reviendra simplement FALSE.

No-capture sans exceptions et attentes


Des verrous «normaux» existent dans le modèle «Si vous avez déjà demandé un verrou, attendez. Si vous ne voulez pas attendre ( NOWAIT, statement_timeout, lock_timeout) - est ici une exception " . Cette approche interfère considérablement dans la transaction, car vous devez alors implémenter un bloc BEGIN-EXCEPTION-ENDpour le traitement ou annuler ( ROLLBACK) la transaction.

La seule façon d'éviter ce comportement est d'utiliser la conception SELECT ... SKIP LOCKEDfournie avec la version 9.5. Malheureusement, avec cette méthode, les options «n’avaient pas du tout quoi bloquer» et «était, mais étaient déjà bloquées» deviennent indiscernables.

Les verrous recommandés causés par les fonctions try reviennent simplement TRUE/FALSE.
Ne confondez pas pg_advisory_locket - la première fonction attendra jusqu'à ce qu'elle reçoive un verrou, et la seconde retournera immédiatement immédiatement s'il est impossible de le capturer "maintenant".pg_try_advisory_lockFALSE

Se verrouille pendant et après la transaction


Comme je l'ai mentionné ci-dessus, les verrous d'objet sont «attachés» au processus et n'existent que dans le cadre de la transaction en cours. Même juste pour imposer - ne réussira pas:

LOCK TABLE tbl;
-- ERROR:  LOCK TABLE can only be used in transaction blocks

En conséquence, à la fin de la transaction, tous les verrous qui lui sont imposés sont supprimés. En revanche, les verrous consultatifs ont été initialement conçus avec la capacité de maintenir des verrous et en dehors du champ d'une transaction :

SELECT pg_advisory_lock(1);
SELECT * FROM pg_locks WHERE pid = pg_backend_pid() AND locktype = 'advisory';

-[ RECORD 1 ]------+--------------
locktype           | advisory
database           | 263911484
relation           |
page               |
tuple              |
virtualxid         |
transactionid      |
classid            | 0 <--  int4 #1    int8
objid              | 1 <--  int4 #2    int8
objsubid           | 1
virtualtransaction | 416/475768
pid                | 29264
mode               | ExclusiveLock
granted            | t
fastpath           | f

Mais déjà à partir de la version 9.1, des versions xact de fonctions consultatives sont apparues qui vous permettent d'implémenter le comportement des verrous "ordinaires" qui sont automatiquement libérés lorsque la transaction qui les a imposés est terminée.

Exemples d'utilisation dans VLSI


En fait, comme tout autre verrou, le conseil sert à garantir l'unicité du traitement d'une ressource. Dans notre pays, ces ressources sont généralement soit la table entière, soit un enregistrement spécifique de la table, qui pour une raison quelconque ne veut pas être "verrouillé".

Monotraitement du travailleur


Si la nécessité de traiter certaines données dans la base de données est déclenchée par un événement externe, mais que le multitraitement est redondant ou peut conduire à une condition de concurrence critique , il est raisonnable de faire fonctionner un seul processus sur ces données à la fois .

Pour ce faire, nous allons essayer de verrouiller avec l'identificateur de table comme premier paramètre et l'ID de traitement d'application spécifique comme deuxième:

SELECT pg_try_advisory_lock(
  'processed_table'::regclass::oid
, -1 --   worker'
);

Si nous revenons FALSE, alors quelqu'un d'autre détient déjà un tel verrou, et spécifiquement ce processus n'a rien à faire, mais il est préférable de terminer tranquillement. C'est ainsi que fonctionne, par exemple, le processus de calcul dynamique du coût des marchandises dans un entrepôt , de manière isolée pour chaque schéma client individuel.

Traitement des files d'attente simultanées


Maintenant, la tâche est «l'opposé» - nous voulons que les tâches d'une table de file d'attente soient traitées aussi rapidement que possible, multithread, tolérantes aux pannes et même à partir de logiques métier différentes (enfin, nous n'en avons pas la puissance) - par exemple, comme le fait notre opérateur sur le transfert des rapports électroniques aux agences gouvernementales ou au service OFD .

Les BL de «traitement» étant différents serveurs, aucun mutex ne peut plus être raccroché. Il n'est pas sûr de désigner un coordinateur de processus de distribution de tâches spéciales, de «mourir» lui - et tout augmentera. Et il s'avère donc qu'il est plus efficace de distribuer les tâches directement au niveau de la base de données, et il y a une telle façon - dans les temps anciens, le modèle a été honnêtement espionné par Dmitry Koterov , puis modifié de manière créative.

Dans ce cas, nous imposons un verrou sur l'ID de table et PK d'un enregistrement particulier:

SELECT
  *
FROM
  queue_table
WHERE
  pg_try_advisory_lock('queue_table'::regclass::oid, pk_id)
ORDER BY
  pk_id
LIMIT 1;

Autrement dit, le processus recevra de la table le premier record qui n'a pas encore été bloqué par ses frères concurrents.

Cependant, si PK ne se compose pas de (entier) , mais de (entier, entier) (comme dans le même calcul de coût, par exemple), vous pouvez imposer un verrou directement sur cette paire - il est peu probable qu'une intersection avec le "concurrent" se produise.

Important! N'oubliez pas d' entretenir régulièrement et correctement votre table de file d'attente !

Traitement exclusif des documents


Il est utilisé partout dans les solutions de gestion de documents . En effet, dans un système web distribué, un même document peut être ouvert pour être visualisé par différents utilisateurs en même temps, mais il ne peut être traité (changer son état, etc.) à tout moment que par un seul.

Problèmes traditionnels


Où sans eux! Presque tout se résume à une chose : ils n'ont pas déverrouillé ce qu'ils ont verrouillé .

Superposition multiple d'un verrou consultatif


RTFM , comme on dit:
Si plusieurs demandes de blocage sont reçues en même temps, elles s'accumulent, donc si une ressource a été bloquée trois fois, elle doit être débloquée trois fois pour être disponible dans d'autres sessions.

Mettez trop de verrous à la fois




Des milliers d'entre eux! Lisez le manuel de nouveau :
Les verrous consultatifs et réguliers sont stockés dans la zone de mémoire partagée, dont la taille est déterminée par les paramètres de configuration max_locks_per_transactionet max_connections. Il est important que cette mémoire soit suffisante, car sinon le serveur ne pourra émettre aucun verrou . Ainsi, le nombre de verrous recommandés qu'un serveur peut émettre est généralement limité à des dizaines ou des centaines de milliers, selon la configuration du serveur.

En général, si une situation survient lorsque vous souhaitez imposer plusieurs milliers de verrous consultatifs (même si vous les supprimez correctement plus tard) - réfléchissez bien où vous exécuterez lorsque le serveur se lèvera.

Fuites lors du filtrage des enregistrements


Ici, nous prenons la demande précédente et ajoutons une condition inoffensive telle que le contrôle de parité ID - AND pk_id % 2 = 0. Les deux conditions pour chaque inscription seront vérifiées ! En conséquence, il a été pg_try_advisory_lockterminé, le verrou a été superposé, puis l'enregistrement a été filtré par parité.



Ou une option du manuel:
SELECT pg_advisory_lock(id) FROM foo WHERE id = 12345; -- ok
SELECT pg_advisory_lock(id) FROM foo WHERE id > 12345 LIMIT 100; -- !

C'est tout - le blocage demeure , mais nous n'en sommes pas conscients. Il est traité avec les demandes correctes, dans le pire des cas - pg_advisory_unlock_all.

Oh, confus!


Des classiques du genre ...

Confus et , et se demandent pourquoi ça marche depuis longtemps. Mais parce que la version non-try attend . Confondre et , et se demander où est passé le verrou - et cela "s'est terminé" avec la transaction. Et la transaction consistait en une seule demande, car nulle part elle n'a été déclarée "explicitement", oui.pg_try_advisory_lockpg_advisory_lock

pg_try_advisory_lockpg_try_advisory_xact_lock

Travailler avec pgbouncer


C'est une source de douleur distincte pour beaucoup lorsque, pour des raisons de performance, travailler avec la base de données passe par pgbouncer en mode transaction .

Cela signifie que deux de vos transactions voisines qui s'exécutent sur la même connexion à la base de données (qui passe par le pgbouncer) peuvent être exécutées dans différentes connexions "physiques" du côté de la base de données. Et ils ont leurs propres serrures ... Chacun a



quelques options:

  • ou allez travailler via une connexion directe à la base de données
  • ou inventer un algorithme pour que tous les verrous consultatifs se trouvent uniquement dans la transaction (xact)

C'est tout pour le moment.

All Articles