2010-11-09 11 views
0

J'ai deux processus qui fonctionnent avec des données dans la même table.Concurrence - un processus se met à jour, un autre insère

Un processus insère quotidiennement, un par un (ADO.NET pur), environ 20000 enregistrements dans la table cible.

Le second appels de processus (périodiquement, toutes les 15 minutes) une procédure stockée qui

  1. détecte les doublons dans ces 20000 dossiers en regardant tous les dossiers arrière 7 jours et les marque en tant que telle.
  2. Marque tous les enregistrements qui ne sont pas des doublons avec un indicateur 'ToBeCopied'.
  3. Sélectionnez un nombre de colonnes parmi les enregistrements marqués comme 'ToBeCopied' et renvoyez l'ensemble.

Parfois, ces deux processus se chevauchent (en raison de retards dans le traitement des données) et je suis Soupçonnant que si le premier processus insère de nouveaux enregistrements lorsque le deuxième processus est quelque part entre 1 et 2 alors les enregistrements seront marqués « ToBeCopied » sans avoir passé à travers le tamisage en double.

Cela signifie que la procédure de stockage renvoie maintenant des doublons.

C'est ma théorie mais en pratique, je n'ai pas été en mesure de le reproduire ...

J'utilise LINQ to SQL pour insérer des doublons (40-50 ou si une seconde) et tout cela est en cours d'exécution I J'appelle manuellement la procédure stockée et stocke ses résultats.

Il semble que lorsque la procédure stockée est en cours d'exécution, les pauses d'insertion ... de telle sorte qu'à la fin, aucun doublon n'a été ajouté au jeu de résultats final. Je me demande si LINQ to SQL ou SQL Server a un mécanisme par défaut qui empêche la simultanéité et met en pause l'insertion pendant que la sélection ou la mise à jour a lieu.

Qu'en pensez-vous?

EDIT 1:

Les 'doublons' ne sont pas des rangées identiques. Ils sont 'équivalents' étant donné les entités métier/logiques que ces enregistrements représentent. Chaque ligne a une clé primaire unique.

P.S. La sélection du jeu de résultats a lieu avec NOLOCK. Essayer de reproduire sur SQL Server 2008. Un problème est censé survenir sur SQL Server 2005.

Répondre

3

Qu'est-ce que je pense?

  • Pourquoi avez-vous des doublons dans la base de données? La pureté des données commence dans le client à la planche à dessin de l'application, qui devrait avoir un modèle de données qui ne permet tout simplement pas de doublons.
  • Pourquoi avez-vous des doublons dans la base de données? Les contraintes de vérification devraient empêcher que cela se produise si l'application cliente se comporte mal
  • Si vous avez des doublons, le lecteur doit être prêt à les manipuler.
  • Vous ne pouvez pas détecter les doublons en deux étapes (regardez puis marquez), il doit s'agir d'une seule marque atomique. En fait, vous ne pouvez presque rien faire dans une base de données en deux étapes 'look and mark'. Tous les processus de recherche d'enregistrement puis de marquage des enregistrements trouvés échouent sous la concurrence. NOLOCK vous donnera inconsistent reads
  • Les enregistrements seront manquants ou lus deux fois. Utilisez l'isolation SNAPSHOT.
  • Linq-To-SQL n'a pas de poussière pixie pour remplacer la mauvaise conception.

Mise à jour

par exemple Considérez ceci:

Une table de mise en scène avec une structure comme:

CREATE TABLE T1 (
    id INT NOT NULL IDENTITY(1,1) PRIMARY KEY, 
    date DATETIME NOT NULL DEFAULT GETDATE(), 
    data1 INT NULL, 
    data2 INT NULL, 
    data3 INT NULL); 

Process A est en train de faire des inserts à loisir dans cette table. Il doe snot faire une validation, il dumps simplement les enregistrements bruts dans:

INSERT INTO T1 (data1, data2, data3) VALUES (1,2,3); 
INSERT INTO T1 (data1, data2, data3) VALUES (2,1,4); 
INSERT INTO T1 (data1, data2, data3) VALUES (2,2,3); 
... 
INSERT INTO T1 (data1, data2, data3) VALUES (1,2,3); 
INSERT INTO T1 (data1, data2, data3) VALUES (2,2,3); 
... 
INSERT INTO T1 (data1, data2, data3) VALUES (2,1,4); 
... 

processus B est chargé d'extraire cette table de mise en scène et le déplacement des données nettoyées dans un T2 de table. Il doit supprimer les doublons qui, par règles métier, désignent les enregistrements ayant les mêmes valeurs dans data1, data2 et data3. Au sein d'un ensemble de doublons, seul le premier enregistrement par date doit être conservé:

set transaction isolation snapshot; 
declare @maxid int; 

begin transaction 
-- Snap the current max (ID) 
-- 
select @maxid = MAX(id) from T1; 

-- Extract the cleaned rows into T2 using ROW_NUMBER() to 
-- filter out duplicates 
-- 
with cte as (
SELECT date, data1, data2, datta3, 
    ROW_NUMBER() OVER 
     (PARTITION BY data1, data2, data3 ORDER BY date) as rn 
FROM T1 
WHERE id <= @maxid) 
MERGE INTO T2 
USING (
    SELECT date, data1, data2, data3 
    FROM cte 
    WHERE rn = 1 
) s ON s.data1 = T2.data1 
    AND s.data2 = T2.data2 
    AND s.data3 = T2.data3 
WHEN NOT MATCHED BY TARGET 
    THEN INSERT (date, data1, data2, data3) 
    VALUES (s.date, s.data1, s.data2, s.data3); 

-- Delete the processed row up to @maxid 
-- 
DELETE FROM T1 
    WHERE id <= @maxid; 
COMMIT; 

En supposant processus A insère uniquement, cette procédure traiterait en toute sécurité la table de mise en scène et extraire les doublons nettoyés. Bien sûr, il ne s'agit que d'un squelette, un vrai processus ETL aurait une gestion des erreurs via BEGIN TRY/BEGIN CATCH et le contrôle de la taille du journal des transactions via le batching.

+0

+1. Bien que, pour être juste, cela ressemble à une sorte de table de transfert, qui stocke les données brutes pour le nettoyage, qui plus tard déplace les données "propres" à la destination finale. Votre 4ème puce semble être le nœud du problème. –

+0

Les 'doublons' ne sont pas des enregistrements physiques en double. Ce sont des lignes distinctes avec une clé primaire unique. Nous considérons que certains documents sont «équivalents» (ce mot pourrait peut-être mieux vous satisfaire) et nous les traitons comme tels. – Rire1979

+0

La détection des doublons se produit dans une opération INSERT-SELECT. Marquer les enregistrements non dupliqués comme 'ToBeCopied' se produit comme une deuxième étape (comme décrit à l'origine). Le problème que la question tente de résoudre est pourquoi mon installation ** ne peut pas ** reproduire ce problème. Ce n'est pas que je m'attends à ce que Linq-to-SQL le répare magiquement. Je travaille sur une reproduction de bug. – Rire1979

0

Quand appelez-vous submit sur votre contexte de données? Je crois que cela se produit dans une transaction.

En ce qui concerne votre problème, ce que vous dites semble plausible - serait-il peut-être plus judicieux de vous faire charger dans une table de mise en scène (si elle est lente), puis faites un

SELECT * FROM StagingTable INTO ProductionTable 

une fois que votre charge est Achevée?