Тривиальная задачка

lumf

есть счета пользователей(записи в таблице).есть много потоков которые хотят изменить одну и ту же запись таким образом:в хп передается сколько денег необходимо снять со счета,1. проверяется есть необходимая сумма, если есть то пункт 2.2. необходимо уменьшить количество средств на счете пользователя.начитался всях там блокировок, но как применить не совсем понимаю.пердполагаю что первый селект необходимо делать с хинтом updlockподскажите как максимально быстродейственно и чтобы делоков не было
7 ответов

lumf

Можно даже одним действием
UPDATE t1 set summa = summa - @summa 
WHERE user_id = @user_id and summa >= @summa
И никаких блокировок и пр.


lumf

подскажите как максимально быстродейственно и чтобы делоков не было
Хинт updlock используется для того чтобы вместо shared lock наложить exclusive lock на таблицу во время чтения, что поможет избежать deadlock. Выдержка из BOL
Use update locks instead of shared locks while reading a table, and hold locks until the end of the statement or transaction. UPDLOCK has the advantage of allowing you to read data (without blocking other readers) and update it later with the assurance that the data has not changed since you last read it.


lumf

Хинт updlock используется для того чтобы вместо shared lock наложить exclusive lock на таблицу во время чтения, что поможет избежать deadlock. Выдержка из BOL
и все же тогда получается вот такой вариант вообще оптимален?
UPDATE t1 set summa = summa - @summa 
WHERE user_id = @user_id and summa >= @summa
возможны ли здесь дедлоки?


lumf

и все же тогда получается вот такой вариант вообще оптимален?возможны ли здесь дедлоки?
Безусловно оптимален :-) Ведь все делается одним запросом, блокировка будет наложена, скорее всего, только на одну строку. Если у Вас будет еще и кластерный индекс по user_id, то он вообще будет выполняться едва ли не мгновенно.Дедлоки вряд ли возникнут. Какой-то другой процесс может подождать (если захочет наложить блокировку на страницу или таблицу в целом), пока данный запрос не отпустит блокировку строки.


lumf

классический код, полагаю, таков:
begin tran

select @rest = rest
from accounts with (xlock)
where id = @accountid

if @@rowcount <> <b>1</b> goto err_account_find

/* проверки всякие по счету */

update accounts
set rest = @rest
where id = @id

if @@error <> <b>0</b> goto exit_error

commit tran
подразумевается что id в accounts pk а то и pkc. "проверки всякие" - имеется в виду что проверки могут быть сложнее, чем просто сумму на >= 0. если же проверка только сумма >= 0 то вполне хватит и варианта от Breakneck. только там у вас будет одно исключение на две ситуации - когда денег нехватает и когда счета нет. это надо будет обработать отдельно


lumf

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


lumf

только обычно операции идут не с одним, а как минимум с двумя счетамив одной транзациии дейдлоки могут возникнуть
чтобы они не возникли, был создан хинт updlock, вместо исп по умолчанию в read commited shared lock