Должен ли я избегать использования sp_getAppLock? - программирование
Подтвердить что ты не робот

Должен ли я избегать использования sp_getAppLock?

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

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

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

Мой план состоял в том, чтобы заключить хранимую процедуру в транзакцию и настроить spGetAppLock с областью транзакций. Код был написан и протестирован успешно.

Код теперь предлагается для обзора, и мне сказали, что я не должен называть эту функцию. Однако, задавая очевидный вопрос "почему бы и нет?", Единственные причины, по которым я нахожусь, очень субъективны, чтобы осложнить любую форму блокировки.

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

ТИА, Пит

Дополнительная информация: Приложение находится поверх этого с 2 потоками T1 и T2. Каждый поток ожидает другого сообщения M1 и M2. В бизнес-логике говорится, что обработка может произойти только после того, как прибыли M1 и M2. Хранимая процедура регистрирует, что Mx прибыл (вставить), а затем проверяет, присутствует ли My (выберите). Встроенная блокировка в порядке, чтобы убедиться, что вставки происходят серийно. Но выбор также должен происходить серийно, и я думаю, что мне нужно сделать что-то сверх встроенной функции здесь.

просто для ясности, я хочу, чтобы "обработка" произошла ровно один раз. Поэтому я не могу позволить хранимой процедуре возвращать ложные срабатывания или ложные негативы. Я обеспокоен тем, что если хранимый proc работает дважды в очень быстрой последовательности, то оба "selects" могут возвращать данные, которые указывают, что это целесообразно для выполнения обработки.

4b9b3361

Ответ 1

Какова процедура, заключающаяся в том, что вы не можете полагаться на механизмы управления SQL-серверами concurrency? Часто запросы можно переписать, чтобы разрешить реальный concurrency.

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


Если таблица не содержит других строк, кроме M1 и M2, блокировка таблицы по-прежнему будет лучшим выбором.

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

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;  
BEGIN TRAN;
 SELECT 
 @hasM1 = MAX(CASE WHEN msg_type='M1' THEN 1 ELSE 0 END), 
 @hasM2 = MAX(CASE WHEN msg_type='M2' THEN 1 ELSE 0 END)
 FROM messages WITH(UPDLOCK)
 WHERE msg_type IN ('M1','M2')

 INSERT ...

 IF(??) EXEC do_other_stuff_and_delete_messages;
COMMIT

В инструкции IF перед (!) COMMIT вы можете использовать информацию, собранную перед вставкой, вместе с информацией, которую вы вставили, чтобы решить, нужна ли дополнительная обработка.

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

SERIALIZABLE - это единственный уровень изоляции транзакций, который позволяет блокировать строки, которые еще не существуют, поэтому первый оператор select с WITH (UPDLOCK) эффективно предотвращает вставку другой строки, пока первое выполнение все еще выполняется.

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

Сервисный брокер на msdn, также посмотрите на "активацию" для автоматической обработки сообщений.

Ответ 2

sp_GetAppLock аналогичен многим другим инструментам и, как таковой, может быть неправильно использован, чрезмерно использован или правильно использован. Это точное соответствие для типа проблемы, описанного в оригинальном плакате. Это хорошее сообщение MSSQL Tips об использовании Предотвращение одновременного запуска нескольких хранимых процедур SQL Server несколькими пользователями http://www.mssqltips.com/sqlservertip/3202/prevent-multiple-users-from-running-the-same-sql-server-stored-procedure-at-the-same-time/

Ответ 3

Мы используем sp_getapplock все время из-за того, что мы поддерживаем некоторые устаревшие приложения, которые были повторно обработаны для использования SQL-сервера, а модель блокировки SQL Server не является точным соответствием для нашего приложения логика.

Мы склонны придерживаться "пессимистической" модели блокировки, где мы блокируем объект, прежде чем разрешаем пользователю его редактировать, и широко используем подсказку (NOLOCK) при чтении данных, чтобы обойти любую блокировку от нативных блокировок на фактическом столы. sp_getapplock подходит для этого. Мы также используем его для обеспечения критических путей в больших многопользовательских системах. Вы должны быть систематически о том, что вы называете заблокированными вами замками.

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

Ответ 4

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

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

   SELECT ... FROM WITH(XLOCK,ROWLOCK,READCOMMITTED) ... WHERE ...

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

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

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

Эксклюзивный замок доказывает:

    USE master
    GO

    IF OBJECT_ID('dbo.tblTest') IS NOT NULL
        DROP TABLE dbo.tblTest

    CREATE TABLE tblTest ( id int PRIMARY KEY )

    ;WITH cteNumbers AS (
        SELECT 1 N
        UNION ALL
        SELECT N + 1 FROM cteNumbers WHERE N<1000
    )
    INSERT INTO
        tblTest
    SELECT
        N 
    FROM
        cteNumbers
    OPTION (MAXRECURSION 0)

    BEGIN TRANSACTION

    SELECT * FROM dbo.tblTest WITH(XLOCK,ROWLOCK,READCOMMITTED) WHERE id = 1

    SELECT * FROM sys.dm_tran_locks WHERE resource_database_id = DB_ID('master')

    ROLLBACK TRANSACTION