web-dev-qa-db-fra.com

Comment puis-je verrouiller la ligne de lecture / mise à jour dans db2?

J'essaie de résoudre une condition de course dans mon application.

J'ai une table utilisée comme une file d'attente de travail et de nombreux threads et la lecture/mise à jour. La requête suivante est exécutée à partir de l'application. Tout d'abord, il lit l'identifiant des travaux disponibles, puis il les réserve et obtient les informations.

SELECT ID FROM JOBS WHERE LOCKED_BY IS NULL LIMIT 5; --Check available jobs
UPDATE JOBS LOCKED_BY = 'Thread#1' WHERE ID IN (?); --Lock jobs
SELECT * FROM JOBS WHERE ID IN(?); --Get info about jobs to process

La question se pose lorsque plusieurs threads vont à cette table en même temps. Cela se passe-t-il si 'Thread#2' lit la ligne avant 'Thread#1' exécute le UPDATE. Causer les mêmes travaux à exécuter deux fois.

J'ai trouvé une solution en enveloppant les requêtes dans:

LOCK TABLE JOBS;
--Queries from above
COMIT;

Cela fonctionne bien et empêche les conditions de la race, mais c'est un peu extrême, car les fils doivent attendre l'autre pour la fin.

Comment puis-je vous assurer que je ne peux pas placer une serrure uniquement sur les enregistrements de la requête suivante?

SELECT ID FROM JOBS WHERE LOCKED_BY IS NULL LIMIT 5;

Ceci est sur la base de données AS400 DB2, maintenant LUW

1
Ruslan

Pourquoi utilisez-vous une table? L'IBM I fournit un objet de la file d'attente de données natif ...

Si votre application est en cours d'exécution dans une langue dans une langue qui n'offre pas un accès direct aux objets natifs, vous pouvez toujours créer une procédure stockée ou une fonction définie par l'utilisateur sur le I pour donner accès à la file d'attente de données.

Cela dit, la réponse à votre question est la même pour tout SGBDM, utilisation niveaux d'isolement.

Dans votre cas, vous souhaitez utiliser Lire la stabilité

Comme le niveau RR, la stabilité de lecture de niveau (RS) garantit:

Toute ligne lu lors d'une unité de travail n'est pas modifiée par d'autres groupes d'activation utilisant différentes définitions d'engagement jusqu'à ce que l'unité de travail soit complète. 1 Toute ligne modifiée (ou une ligne actuellement verrouillée avec un verrou de ligne de mise à jour) par un autre groupe d'activation à l'aide d'une définition d'engagement différente ne peut être lue tant qu'elle n'est pas commise.

Simplement ajouter WITH RS à vos déclarations ...

SELECT ID FROM JOBS WHERE LOCKED_BY IS NULL WITH RS LIMIT 5
UPDATE JOBS LOCKED_BY = 'Thread#1' WHERE ID IN (?) WITH RS
COMMIT

La première déclaration sera verrouillée (jusqu'à) 5 lignes..tulé le COMMIT est terminé.

Notez que la table devra être journalisée si elle n'est pas déjà.

3
Charles

Comme une alternative au verrouillage (courant avec isolation RS aura toujours des filets attendez-vous et éventuellement une impasse, à moins que vous SELECT .. FOR UPDATE), si vous pouvez tolérer le traitement de moins de 5 emplois à la fois, vous pouvez utiliser une approche optimiste:

UPDATE JOBS LOCKED_BY = 'Thread#1' WHERE ID IN (?) AND LOCKED_BY IS NULL; --Lock jobs
SELECT * FROM JOBS WHERE ID IN(?) AND LOCKED_BY = 'Thread#1'; --Get info 
0
mustaccio