2010-11-09 84 views
0

我有两个进程使用同一个表中的数据。并发 - 一个进程更新,另一个插入

一种方法每日插入,一个接一个地(纯ADO.NET),约20000记录的目标表。

第二处理呼叫(周期性地,每15分钟)的存储过程

  1. 通过在所有记录找7天回检测那些20000次记录的重复和它们标记为这样。
  2. 标记不带有'ToBeCopied'标志的重复记录。
  3. 从标记为“ToBeCopied”的记录中选择多个列并返回该集合。

有时这两个过程重叠(由于在数据处理延迟),我怀疑的是,如果第一处理插入新的记录时,第二工艺是介于1和2之间,然后记录将不具有被标记为“ToBeCopied”经历了重复筛选。

这意味着现在存储过程正在返回一些重复项。

这是我的理论,但在实践中我一直没能复制它...

我使用LINQ to SQL插入重复(40-50左右秒),虽然这是我跑我手动调用存储过程并存储其结果。

似乎当所存储的程序运行的插入暂停...,使得在端部没有重复已经做了它对最终结果集。

我想知道是否的LINQ to SQL或SQL Server具有防止并发和在暂停插入而选择或更新发生默认的机制。

您认为如何?

编辑1:

'重复'不是相同的行。考虑到这些记录所代表的商业/逻辑实体,它们是“等价的”。每一行都有一个唯一的主键。

P.S.使用NOLOCK选择结果集。试图重现的SQL Server 2008上的问题是所谓的当你叫提交您的数据上下文SQL Server 2005的

回答

3

我觉得呢?

  • 为什么你在数据库中有重复?数据纯度从应用程序绘图板的客户端开始,应该有一个数据模型,它不允许重复。
  • 为什么你在数据库中有重复?如果客户端应用程序行为异常,检查约束应防止发生这种情况
  • 如果您有重复项,读者必须准备好处理它们。
  • 你不能在两个阶段检测重复(看起来然后标记),它必须是一个单一的原子标记。事实上,你无法在两个阶段的“外观和标记”中做任何事情。所有'寻找记录,然后标记找到的记录'进程在并发下失败。
  • NOLOCK会给你inconsistent reads。记录将丢失或读取两次。使用SNAPSHOT隔离。
  • Linq-To-SQL没有小精灵来代替坏的设计。

更新

考虑此例如:

与像的结构的临时表:

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); 

过程A闲时做插入到该表中。它母鹿鼻涕做任何验证,它只是转储原记录:

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); 
... 

过程B与提取该临时表和清理数据移动到一个表T2任务。它必须删除按业务规则重复的重复项,意味着data1,data2和data3中具有相同值的记录。内的一组重复的,仅由date第一记录应保持:

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; 

假设过程A只是插入,此过程会安全地处理该临时表,并提取清洗重复。当然,这只是一个框架,一个真正的ETL过程将通过BEGIN TRY/BEGIN CATCH进行错误处理,并通过批处理控制事务日志大小。

+0

+1。尽管公平起见,这听起来像是某种临时表,它存储了用于清理的原始数据,后者将“干净”数据移动到最终目的地。你的第四个问题听起来像是问题的关键。 – 2010-11-09 16:38:17

+0

'重复'不是重复的物理记录。它们是具有唯一主键的不同行。我们正在考虑某些记录是“等同的”(也许这个词会更好地满足你),我们正在对待它们。 – Rire1979 2010-11-09 18:54:51

+0

重复的检测发生在一个INSERT - SELECT操作中。将非重复记录标记为“ToBeCopied”作为第二步(如最初所述)。该问题试图解决的问题是为什么我的设置**不能**重现此问题。并不是我期待Linq-to-SQL能够魔法修复它。我正在研究错误复制。 – Rire1979 2010-11-09 19:06:03

0

上发生?我相信这发生在一个交易中。

至于你的问题,你说的话的声音似是而非 - 将它也许更有意义,你加载到一个临时表(如果它的速度慢),然后做一个

SELECT * FROM StagingTable INTO ProductionTable 

一旦你的负荷完成?

相关问题