Параллелизм - один процесс обновляет, другой вставляет - PullRequest
0 голосов
/ 09 ноября 2010

У меня есть два процесса, которые работают с данными в одной таблице.

Один процесс вставляет ежедневно, один за другим (чистый ADO.NET), около 20000 записей в целевой таблице.

Второй процесс вызывает (периодически, каждые 15 минут) хранимую процедуру, которая

  1. Обнаруживает дубликаты в этих 20000 записях, просматривая все записи 7 дней назад и помечая их как таковые.
  2. Помечает все записи, которые не являются дубликатами, с помощью флага «ToBeCopied».
  3. Выберите количество столбцов из записей, помеченных как «ToBeCopied», и возвращает набор.

Иногда эти два процесса перекрываются (из-за задержек в обработке данных), и я подозреваю, что если первый процесс вставляет новые записи, когда второй процесс находится где-то между 1 и 2, тогда записи будут помечены как «ToBeCopied», не пройдя повторное просеивание.

Это означает, что теперь процедура хранения возвращает несколько дубликатов.

Это моя теория, но вНа практике мне не удалось его воспроизвести ...

Я использую LINQ to SQL для вставки дубликатов (40-50 или около того в секунду), и пока он работает, я вручную вызываю хранимую процедуру и сохраняюего результаты.

Похоже, что когда хранимая процедура выполняет вставку, паузы ... такие, что в конце не было дубликатов до окончательного набора результатов.

Интересно,LINQ to SQL или SQL Server имеет механизм по умолчанию, который предотвращает параллелизм и приостанавливает вставку во время выбора или обновления.

Как вы думаете?

РЕДАКТИРОВАТЬ 1:

«Дубликаты» не являются одинаковыми строками.Они «эквивалентны», учитывая бизнес / логические объекты, которые представляют эти записи.Каждая строка имеет уникальный первичный ключ.

PS Выбор набора результатов осуществляется с помощью NOLOCK.Попытка воспроизвести на SQL Server 2008. Предполагается, что проблема возникает на SQL Server 2005.

Ответы [ 2 ]

3 голосов
/ 09 ноября 2010

Что я думаю?

  • Почему у вас есть дубликаты в базе данных? Чистота данных начинается с клиента на чертежной доске приложения, которая должна иметь модель данных, которая просто не допускает дублирования.
  • Почему у вас есть дубликаты в базе данных? Проверка ограничений должна предотвратить это, если клиентское приложение плохо себя ведет
  • Если у вас есть дубликаты, читатель должен быть готов к их обработке.
  • Вы не можете обнаружить дубликаты в два этапа (посмотрите, затем отметьте), это должна быть одна атомная метка. На самом деле, вы ничего не можете сделать в базе данных в два этапа «посмотреть и пометить». Все процессы «ищите записи, а затем отмечайте найденные записи» завершаются неудачно в параллельном режиме.
  • NOLOCK выдаст вам непоследовательные чтения . Записи будут отсутствовать или читать дважды. Используйте 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;

При условии, что Process A только вставляет, эта процедура будет безопасно обрабатывать промежуточную таблицу и извлекать очищенные дубликаты. Конечно, это всего лишь скелет, настоящий процесс ETL будет иметь обработку ошибок с помощью BEGIN TRY / BEGIN CATCH и управление размером журнала транзакций с помощью пакетной обработки.

0 голосов
/ 09 ноября 2010

Когда вы звоните, отправьте на ваш контекст данных?Я полагаю, что это происходит в транзакции.

Что касается вашей проблемы, то, что вы говорите, звучит правдоподобно - возможно, было бы более целесообразно загрузить в промежуточную таблицу (если она медленная), а затем выполнить

SELECT * FROM StagingTable INTO ProductionTable 

после завершения загрузки?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...