2010-05-20 14 views
1

J'ai écrit un programme linq-to-sql qui effectue essentiellement une tâche ETL, et j'ai remarqué de nombreux endroits où la parallélisation améliorera ses performances. Cependant, je suis préoccupé par la prévention des violations de contraintes d'unicité lorsque deux threads exécutent la tâche suivante (code Psuedo).Quel niveau d'isolation dois-je utiliser pour la transaction suivante insérer-si-non-présent?

Record CreateRecord(string recordText) 
{ 
    using (MyDataContext database = GetDatabase()) 
    { 
     Record existingRecord = database.MyTable.FirstOrDefault(record.KeyPredicate()); 
     if(existingRecord == null) 
     { 
      existingRecord = CreateRecord(recordText); 
      database.MyTable.InsertOnSubmit(existingRecord); 
     } 

     database.SubmitChanges(); 
     return existingRecord; 
    } 
} 

En général, ce code exécute une instruction SELECT pour tester pour l'enregistrement existance, suivi d'une déclaration INSERT si l'enregistrement n'existe pas. Il est encapsulé par une transaction implicite.

Lorsque deux threads exécutent ce code pour la même instance de recordText, je souhaite les empêcher de déterminer simultanément que l'enregistrement n'existe pas, essayant ainsi de créer le même enregistrement. Un niveau d'isolement et une transaction explicite fonctionneront bien, sauf que je ne suis pas certain quel niveau d'isolation je devrais utiliser - Serializable devrait fonctionner, mais semble trop strict. Y a-t-il un meilleur choix?

Répondre

1

J'utilise SQL similaire à ce qui est montré ci-dessous pour éviter de telles situations. UPDLOCK spécifie que les verrous de mise à jour doivent être pris et conservés jusqu'à la fin de la transaction et HOLDLOCK est équivalent à SERIALIZABLE. SERIALIZABLE rend les verrous partagés plus restrictifs en les conservant jusqu'à la fin d'une transaction, au lieu de libérer le verrou partagé dès que la table ou la page de données requise n'est plus nécessaire, que la transaction soit terminée ou non. L'analyse est effectuée avec la même sémantique qu'une transaction exécutée au niveau d'isolement SERIALIZABLE. HOLDLOCK ne s'applique qu'à la table ou à la vue pour laquelle il est spécifié et uniquement pour la durée de la transaction définie par l'instruction dans laquelle il est utilisé. HOLDLOCK ne peut pas être utilisé dans une instruction SELECT qui inclut l'option FOR BROWSE.

declare @LocationID   int 
declare @LocationName  nvarchar (50) 

/* fill in LocationID and LocationName appropriately */ 

INSERT dbo.Location 
(LocationID, LocationName) 
SELECT @LocationID, @LocationName 
WHERE NOT EXISTS (
    SELECT L.* 
    FROM dbo.Location L WITH (UPDLOCK, HOLDLOCK) 
    WHERE L.LocationID = @LocationID) 

Selon la réponse à this question, Serializable semble être le chemin à parcourir.