Эффективно вытащить тысячи записей с SQL Server? - PullRequest
0 голосов
/ 14 августа 2010

Это часть 2 к этому вопросу: Выбрать строки и обновить те же строки для блокировки?

В моей базе данных есть таблица, в которой хранятся миллионы записей. Я создал хранимую процедуру, чтобы извлечь количество записей X и пометить их как заблокированные, поэтому, когда мое приложение вызывает следующий набор значений X, оно будет извлекать только записи, которые не заблокированы и т. Д. Я обнаружил, что с миллионами записей это не очень эффективно. Запросы требуют времени для выполнения. У кого-нибудь есть предложения по повышению эффективности запросов при сохранении той же идеи для блокировки записей? Ниже приведена хранимая процедура:

Set Rowcount @topCount
 SELECT *
 into #temp_candidates
 FROM dbo.Candidates
 Where isTested = 0 and
       isLocked = 0 and 
       validated = 0 and
       validationId is null
 UPDATE dbo.Candidates
 SET islocked = 1,
 validationId = @validationId,
 validationDateTime = @validationDateTime
 WHERE id IN (SELECT id FROM #temp_candidates)
 Select *
 from dbo.Candidates
 where id in (SELECT id FROM #temp_candidates) IF EXISTS (SELECT NULL FROM
  tempdb.dbo.sysobjects WHERE ID = OBJECT_ID(N'tempdb..#temp_candidates')) 
   BEGIN 
     DROP TABLE #temp_candidates
   END 
END

Ответы [ 2 ]

2 голосов
/ 14 августа 2010

Этот вид процедуры гарантированно потерпит неудачу при параллельности. То, чего вы хотите достичь, - это использовать таблицу в качестве очереди, которая, как известно, подвержена ошибкам, и есть хорошо известные проверенные и опробованные способы использования Использование таблиц в качестве очередей . Прямо сейчас вы находитесь на пути, который никуда не ведет быстро. На ваш предыдущий вопрос вы должны были выбрать ответ Кейда.

Чтобы исключить элементы из таблицы (заблокировать их для обработки при наличии одновременных потоков, пытающихся сделать то же самое), необходимо выполнить несколько условий, все они описаны в статье, на которую я ссылался, и фактический запрос - только одно из них. , Самое важное условие - это наличие правильного кластеризованного индекса. Поле isLocked должно быть крайним левым полем в кластеризованном ключе, за которым должны следовать критерии упорядочения.

CREATE CLUSTERED INDEX cdxCandidates ON Candidates(isLocked, ...);

Этот кластеризованный индекс необходим для эффективных операций удаления из очереди. Тогда вы должны использовать предложение OUTPUT, это единственный способ вытащить это правильно:

WITH cte AS (
  SELECT TOP (@x) isLocked, ...
  FROM Candidates WITH (READPAST)
  WHERE isLocked = 0
  ORDER BY ...)
UPDATE cte
  SET isLocked = 1
  OUTPUT INSERTED.*;

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

Между прочим, эта «схема» является именно схемой (условие кластеризованного индекса и обновление с помощью OUTPUT поверх READPAST SELECT) используется в QUEUE компонента Service Broker, которые были специально разработаны для операций с высоким числом одновременных операций постановки / удаления. Были рассмотрены и другие альтернативы. Это единственный вариант, который имеет достойные шансы получить какую-либо производительность при сохранении корректности

1 голос
/ 14 августа 2010

Я не уверен, что понимаю необходимость временной базы данных.Почему бы просто не установить isLocked = 1 для любых записей, которые вы хотите (внутри транзакции).Делай что хочешь с ними.Затем установите isLocked = 1, где isLocked = 0 и повторите.

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