2009-02-04 6 views
0

Si le code suivant est appelé à partir de plusieurs threads dans une application, existe-t-il un risque de blocage? La transaction utilisée pour se connecter à la base de données à cet effet est ouverte juste avant cet appel et fermée dès son retour. Application: Java Base de données: OracleBase de données Deadlocks lors de l'utilisation de Rownum?

FUNCTION reserveWork(in_batch_id NUMBER, 
         in_work_size NUMBER, 
         in_contentType_id NUMBER) RETURN NUMBER IS 
    rows_reserved NUMBER := 0; 

    BEGIN 
    UPDATE 
      D_Q1 
    SET 
      DQ1_BAT_ID = in_batch_id 
    WHERE 
     DQ1_BAT_ID is null 
     AND DCT_ID = in_contentType_id 
     AND ROWNUM < (in_work_size + 1); 

    rows_reserved := SQL%ROWCOUNT; 

    RETURN (rows_reserved); 

    END; 

Répondre

2

Pour qu'un blocage se produise, vous devez avoir ces deux conditions.

  1. Chaque transaction doit être verrouillée plusieurs fois.

  2. Les verrous doivent être saisis dans un ordre différent.

La condition 1 est vraie car chacune de vos unités d'exécution verrouille plusieurs lignes. La condition 2 est vraie en théorie car l'ordre des lignes renvoyées n'est pas déterministe. Par exemple, le thread 1 pourrait essayer de mettre à jour les lignes 1,2,3 et le thread 2 pourrait essayer de mettre à jour les lignes 3,2,1.

En pratique, Oracle peut toujours renvoyer les lignes dans le même ordre afin de ne jamais créer de blocage. Quoi qu'il en soit, préparez-vous à gérer l'erreur ORA-00060 et renvoyez la demande.

Une autre idée consiste à le faire en deux étapes. La première procédure fait un SELECT * WHERE ... FOR UPDATE NO WAIT pour verrouiller les lignes, si cela ne retourne pas un ORA-00054, la deuxième procédure fait la mise à jour réelle. Sinon, vous réessayez.

Dans tous les cas, assurez-vous que INITTRANS dans votre table CREATE TABLE est défini sur le nombre de clients qui mettra à jour simultanément la table.

+0

Wow, j'ai lu la réponse de Tom Kyte référencée dans la réponse de Gary ci-dessous. Très sympa! Cela signifie que si la transaction bloque, elle sera redémarrée, donc elle ne devrait pas être bloquée. –

1

Il y a un risque certain de blocage si vous utilisez plusieurs mises à jour sur la même table.

D'autant que je ne peux pas voir un COMMIT ou ROLLBACK dans votre code? Je présume que cela est fait dans le JDBC?

Plus la mise à jour prendra de temps, plus le risque de blocage sera élevé.

+0

Oui, commit/rollback est fait immédiatement suite à un appel à cette fonction en fonction de si une exception a été générée ou non. – Adam

1

Un blocage se produit lorsque la transaction A verrouille un enregistrement doit alors attendre la transaction B pour déverrouiller un enregistrement, alors que la transaction B est en attente sur un dossier déjà verrouillé par transaction A.

Oracle dispose d'un mécanisme assez sophistiqué pour gérer les modifications apportées aux tables au cours d'une mise à jour. Voir

http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:11504247549852 

En général, le risque d'un blocage augmente le plus court une transaction et les données plus un changement de transaction. Je dirais qu'il est peu probable que cela se bloque, mais il risque de faire la queue - si vous avez trois ou quatre sessions simultanées exécutant ce SQL, chaque session aura le même chemin d'exécution pour le SQL, identifiera les mêmes lignes pour la mise à jour, l'un arrivera en premier, les autres attendront. Lorsque cette première transaction est terminée, une autre réintègre les enregistrements, trouve qu'ils sont modifiés et redémarre comme décrit dans l'article de Tom Kyte et sélectionne le prochain groupe de lignes.

Si vous êtes sur 11g, il y a un SKIP LOCKED que vous pouvez utiliser. Il est présent, mais non documenté, dans les versions antérieures. Donc, il serait utilisé à vos risques et périls.

http://download.oracle.com/docs/cd/B28359_01/server.111/b28286/statements_10002.htm#SQLRF01702 

De cette façon, vous auriez

SELECT primary_key BULK COLLECT INTO pk_variable_array FROM D_Q1 
WHERE DQ1_BAT_ID is null 
AND DCT_ID = in_contentType_id 
AND ROWNUM < (in_work_size + 1) 
FOR UPDATE SKIP LOCKED; 
-- 
FORALL i in 1..pk_variable_array 
UPDATE D_Q1 
SET DQ1_BAT_ID = in_batch_id 
WHERE primary_key = pk_variable_array(i) 
Questions connexes