Предположим, я собираюсь запустить проект с помощью ASP.NET и SQL Server 2005. Я должен разработать требование параллелизма для этого приложения. Я планирую добавить столбец TimeStamp в каждой таблице. При обновлении таблиц я проверю, что столбец TimeStamp - то же, поскольку он был выбран.
Это приблизится быть, достаточны? Или есть ли какие-либо недостатки для этого подхода при каких-либо обстоятельствах?
Совет.
Спасибо
Lijo
Прежде всего, способ, который вы описали в своем вопросе, на мой взгляд, является лучшим способом для приложения ASP.NET с MS SQL в качестве базы данных. В базе данных нет блокировки. Он идеально подходит для постоянно отключенных клиентов, таких как веб-клиенты.
Как можно понять из некоторых ответов, существует недопонимание в терминологии. Мы все имеем в виду использование Microsoft SQL Server 2008 или выше для хранения базы данных. Если вы откроете в документации по MS SQL Server 2008 тему "rowversion (Transact-SQL)", то найдете следующее:
"timestamp является синонимом для... rowversion типа данных и подчиняется поведению синонима типа данных". ... "Синтаксис timestamp является устаревшим. Эта функция будет удалена в будущей версии Microsoft SQL Server. Избегайте использования этой функции в новых разработках, и планируйте модифицировать приложения, которые в настоящее время используют эту функцию."
Итак, тип данных timestamp является синонимом типа данных rowversion для MS SQL. Он содержит 64-битный счетчик, который существует внутри каждой базы данных и может быть представлен как @@DBTS. После модификации одной строки в одной таблице базы данных, счетчик будет увеличен.
Читая ваш вопрос, я прочитал "TimeStamp" как имя столбца типа rowversion данных. Мне лично больше нравится имя RowUpdateTimeStamp. В AzManDB (см. Microsoft Authorization Manager с Store as DB) я встречал такое название. Иногда использовались также ChildUpdateTimeStamp для отслеживания иерархических RowUpdateTimeStamp структур (в отношении триггеров).
Я реализовал этот подход в своем последнем проекте и был очень доволен. В общем, вы делаете следующее:
SELECT s.Id AS Id
,s.Name AS SoftwareName
,m.Name AS ManufacturerName
,CASE WHEN s.RowUpdateTimeStamp > m.RowUpdateTimeStamp
THEN s.RowUpdateTimeStamp
ELSE m.RowUpdateTimeStamp
END AS RowUpdateTimeStamp
FROM dbo.Software AS s
INNER JOIN dbo.Manufacturer AS m ON s.Manufacturer_Id=m.Id
Или сделать приведение данных, как показано ниже
SELECT s.Id AS Id
,s.Name AS SoftwareName
,m.Name AS ManufacturerName
,CASE WHEN s.RowUpdateTimeStamp > m.RowUpdateTimeStamp
THEN CAST(s.RowUpdateTimeStamp AS bigint)
ELSE CAST(m.RowUpdateTimeStamp AS bigint)
END AS RowUpdateTimeStamp
FROM dbo.Software AS s
INNER JOIN dbo.Manufacturer AS m ON s.Manufacturer_Id=m.Id
для хранения RowUpdateTimeStamp как bigint, что соответствует ulong типу данных C#. Если вы делаете OUTER JOINTs или JOINTs из многих таблиц, то конструкция MAX(RowUpdateTimeStamp)
из всех таблиц будет выглядеть немного сложнее. Поскольку MS SQL не поддерживает функции типа MAX(a,b,c,d,e), соответствующая конструкция может выглядеть следующим образом:
(SELECT MAX(rv)
FROM (SELECT table1.RowUpdateTimeStamp AS rv
UNION ALL SELECT table2.RowUpdateTimeStamp
UNION ALL SELECT table3.RowUpdateTimeStamp
UNION ALL SELECT table4.RowUpdateTimeStamp
UNION ALL SELECT table5.RowUpdateTimeStamp) AS maxrv) AS RowUpdateTimeStamp
spSoftwareUpdate
может выглядеть такCREATE PROCEDURE dbo.spSoftwareUpdate
@Id int,
@SoftwareName varchar(100),
@originalRowUpdateTimeStamp bigint, -- used for optimistic concurrency mechanism
@NewRowUpdateTimeStamp bigint OUTPUT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
-- ExecuteNonQuery() returns -1, but it is not an error
-- one should test @NewRowUpdateTimeStamp for DBNull
SET NOCOUNT ON;
UPDATE dbo.Software
SET Name = @SoftwareName
WHERE Id = @Id AND RowUpdateTimeStamp <= @originalRowUpdateTimeStamp
SET @NewRowUpdateTimeStamp = (SELECT RowUpdateTimeStamp
FROM dbo.Software
WHERE (@@ROWCOUNT > 0) AND (Id = @Id));
END
Код хранимой процедуры dbo.spSoftwareDelete
выглядит так же. Если не включить NOCOUNT
, то во многих сценариях можно получить DBConcurrencyException, сгенерированный автоматически. Visual Studio предоставляет вам возможности для использования оптимистичного параллелизма, например, флажок "Use optimistic concurrency" в Advanced Options TableAdapter
или DataAdapter
.
Если вы посмотрите на dbo.spSoftwareUpdate
хранимую процедуру, то увидите, что я использую RowUpdateTimeStamp <= @originalRowUpdateTimeStamp
в WHERE вместо RowUpdateTimeStamp = @originalRowUpdateTimeStamp
. Я делаю это потому, что, значение @originalRowUpdateTimeStamp
, которое имеет клиент, обычно строится как MAX(RowUpdateTimeStamp)
из нескольких таблиц. Поэтому может оказаться, что RowUpdateTimeStamp < @originalRowUpdateTimeStamp
. Либо вы должны использовать строгое равенство = и воспроизвести здесь такой же сложный оператор JOIN, какой вы использовали в операторе SELECT, либо использовать конструкцию <=, как я, и остаться в точности таким же безопасным, как и раньше.
Кстати, можно построить очень хорошее значение для ETag на основе RowUpdateTimeStamp, которое можно отправить в HTTP заголовке клиенту вместе с данными. С помощью ETag можно реализовать интеллектуальное кэширование данных на стороне клиента.
Я не могу написать здесь весь код, но вы можете найти много примеров в Интернете. Хочу лишь еще раз повторить, что, на мой взгляд, использование оптимистичного параллелизма на основе rowversion является лучшим способом для большинства сценариев ASP.NET.
Предложение версии строки верное, я бы сказал, но его разочаровывает то, что временная метка скоро станет устаревшей. Некоторые из моих СТАРЫХ приложений используют это по разным причинам, а не для проверки параллелизма.
Я не уверен, что параллелизм следует обрабатывать в базе данных таким образом. Сама база данных должна иметь возможность управлять изоляцией и поведением транзакций, но поведение потоковой передачи должно выполняться в коде.
В SQL Server в зависимости от типа ситуации рекомендуется создать столбец типа 'rowversion' и использовать его, чтобы проверить, любое из полей в этой строке изменилось.
SQL Server гарантирует, что если какое-либо значение в строке изменится (или будет вставлена новая строка), его столбец rowversion будет автоматически обновлен до другого значения. Позволить базе данных сделать это за вас гораздо надежнее, чем пытаться сделать это самостоятельно.
В операторах обновления вам просто нужно добавить предложение where, чтобы проверить, что значение rowversion такое же, как и при первом получении строки. Если нет, значит, кто-то другой изменил строку (например, она грязная)
Кроме того, с этой страницы:
Синтаксис метки времени устарел. Эта функция будет удалена в будущая версия Microsoft SQL Server. Избегайте использования этой функции в новых разработках и запланируйте изменение приложений, которые в настоящее время используют эту функцию.