Переведите следующую логику синхронизации C # в хранимую процедуру - PullRequest
1 голос
/ 06 марта 2012

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

Думайте о странице как о некотором родежесткого диска, который имеет 4 столбца

Pages
   PageID
   SpaceAvailable
   SpaceOccupied
   TotalSpace

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

// a static lock to prevent race condition
static object Locker = new Object(); 

long AllocateNewPage(MyContext context, int requestedSize){
   long pageID = 0;

   // what is T-SQL lock equaivalent?
   lock(Locker){
      using(TransactionScope scope = new TransactionScope()){
         var page = context.Pages
                        .Where(x=>x.SpaceAvailable>requestedSize)
                        .OrderBy(x=>x.PageID)
                        .First();
         page.SpaceOccupied = page.SpaceOccupied + requestedSize;
         page.SpaceAvailable = page.SpaceAvailable - requestedSize;
         context.SaveChanges();
         scope.Commit();
         pageID = page.PageID;
      }
   }
   return pageID;
}

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

CREATE procedure [GetPageID]
(
    @SpaceRequested int
)
AS
BEGIN

    DECLARE @DBID int
    DECLARE @lock int
    DECLARE @LockName varchar(20)

    SET @LockName = 'PageLock'

    BEGIN TRANSACTION

        -- acquire a lock
        EXEC @lock = sp_getapplock 
                            @Resource = @LockName, 
                            @LockMode = 'Exclusive', 
                            @LockTimeout = 5000

        IF @lock<>0 BEGIN
            ROLLBACK TRANSACTION
            SET @DBID = -1
            SELECT @DBID
            return 0
        END
        SET @DBID = coalesce((SELECT TOP 1 PageID 
                                  FROM Pages 
                                  WHERE SpaceAvailable > @SpaceRequested 
                                  ORDER BY PageID ASC ),0)
        UPDATE Pages SET 
            SpaceAvailable = SpaceAvailable - @SpaceRequested,
            SpaceOccupied = SpaceOccupied + @SpaceRequested
        WHERE PageID = @DBID

        EXEC @lock = sp_releaseapplock @Resource = @LockName

    COMMIT TRANSACTION

    SELECT @DBID
END

Я не очень разбираюсь в хранимых процедурах, но мне нужно размещать страницы в заблокированном режиме, чтобы ни одна страница не была переполнена.

Я ДУМАЮ? Даже если я работаю в транзакции, мне все еще нужна блокировка?

1 Ответ

2 голосов
/ 06 марта 2012

Да, вы слишком много думаете.Позвольте SQL Server управлять блокировками.

create procedure [GetPageID]
  @SpaceRequested int
as
begin 
  set nocount on;

  begin tran;

  update top (1) pages
  set
    SpaceAvailable -= @SpaceRequested,
    SpaceOccupied += @SpaceRequested
  output
    inserted.PageID
  where
    SpaceAvailable > @SpaceRequested
  order by PageID asc;

  commit tran;

end;

Вышеприведенное также может быть написано в два этапа, которые вы показываете в своем вопросе (в случае, если вы предпочитаете это или ваша версия SQL Server соответствуетдостаточно взрослый):

create procedure [GetPageID]
  @SpaceRequested int
as
begin 
  set nocount on;

  begin tran;

  declare @page_id int;

  select top (1) @page_id = PageID
  from pages with (updlock, rowlock)
  where SpaceAvailable > @SpaceRequested
  order by PageID asc;

  update Pages
  set
    SpaceAvailable = SpaceAvailable - @SpaceRequested,
    SpaceOccupied = SpaceOccupied + @SpaceRequested
  where
    PageID = @page_id;

  commit tran;

  select @page_id;

end;
...