Каков наилучший способ обновления данных в таблице, когда она используется, без блокировки таблицы? - PullRequest
5 голосов
/ 20 мая 2009

У меня есть таблица в базе данных SQL Server 2005, которая используется часто. Он имеет наш продукт в наличии информацию о наличии. Мы получаем обновления каждый час с нашего склада, и в течение последних нескольких лет мы выполняли процедуру, которая усекает таблицу и обновляет информацию. Это занимает всего несколько секунд и до сих пор не было проблемой. Сейчас у нас гораздо больше людей, использующих наши системы, которые запрашивают эту информацию, и в результате мы видим много тайм-аутов из-за блокирования процессов.

... так ...

Мы исследовали наши варианты и пришли к идее смягчить проблему.

  1. У нас будет две таблицы. Таблица A (активная) и таблица B (неактивная).
  2. Мы бы создали представление, которое указывает на активную таблицу (таблица A).
  3. Все вещи, нуждающиеся в этой табличной информации (4 объекта), теперь должны были пройти через представление.
  4. Почасовая подпрограмма усекает неактивную таблицу, обновляет ее самой последней информацией, а затем обновляет представление, указывая на неактивную таблицу, делая ее активной.
  5. Эта подпрограмма будет определять, какая таблица активна, и в основном переключать представление между ними.

Что с этим не так? Будет ли переключение просмотра в середине запроса вызвать проблемы? Может ли это работать?

Спасибо за ваш опыт.

Дополнительная информация

  • подпрограмма представляет собой пакет служб SSIS, который выполняет множество шагов и в конечном итоге усекает / обновляет соответствующую таблицу

  • Процессы блокировки - это две другие хранимые процедуры, которые запрашивают эту таблицу.

Ответы [ 10 ]

6 голосов
/ 20 мая 2009

Рассматривали ли вы использование изоляции моментального снимка . Это позволило бы вам начать большую жирную транзакцию для ваших вещей в SSIS и все еще читать из таблицы.

Это решение кажется намного чище, чем переключение таблиц.

2 голосов
/ 20 мая 2009

Лично, если вы всегда будете вводить время простоя, чтобы запустить пакетный процесс для таблицы, я думаю, вам следует управлять пользовательским интерфейсом на уровне доступа к бизнесу / данным. Представьте объект управления таблицей, который отслеживает соединения с этой таблицей и контролирует пакетную обработку.

Когда новые данные пакета готовы, объект управления останавливает все новые запросы (возможно, даже ставит в очередь?), Позволяет завершить существующие запросы, запускает пакет, а затем снова открывает таблицу для запросов. Объект управления может вызвать событие (BatchProcessingEvent), которое может интерпретировать уровень пользовательского интерфейса, чтобы дать людям понять, что таблица в данный момент недоступна.

Мои $ 0,02,

Nate

2 голосов
/ 20 мая 2009

Просто прочитайте, что вы используете SSIS

вы можете использовать компонент TableDiference из: http://www.sqlbi.eu/Home/tabid/36/ctl/Details/mid/374/ItemID/0/Default.aspx

alt text
(источник: sqlbi.eu )

Таким образом, вы можете применить изменения к таблице, ОДИН за ОДИН, но, конечно, это будет намного медленнее и в зависимости от размера таблицы потребует больше оперативной памяти на сервере, но проблема блокировки будет полностью исправлена.

2 голосов
/ 20 мая 2009

Я думаю, что это происходит неправильно - обновление таблицы должно блокировать ее, хотя вы можете ограничить эту блокировку для каждой страницы или даже для строки.

Я бы посмотрел на то, чтобы не обрезать таблицу и не заполнить ее. Это всегда будет мешать пользователям, пытающимся его прочитать.

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

Попробуйте добавить подсказку with (nolock) в оператор чтения SQL View. Вы должны иметь возможность получать очень большие объемы чтения пользователями даже при регулярном обновлении таблицы.

1 голос
/ 20 мая 2009

Если в вашем распоряжении имеется SQL Server Enterprise Edition, тогда я могу предложить вам использовать технологию создания разделов SQL Server.

Возможно, ваши текущие требуемые данные находятся в разделе «Live», а обновленная версия данных - в разделе «Secondary» (который недоступен для запросов, а скорее для администрирования данных).

После того, как данные были импортированы в раздел «Secondary», вы можете мгновенно ВЫКЛЮЧИТЬ раздел «LIVE» OUT и раздел «Secondary» IN, что приведет к нулевому простою и отсутствию блокировки.

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

Каждый раз, когда вам нужно выполнить импорт, вы просто повторяете / отменяете процесс.

Чтобы узнать больше о секционировании SQL Server, смотрите:

http://msdn.microsoft.com/en-us/library/ms345146(SQL.90).aspx

Или вы можете просто спросить меня: -)

EDIT:

В дополнение к этому, для решения любых проблем с блокировкой можно использовать технологию управления версиями строк в SQL Server.

http://msdn.microsoft.com/en-us/library/ms345124(SQL.90).aspx

1 голос
/ 20 мая 2009

Одним из возможных решений будет минимизация времени, необходимого для обновления таблицы.

Сначала я бы создал промежуточную таблицу для загрузки данных из хранилища.

Если вам нужно сделать «вставки, обновления и удаления» в финальной таблице

Предположим, что финальный стол выглядит следующим образом:

Table Products:
    ProductId       int
    QuantityOnHand  Int

А вам нужно обновить Кол-во на складе со склада.

Сначала создайте промежуточную таблицу, например:

Table Prodcuts_WareHouse
    ProductId       int
    QuantityOnHand  Int

А затем создайте таблицу «Действия» следующим образом:

Table Prodcuts_Actions
    ProductId       int
    QuantityOnHand  Int
    Action          Char(1)

Процесс обновления должен быть примерно таким:

1.Обрезная таблица Prodcuts_WareHouse

2.Обрезать таблицу Prodcuts_Actions

3. Заполните таблицу Prodcuts_WareHouse данными из хранилища

4. Заполните таблицу Prodcuts_Actions следующим образом:

Вставки:

INSERT INTO Prodcuts_Actions (ProductId, QuantityOnHand,Action)
SELECT     SRC.ProductId, SRC.QuantityOnHand, 'I' AS ACTION
FROM         Prodcuts_WareHouse AS SRC LEFT OUTER JOIN
                      Products AS DEST ON SRC.ProductId = DEST.ProductId
WHERE     (DEST.ProductId IS NULL)

Удаление

INSERT INTO Prodcuts_Actions (ProductId, QuantityOnHand,Action)
SELECT     DEST.ProductId, DEST.QuantityOnHand, 'D' AS Action
FROM         Prodcuts_WareHouse AS SRC RIGHT OUTER JOIN
                      Products AS DEST ON SRC.ProductId = DEST.ProductId
WHERE     (SRC.ProductId IS NULL)

Обновление

INSERT INTO Prodcuts_Actions (ProductId, QuantityOnHand,Action)
SELECT     SRC.ProductId, SRC.QuantityOnHand, 'U' AS Action
FROM         Prodcuts_WareHouse AS SRC INNER JOIN
                      Products AS DEST ON SRC.ProductId = DEST.ProductId AND SRC.QuantityOnHand <> DEST.QuantityOnHand

До сих пор вы не заблокировали финальный стол.

5. В транзакции обновите финальную таблицу:

BEGIN TRANS

DELETE Products FROM Products INNER JOIN
Prodcuts_Actions ON Products.ProductId = Prodcuts_Actions.ProductId
WHERE     (Prodcuts_Actions.Action = 'D')

INSERT INTO Prodcuts (ProductId, QuantityOnHand)
SELECT ProductId, QuantityOnHand FROM Prodcuts_Actions WHERE Action ='I';

UPDATE Products SET QuantityOnHand = SRC.QuantityOnHand 
FROM         Products INNER JOIN
Prodcuts_Actions AS SRC ON Products.ProductId = SRC.ProductId
WHERE     (SRC.Action = 'U')

COMMIT TRAN

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

Вы даже можете не использовать транзакцию на последнем шаге, поэтому между командами будет освобождена таблица.

1 голос
/ 20 мая 2009

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

Усечение не зарегистрировано, поэтому его нельзя выполнить в транзакции.

Если вы выполняете операцию в транзакции, существующие пользователи не будут затронуты.

Как это сделать, будет зависеть от таких вещей, как размер таблицы и насколько радикально изменятся данные. Если вы дадите больше подробностей, возможно, я мог бы посоветовать дальше.

0 голосов
/ 20 мая 2009

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

Надеюсь, это поможет,

Bill

0 голосов
/ 20 мая 2009

Если таблица не очень большая, вы можете на короткое время кэшировать данные в вашем приложении. Это не может полностью исключить блокировку, но уменьшит шансы на то, что таблица будет запрошена при обновлении.

0 голосов
/ 20 мая 2009

Мы делаем это на наших системах с высокой нагрузкой, и у нас не было никаких проблем. Однако, как и в случае со всей базой данных, единственный способ убедиться, что это поможет, - это внести изменения в dev и затем загрузить его. Не зная, что еще делает ваш пакет служб SSIS, он все равно может вызвать блокировку.

...