SQL saklı yordamını aynı anda bir kişi tarafından çalıştırılmak üzere nasıl sınırlayabilirim?


12

Temelde bir tablodan değerleri seçer ve başka bir tür arşivleme ekler saklı yordam var. Aynı anda birden fazla insanın bunu yapmasını önlemek istiyorum.

Bu prosedür çalışırken, başka kimsenin başlamasını istemiyorum, ancak serileştirme istemiyorum, diğer kişi işlemi bitirdikten sonra prosedürü çalıştıracak.

Ben prosedür çalışırken ben hata almaya başlamak için çalışan diğer kişi için ne istiyorum.

Ben sp_getapplock kullanarak denedim, ancak tamamen prosedürü çalıştırmak kişinin durduramazsınız.

Ayrıca sys.dm_exec_requests ile prosedür bulma ve prosedürü engelleme çalıştı, bu işe yarar, ben bazı sunucularda sys.dm_exec_sql_text (sql_handle) çalıştırmak için izinleri yok çünkü bu optimal olmadığını düşünüyorum.

Bunu yapmanın en iyi yolu nedir?


3
Bir adım geri gidebilir ve prosedürün ne yaptığına ve neden aynı anda birden fazla kişinin çalışmasını önlemek istediğinize dair daha fazla bilgi verebilir misiniz? Bu gereksinimi ortadan kaldıran bir kodlama tekniği veya bir şeyleri işlemek için uygulayabileceğiniz bir tür kuyruklama olabilir.
AMtwo

Yanıtlar:


15

@ Tibor-Karaszi'nin cevabına eklemek için kilit zaman aşımı ayarlamak aslında bir hata oluşturmaz (dokümanlara karşı bir PR gönderdim). sp_getapplock sadece -1 döndürür, bu nedenle dönüş değerini kontrol etmeniz gerekir. Şöyle böyle:

create or alter procedure there_can_be_only_one 
as
begin
begin transaction

  declare @rv int
  exec @rv = sp_getapplock 'only_one','exclusive','Transaction',0
  if @rv < 0
   begin
      throw 50001, 'There is already an instance of this procedure running.', 10
   end

  --do stuff
  waitfor delay '00:00:20'


commit transaction
end

8

Proc başlangıcında sp_getapplock kullanın ve bir kilit zaman aşımı süresini çok düşük bir değere ayarlayın. Bu şekilde engellendiğinizde hata alırsınız.


7

Başka bir seçenek, prosedüre erişimi kontrol etmek için bir tablo oluşturmaktır. aşağıdaki örnek, olası bir tabloyu ve bu tabloyu kullanabilen bir prosedürü göstermektedir.

CREATE TABLE dbo.ProcedureLock
    (
    ProcedureLockID INT NOT NULL IDENTITY(1,1)
    , ProcedureName SYSNAME NOT NULL
    , IsLocked BIT NOT NULL CONSTRAINT DF_ProcedureLock_IsLocked DEFAULT (0)
    , UserSPID INT NULL
    , DateLockTaken DATETIME2(7) NULL
    , DateLockExpires DATETIME2(7) NULL
    , CONSTRAINT PK_ProcedureLock PRIMARY KEY CLUSTERED (ProcedureLockID)
    )

CREATE UNIQUE NONCLUSTERED INDEX IDXUQ_ProcedureLock_ProcedureName
    ON dbo.ProcedureLock (ProcedureName)

INSERT INTO dbo.ProcedureLock
    (ProcedureName, IsLocked)
VALUES ('dbo.DoSomeWork', 0)

GO

CREATE PROCEDURE dbo.DoSomeWork
AS
BEGIN

    /** Take Lock */
    UPDATE dbo.ProcedureLock
    SET IsLocked = 1
        , UserSPID = @@SPID
        , DateLockTaken = SYSDATETIME()
        , DateLockExpires = DATEADD(MINUTE, 10, SYSDATETIME())
    WHERE ProcedureName = 'dbo.DoSomeWork'
        AND (IsLocked = 0
            OR (IsLocked = 1 AND DateLockExpires < SYSDATETIME())
            )

    IF COALESCE(@@ROWCOUNT, 0) = 0
    BEGIN
        ;THROW 50000, 'This procedure can only be run one at a time, please wait', 1;
    END

    /** DO WHATEVER NEEDS TO BE DONE */

    /** Release the lock */
    UPDATE dbo.ProcedureLock
    SET IsLocked = 0
        , UserSPID = NULL
        , DateLockTaken = NULL
        , DateLockExpires = NULL
    WHERE ProcedureName = 'dbo.DoSomeWork'

END

1
Bu, soruyu okuduktan hemen sonra düşündüğümle çok benzer (veya belki de aynı), ancak bu fikre, nasıl ele alınacağından tam olarak emin olmadığım ve cevabınızda ele alındığını göremediğim bir sorun yaşadım. ya. Benim kaygılarım, "yapılması gerekeni yap" bölümünde bir şey olursa ne olur? IsLockedBu durumda durumu nasıl 0'a sıfırlarsınız ? Ayrıca COALESCEburada kullanımınızı merak ediyorum . @@ROWCOUNTGibi ifadelerden sonra null olabilir UPDATEmi? Son olarak, sadece küçük bir nitpick, neden THROWbu özel durumda ifadenin önüne noktalı virgül koymalıyım ?
Andriy M

Kilit süresinin dolması bununla baş etmenin bir yoludur. Makul bir zaman dilimine ayarlanması gerekir, örneğimde 10 dakikaya ayarladım. Çalışma mantığınızı bir deneme / yakalama bloğunda kapsülleyebilir ve isterseniz kilit içinde de kilidini açabilirsiniz. COALESCE işlevini alışkanlık dışında kullanıyorum, ancak @@ ROWCOUNT değeri NULL olamaz. önde gelen noktalı virgül Visual Studio veritabanı projeleri ile çalışmaktan geliyor, orada değilse şikayet ediyor. hiçbir şekilde zarar vermez.
Jonathan Fite

-1

Sorunu yanlış bir şekilde çözmeye çalıştığınızı düşünüyorum. İstediğiniz veritabanı tutarlılığını en üst düzeyde korumaktır. İki kişi aynı anda bir saklı yordam çalıştırırsa, veritabanı tutarlılığı ihlal edilebilir.

Çeşitli veritabanı tutarsızlıklarına karşı koruma sağlamak için SQL standardının dört işlem yalıtım düzeyi vardır:

  • Temelde işlemlerin değerlerini kaybettiği, diğer işlemlerin kirli veri gördüğü RAKAMI OKUYUN . Bunu kullanma!
  • OKU İŞLENEN işlemler sadece taahhüt verileri görmek nerede, ancak iki işlemler birbirlerinin ayak üzerinde adım atabilirsiniz tutarsızlıklar olabilir
  • Bir tür tutarsızlığın, tekrarlanamayan okumanın çözüldüğü TEKRARLANABİLİR OKUYUN
  • İşlemlerin yürütülmesinin, sonuçlarının yürütülmesinin sonuçlandığı sonuçlara yol açacağı bazı sanal düzenlerin varlığını garanti eden SERIALIZABLE

Bununla birlikte, SQL standardı bu veritabanı tutarsızlıkları için kilit tabanlı bir yaklaşıma sahiptir ve performans nedenlerinden dolayı birçok veritabanı temel olarak şu düzeylere sahip bir anlık görüntü yalıtımı tabanlı yaklaşım alır:

  • Kilit tabanlı veritabanlarında olduğu gibi OKULULDUĞU OKUYUN
  • SNAPSHOT ISOLATION burada veritabanı tüm verilerin anlık görüntüsünü görür ve başka bir işlemle güncellenmiş bir satırı güncellemeye çalışırsa iptal edilir, ancak gerçekleşebilecek bazı iyi bilinen anormallikler vardır
  • Kilitleme tabanlı veritabanlarında olduğu gibi SERİLEŞTİRİLEBİLİR , ancak bu kez kilit alarak değil, serileştirme ihlali olmadığından ve böyle bir ihlal tespit edildiğinde bir işlemi iptal ederek farklı bir şekilde uygulanır.

Bu anlık görüntü yalıtımı tabanlı veritabanlarındaki işlem iptalleri endişe verici gelebilir, ancak daha sonra her bir veritabanı bir kilitlenme nedeniyle bir işlemi iptal edecektir, bu nedenle herhangi bir makul uygulamanın bir işlemi yeniden deneyebilmesi gerekir.

İstediğiniz SERİLEŞTİRİLEBİLİR izolasyon düzeyidir: birbiri ardına bağımsız olarak gerçekleştirilen işlemlerin iyi bir duruma yol açması durumunda, işlemlerin paralel yürütülmesinin de iyi bir duruma yol açmasını sağlar. Neyse ki, Michael Cahill doktora tezinde SERİLEŞTİRİLEBİLİR izolasyon seviyesinin, az çaba harcanan anlık görüntü izole veritabanları ile nasıl desteklenebileceğini keşfetti .

Anlık görüntü izole edilmiş bir veritabanında SERIALIZABLE yalıtım düzeyi kullanılıyorsa , iki kişi saklı yordamı aynı anda çalıştırmayı denerse ve birbirlerinin ayak parmaklarına basarlarsa, işlemlerden biri iptal edilir.

Şimdi, SQL Server gerçekten SERIALIZABLE yalıtım düzeyini destekliyor mu ( SERIALIZABLE anahtar sözcüğünün arkasındaki anlık görüntü yalıtımını maskelemek yerine )? Açıkçası, bilmiyorum: onu destekleyen bildiğim tek veritabanı PostgreSQL.

SQL Server'a özel tavsiyeler verememe rağmen, yine de bu yanıtı gönderiyorum, çünkü PostgreSQL kullanıcıları ve PostgreSQL'e geçmeyi düşünebilecek diğer veritabanlarının kullanıcıları benim cevabımdan yararlanabilir. Ayrıca, PostgreSQL'e geçemeyen PostgreSQL olmayan veritabanlarının kullanıcıları, orijinal SERIALIZABLE yalıtım seviyesi sunmak için en sevdikleri veritabanı satıcılarını zorlayabilir .


Ben downvote almak birisi SQL Server SERIALIZABLE izolasyon düzeyine sahip olup olmadığını araştırdı ve olmadığını öğrendim anlamına gelir.
juhist

-2

'Gerçek' sorunun daha karmaşık olabileceğini anlıyorum.

Aksi takdirde: arşivlemenizi ekleme ve / veya güncelleme tetikleyicileri ile yaparsanız, çözmeye çalıştığınız sorunu önleyebilirsiniz.

Umarım yardımcı olur,
-Chris C.


1
"Hemen" derken ne demek istiyorsun? Hemen sonra ne olacak? Taktıktan sonra? Yeni bir satır geldiğinde, hemen arşive gönderilir mi? Yoksa güncelleme sonrasında mı demek istediniz? Herhangi bir veri değişikliği arşivlemeyi tetikliyor mu? Belki de bunu öneren aklınızdaki senaryo hakkında daha spesifik olmalısınız.
Andriy M

Arşivleme, özellikle kaynak tablo sık sık yerleştirilirse ve / veya arşiv arasında işlemsel güvenlik pahalı kilitler gerektiriyorsa, her ek için çok pahalı ve / veya nadiren istenmeye değebilir.
underscore_d

@underscore_d Evet, çok maliyetli olabilir veya her zaman gerekli olmayabilir. Bu yüzden cevabımı şu ifadeyle başlattım the 'real' problem may be more complex. Olmaması durumunda, tetikleyiciler iyi bir çözümdür. Ayrıca, özel bir çözüm yerine veritabanının bir özelliği olduğu için muhtemelen test edilmesi ve bakımı daha kolay olacaktır.
J. Chris Compton

@AndriyM Tetikleyicileri eklemek / güncellemek için bir ref ile değiştirerek kelimeyi hemen kaldırdım. Karışıklık için özür dilerim.
J. Chris Compton

1
Soruyu tekrar okudum ve sanırım karışıklığımın kaynağını görebiliyorum. Burada önerdiğiniz şey, arşivlemekten çok denetime benziyor. Anladığım kadarıyla, verilerin arşivlenmesi verilerin taşınmasını gerektirir (örn. Bir tablodan diğerine). Bununla birlikte, OP prosedürlerinin işlevini "bir çeşit arşivleme" olarak özetlese de, verilerin kaynaktan kaldırılacağını, sadece kendisinden seçileceğini ve hedefe ekleneceğini asla söylemediler. Bu yüzden, OP'nin verilerini taşımak yerine kopyalamak zorunda olduğunu varsaydığınızı tahmin ediyorum , bu durumda tetikleyicileri kullanmak muhtemelen mantıklıdır.
Andriy M
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.