Bagaimana cara membatasi prosedur tersimpan SQL untuk dijalankan oleh satu orang pada satu waktu?

12

Saya memiliki prosedur tersimpan yang pada dasarnya memilih nilai dari satu tabel dan menyisipkannya ke yang lain, semacam pengarsipan. Saya ingin menghindari banyak orang melakukan itu pada saat yang sama.

Ketika prosedur ini sedang berjalan, saya tidak ingin orang lain dapat memulainya, namun saya tidak ingin serialisasi, orang lain akan menjalankan prosedur setelah saya selesai.

Yang saya inginkan adalah orang lain yang mencoba memulainya untuk mendapatkan kesalahan, sementara saya menjalankan prosedur.

Saya sudah mencoba menggunakan sp_getapplock, namun saya tidak bisa menghentikan orang tersebut untuk menjalankan prosedur.

Saya juga mencoba menemukan prosedur dengan sys.dm_exec_requests dan memblokir prosedur, sementara ini berhasil, saya pikir itu tidak optimal karena pada beberapa server saya tidak memiliki izin untuk menjalankan sys.dm_exec_sql_text (sql_handle).

Apa cara terbaik bagi saya untuk melakukan ini?

twoheadedmona
sumber
3
Bisakah Anda mengambil satu langkah mundur, dan memberikan beberapa info lebih lanjut tentang apa yang dilakukan prosedur ini, dan mengapa Anda ingin menghindari beberapa orang menjalankannya pada saat yang sama? Mungkin ada teknik pengkodean yang menghilangkan persyaratan ini, atau semacam antrian yang bisa Anda terapkan untuk menangani berbagai hal.
AMtwo

Jawaban:

15

Untuk menambahkan jawaban @ Tibor-Karaszi, menetapkan batas waktu penguncian sebenarnya tidak menghasilkan kesalahan (saya telah mengirimkan PR terhadap dokumen). sp_getapplock hanya mengembalikan -1, jadi Anda harus memeriksa nilai kembali. Jadi seperti ini:

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
David Browne - Microsoft
sumber
8

Gunakan sp_getapplock di awal proc, dan atur timeout kunci ke nilai yang sangat rendah. Dengan cara ini Anda mendapatkan kesalahan saat Anda diblokir.

Tibor Karaszi
sumber
7

Pilihan lain adalah membuat tabel untuk mengontrol akses ke prosedur. contoh di bawah ini menunjukkan tabel yang memungkinkan serta prosedur yang dapat menggunakannya.

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
Jonathan Fite
sumber
1
Ini sangat mirip dengan (atau mungkin sama dengan) apa yang segera saya pikirkan setelah membaca pertanyaan itu, tetapi saya memiliki masalah dengan gagasan itu sehingga saya tidak sepenuhnya yakin bagaimana cara mengatasinya dan tidak dapat melihatnya ditangani dalam jawaban Anda. antara. Kekhawatiran saya adalah, bagaimana jika sesuatu terjadi selama bagian "melakukan apa pun yang perlu dilakukan"? Bagaimana Anda mengatur ulang IsLockedstatus ke 0 dalam kasus itu? Saya juga ingin tahu tentang penggunaan Anda di COALESCEsini. Dapat @@ROWCOUNTberupa null setelah pernyataan suka UPDATE? Akhirnya, hanya nitpick kecil, mengapa meletakkan tanda koma di depan THROWpernyataan dalam kasus khusus itu?
Andriy M
Kedaluwarsa kunci adalah salah satu cara untuk menangani itu. Ini perlu diatur ke jangka waktu yang masuk akal, saya telah menetapkannya menjadi 10 menit dalam contoh saya. Anda bisa merangkum logika kerja Anda di blok coba / tangkap dan buka kunci di tangkap jika Anda mau. Saya menggunakan COALESCE karena kebiasaan, tetapi tidak ada @@ ROWCOUNT tidak boleh NULL. semi-colon terkemuka berasal dari bekerja dengan proyek-proyek database Visual Studio, ia mengeluh jika itu tidak ada. tidak ada salahnya juga.
Jonathan Fite
-1

Saya pikir Anda mencoba menyelesaikan masalah dengan cara yang salah. Apa yang Anda inginkan adalah perlindungan terbaik terhadap konsistensi basis data. Jika dua orang menjalankan prosedur tersimpan pada saat yang sama, konsistensi basis data dapat dilanggar.

Untuk melindungi dari berbagai jenis ketidakkonsistenan basis data, standar SQL memiliki empat tingkat isolasi transaksi:

  • BACA TIDAK DIKOMPITASI di mana pada dasarnya transaksi kehilangan nilainya, transaksi lain yang melihat data kotor. Jangan gunakan ini!
  • BACA BERKOMITMEN di mana transaksi hanya melihat data yang dikomit, tetapi mungkin ada inkonsistensi di mana dua transaksi dapat melangkahi jari masing-masing
  • BACA YANG DIULANGKAN di mana satu jenis ketidakkonsistenan, bacaan yang tidak dapat diulang, diselesaikan
  • SERIALIZABLE yang menjamin bahwa ada beberapa tatanan virtual di mana melaksanakan transaksi akan menghasilkan hasil yang menghasilkan eksekusi mereka

Namun, standar SQL memiliki pendekatan berbasis penguncian untuk inkonsistensi basis data ini, dan untuk alasan kinerja banyak basis data mengambil pendekatan berbasis isolasi snapshot yang pada dasarnya memiliki level-level ini:

  • BACA BERKOMITMEN yang sama dengan mengunci basis data
  • ISOLASI SNAPSHOT di mana database melihat snapshot semua data dan jika mencoba memperbarui baris yang telah diperbarui oleh beberapa transaksi lain, itu dibatalkan, namun ada beberapa anomali terkenal yang dapat terjadi
  • SERIALIZABLE yang sama dengan mengunci basis data, tapi kali ini diterapkan dengan cara yang berbeda, bukan dengan mengambil kunci tetapi dengan memastikan tidak ada pelanggaran serialisasi, dan jika pelanggaran tersebut terdeteksi, membatalkan transaksi

Pembatalan transaksi dalam database berbasis isolasi snapshot ini mungkin terdengar mengkhawatirkan, tetapi sekali lagi setiap database tunggal akan membatalkan transaksi karena jalan buntu, jadi aplikasi apa pun yang masuk akal tetap harus dapat mencoba kembali suatu transaksi.

Apa yang Anda inginkan adalah tingkat isolasi SERIALISASI : memastikan bahwa jika transaksi yang dilakukan secara independen satu sama lain menghasilkan keadaan yang baik, setiap eksekusi paralel dari transaksi juga menghasilkan keadaan yang baik. Untungnya, Michael Cahill dalam disertasi doktoralnya menemukan bagaimana tingkat isolasi SERIALIZABLE dapat didukung oleh database terisolasi snapshot dengan sedikit usaha.

Jika menggunakan tingkat isolasi SERIALIZABLE dalam database snapshot terisolasi, jika dua orang mencoba menjalankan prosedur yang tersimpan secara bersamaan dan mereka akan menginjak kaki masing-masing, salah satu transaksi akan dibatalkan.

Sekarang, apakah SQL Server benar-benar mendukung tingkat isolasi SERIALIZABLE (alih-alih menyamarkan isolasi snapshot di balik kata kunci SERIALIZABLE )? Sejujurnya, saya tidak tahu: satu-satunya database yang saya tahu yang mendukungnya adalah PostgreSQL.

Meskipun saya gagal memberikan saran khusus untuk SQL Server, saya tetap memposting jawaban ini, karena pengguna PostgreSQL dan pengguna database lain yang dapat mempertimbangkan beralih ke PostgreSQL dapat mengambil manfaat dari jawaban saya. Selain itu, pengguna basis data non-PostgreSQL yang tidak dapat beralih ke PostgreSQL dapat menekan vendor database favorit mereka untuk menawarkan tingkat isolasi asli SERIALIZABLE .

ahli hukum agama
sumber
Saya mengambil downvote berarti seseorang menyelidiki apakah SQL Server memiliki tingkat isolasi SERIALIZABLE dan menemukan bahwa itu tidak.
juhist
-2

Saya menyadari masalah 'nyata' mungkin lebih kompleks.

Jika tidak: jika Anda melakukan pengarsipan dengan memasukkan dan / atau memperbarui pemicu, Anda dapat menghindari masalah yang Anda coba selesaikan.

Semoga itu bisa membantu,
-Chris C.

J. Chris Compton
sumber
1
Apa yang Anda maksud dengan "segera"? Segera setelah apa? Setelah memasukkan? Jadi, begitu baris baru tiba, segera dikirim ke arsip? Atau maksud Anda setelah pembaruan? Jadi, apakah ada perubahan data yang memicu pengarsipan? Mungkin Anda harus lebih spesifik tentang skenario yang ada dalam pikiran Anda yang menyarankan ini.
Andriy M
Arsip mungkin terlalu mahal dan / atau terlalu jarang diinginkan untuk dilakukan pada setiap insert, terutama jika tabel sumber sering dimasukkan dan / atau keamanan transaksional antara itu dan arsip akan membutuhkan kunci mahal.
underscore_d
@underscore_d Ya, mungkin terlalu mahal atau tidak selalu diperlukan. Inilah sebabnya saya memulai jawaban saya dengan pernyataan itu the 'real' problem may be more complex. Jika tidak, pemicu adalah solusi yang baik. Plus itu mungkin akan lebih mudah untuk menguji dan memelihara karena itu adalah fitur dari database daripada solusi khusus.
J. Chris Compton
@AndriyM Saya segera menghapus kata itu, mengganti dengan referensi untuk memasukkan / memperbarui pemicu. Maaf bila membingungkan.
J. Chris Compton
1
Saya telah membaca kembali pertanyaannya dan saya pikir saya dapat melihat sumber kebingungan saya. Apa yang Anda sarankan di sini lebih mirip audit daripada pengarsipan. Seperti yang saya mengerti, pengarsipan data berarti memindahkan data (misalnya dari satu tabel ke yang lain). Namun, meskipun OP meringkas fungsi prosedur mereka sebagai "semacam pengarsipan", mereka tidak pernah mengatakan bahwa data akan dihapus dari sumbernya, hanya bahwa itu akan dipilih dari itu dan dimasukkan ke dalam target. Jadi saya kira Anda berasumsi bahwa OP perlu menyalin , daripada memindahkan , data mereka, dalam hal ini menggunakan pemicu mungkin masuk akal.
Andriy M