Sqlserver
 sql >> Teknologi Basis Data >  >> RDS >> Sqlserver

Mengikuti kebuntuan transaksi tunggal di seluruh versi SQL Server

Salah satu kebuntuan yang kurang umum adalah di mana ada satu pengguna dan mereka menemui jalan buntu pada beberapa sumber daya sistem. Yang baru-baru ini saya temui adalah membuat tipe alias, lalu mendeklarasikan variabel tipe itu, di dalam transaksi yang sama. Bayangkan Anda mencoba menjalankan pengujian unit atau pengujian pra-penerapan, memeriksa kegagalan, dan melakukan rollback dalam kasus apa pun sehingga Anda tidak meninggalkan jejak apa pun dari apa yang telah Anda lakukan. Polanya mungkin terlihat seperti ini:

BEGIN TRANSACTION;
GO
CREATE TYPE EmailAddress FROM VARCHAR(320);
GO
DECLARE @x TABLE (e EmailAddress);
GO
ROLLBACK TRANSACTION;

Atau, lebih mungkin, sedikit lebih rumit:

BEGIN TRANSACTION;
GO
CREATE TYPE EmailAddress FROM VARCHAR(320);
GO
CREATE PROCEDURE dbo.foo 
  @param EmailAddress 
AS
BEGIN 
  SET NOCOUNT ON;
  DECLARE @x TABLE (e EmailAddress);
  INSERT @x SELECT @param;
END
GO
DECLARE @x EmailAddress;
SET @x = N'whatever';
EXEC dbo.foo @param = N'whatever';
GO
ROLLBACK TRANSACTION;

Tempat pertama saya mencoba kode ini adalah SQL Server 2012, dan kedua contoh gagal dengan kesalahan berikut:

Msg 1205, Level 13, State 55, Line 14
Transaksi (Process ID 57) menemui jalan buntu pada sumber daya kunci dengan proses lain dan telah dipilih sebagai korban kebuntuan. Jalankan kembali transaksi.

Dan tidak banyak yang bisa dipelajari dari grafik kebuntuan:

Melangkah mundur beberapa tahun, saya ingat ketika saya pertama kali belajar tentang tipe alias, di SQL Server 2000 (ketika mereka disebut Tipe Data Buatan Pengguna). Pada saat itu, kebuntuan yang saya temui baru-baru ini tidak akan terjadi (tetapi ini setidaknya sebagian karena Anda tidak dapat mendeklarasikan variabel tabel dengan tipe alias – lihat di sini dan di sini). Saya menjalankan kode berikut pada SQL Server 2000 RTM (8.0.194) dan SQL Server 2000 SP4 (8.0.2039), dan itu berjalan dengan baik:

BEGIN TRANSACTION;
GO
EXEC sp_addtype @typename = N'EmailAddress', @phystype = N'VARCHAR(320)';
GO
CREATE PROCEDURE dbo.foo 
  @param EmailAddress 
AS
BEGIN 
  SET NOCOUNT ON;
  SELECT @param;
END
GO
EXEC dbo.foo @param = N'whatever';
GO
DECLARE @x EmailAddress;
SET @x = N'whatever';
EXEC dbo.foo @param = @x;
GO
ROLLBACK TRANSACTION;

Tentu saja, skenario ini tidak tersebar luas pada saat itu karena, bagaimanapun, tidak banyak orang yang menggunakan tipe alias. Meskipun mereka dapat membuat metadata Anda lebih mendokumentasikan diri sendiri dan seperti definisi data, mereka sangat merepotkan jika Anda ingin mengubahnya, yang mungkin menjadi topik untuk posting lain.

SQL Server 2005 muncul, dan memperkenalkan sintaks DDL baru untuk membuat tipe alias:CREATE TYPE . Ini tidak benar-benar menyelesaikan masalah dengan mengubah tipe, itu hanya membuat sintaksnya sedikit lebih bersih. Di RTM, semua contoh kode di atas berfungsi dengan baik tanpa kebuntuan. Di SP4, bagaimanapun, mereka semua akan menemui jalan buntu. Oleh karena itu, di suatu tempat antara RTM dan SP4, mereka mengubah penanganan internal untuk transaksi yang melibatkan variabel tabel menggunakan tipe alias.

Maju cepat beberapa tahun ke SQL Server 2008, di mana parameter bernilai tabel ditambahkan (lihat kasus penggunaan yang bagus di sini). Hal ini membuat penggunaan jenis ini menjadi lebih umum, dan memperkenalkan kasus lain di mana transaksi yang mencoba membuat dan menggunakan jenis seperti itu akan menemui jalan buntu:

BEGIN TRANSACTION;
GO
CREATE TYPE dbo.Items AS TABLE(Item INT);
GO
DECLARE @r dbo.Items;
GO
ROLLBACK TRANSACTION;

Saya memeriksa Connect, dan menemukan beberapa item terkait, salah satunya mengklaim bahwa masalah ini telah diperbaiki di SQL Server 2008 SP2 dan 2008 R2 SP1:

Connect #365876 :Deadlock terjadi saat membuat tipe data yang ditentukan pengguna dan objek yang menggunakannya

Apa yang sebenarnya dirujuk adalah skenario berikut, di mana hanya membuat prosedur tersimpan yang mereferensikan tipe dalam variabel tabel akan menemui jalan buntu di SQL Server 2008 RTM (10.0.1600) dan SQL Server 2008 R2 RTM (10.50.1600):

BEGIN TRANSACTION;
GO
CREATE TYPE EmailAddress FROM VARCHAR(320);
GO
CREATE PROCEDURE dbo.foo 
  @param EmailAddress 
AS
BEGIN 
  SET NOCOUNT ON;
  DECLARE @x TABLE (e EmailAddress);
  INSERT @x SELECT @param;
END
GO
ROLLBACK TRANSACTION;

Namun, ini tidak menemui jalan buntu di SQL Server 2008 SP3 (10.0.5846) atau 2008 R2 SP2 (10.50.4295). Jadi saya cenderung percaya komentar pada item Connect – bahwa bagian dari bug ini telah diperbaiki pada 2008 SP2 dan 2008 R2 SP1, dan tidak pernah menjadi masalah di versi yang lebih modern.

Tapi ini masih menghilangkan kemampuan untuk benar-benar menempatkan tipe alias melalui segala jenis pengujian yang sebenarnya. Jadi pengujian unit saya akan berhasil selama yang ingin saya lakukan hanyalah menguji bahwa saya dapat membuat prosedur – lupakan tentang mendeklarasikan tipe sebagai variabel lokal atau sebagai kolom dalam variabel tabel lokal.

Satu-satunya cara untuk menyelesaikannya adalah dengan membuat tipe tabel sebelum memulai transaksi, dan secara eksplisit menjatuhkannya sesudahnya (atau memecahnya menjadi beberapa transaksi). Ini bisa menjadi sangat rumit, atau bahkan tidak mungkin, untuk sering mengotomatiskan kerangka kerja pengujian dan memanfaatkan sepenuhnya mengubah cara mereka beroperasi untuk memperhitungkan batasan ini.

Jadi saya memutuskan untuk menjalani beberapa tes di build awal dan terbaru dari semua versi utama:SQL Server 2005 RTM, 2005 SP4, 2008 RTM, 2008 SP3, 2008 R2 RTM, 2008 R2 SP2, 2012 RTM, 2012 SP1, dan 2014 CTP2 (dan ya, saya sudah menginstal semuanya). Saya telah meninjau beberapa item Connect dan berbagai komentar yang membuat saya bertanya-tanya kasus penggunaan mana yang didukung dan di mana, dan saya memiliki dorongan aneh untuk mencari tahu aspek mana dari masalah ini yang sebenarnya telah diperbaiki. Saya menguji berbagai skenario kebuntuan potensial yang melibatkan tipe alias, variabel tabel, dan parameter bernilai tabel terhadap semua build ini; kodenya sebagai berikut:

/* 
  alias type - declare in local table variable 
  always deadlocks on 2005 SP4 -> 2014, except in 2005 RTM
*/
 
BEGIN TRANSACTION;
GO
CREATE TYPE EmailAddress FROM VARCHAR(320)
GO
DECLARE @r TABLE(e EmailAddress);
GO
ROLLBACK TRANSACTION;
 
 
/* 
  alias type - create procedure with param & table var 
  sometimes deadlocks - 2005 SP4, 2008 RTM & SP1, 2008 R2 RTM
*/
 
BEGIN TRANSACTION;
GO
CREATE TYPE EmailAddress FROM VARCHAR(320);
GO
CREATE PROCEDURE dbo.foo 
  @param EmailAddress 
AS
BEGIN 
  SET NOCOUNT ON;
  DECLARE @x TABLE (e EmailAddress);
  INSERT @x SELECT @param;
END
GO
ROLLBACK TRANSACTION;
 
 
/* 
  alias type - create procedure, declare & exec 
  always deadlocks on 2005 SP4 -> 2014, except on 2005 RTM
*/
 
BEGIN TRANSACTION;
GO
CREATE TYPE EmailAddress FROM VARCHAR(320);
GO
CREATE PROCEDURE dbo.foo 
  @param EmailAddress 
AS
BEGIN 
  SET NOCOUNT ON;
  DECLARE @x TABLE (e EmailAddress);
  INSERT @x SELECT @param;
END
GO
DECLARE @x EmailAddress;
SET @x = N'whatever';
EXEC dbo.foo @param = N'whatever';
GO
ROLLBACK TRANSACTION;
 
 
/* obviously did not run these on SQL Server 2005 builds */
 
/* 
  table type - create & declare local variable 
  always deadlocks on 2008 -> 2014
*/
 
BEGIN TRANSACTION;
GO
CREATE TYPE dbo.Items AS TABLE(Item INT);
GO
DECLARE @r dbo.Items;
GO
ROLLBACK TRANSACTION;
 
 
/* 
  table type - create procedure with param and SELECT 
  never deadlocks on 2008 -> 2014
*/
 
BEGIN TRANSACTION;
GO
CREATE TYPE dbo.Items AS TABLE(Item INT);
GO
CREATE PROCEDURE dbo.foo 
  @param dbo.Items READONLY
AS
BEGIN 
  SET NOCOUNT ON;
  SELECT Item FROM @param;
END
GO
ROLLBACK TRANSACTION;
 
 
/* 
  table type - create procedure, declare & exec 
  always deadlocks on 2008 -> 2014
*/
 
BEGIN TRANSACTION;
GO
CREATE TYPE dbo.Items AS TABLE(Item INT);
GO
CREATE PROCEDURE dbo.foo 
  @param dbo.Items READONLY
AS
BEGIN 
  SET NOCOUNT ON;
  SELECT Item FROM @param;
END
GO
DECLARE @x dbo.Items;
EXEC dbo.foo @param = @x;
GO
ROLLBACK TRANSACTION;

Dan hasilnya mencerminkan cerita saya di atas:SQL Server 2005 RTM tidak menemui jalan buntu pada salah satu skenario, tetapi pada saat SP4 bergulir, semuanya menemui jalan buntu. Ini telah diperbaiki untuk skenario "buat tipe dan buat prosedur", tetapi tidak satu pun yang lain, di 2008 SP2 dan 2008 R2 SP1. Berikut adalah tabel yang menunjukkan semua hasil:

Versi SQL Server / Build #
SQL 2005 SQL 2008 SQL 2008 R2 SQL 2012 SQL 2014
RTM
9.0.1399
SP4
9.0.5324
RTM
10.0.1600
SP3
10.0.5846
RTM
10.50.1600
SP2
10.50.4295
RTM
11.0.2100
SP1
11.0.3381
CTP2
12.0.1524
Jenis Alias deklarasikan dalam tabel var
buat prosedur
buat &jalankan proc
Jenis Tabel deklarasikan var lokal T/A
buat prosedur
buat &jalankan proc

Kesimpulan

Jadi, moral dari ceritanya adalah, masih belum ada perbaikan untuk use case yang dijelaskan di atas, di mana Anda ingin membuat tipe tabel, membuat prosedur atau fungsi yang menggunakan tipe, mendeklarasikan tipe, menguji modul, dan menggulung semuanya kembali. Bagaimanapun, berikut adalah item Connect lainnya untuk Anda lihat; semoga Anda dapat memilih mereka dan meninggalkan komentar yang menjelaskan bagaimana skenario kebuntuan ini memengaruhi bisnis Anda secara langsung:

  • Connect #581193 :Membuat tipe tabel dan menggunakannya dalam transaksi yang sama menyebabkan kebuntuan
  • Connect #800919 :Masalah dalam Membuat Fungsi dengan TableValue Jenis Pengembalian dalam transaksi dengan tipe yang ditentukan pengguna dalam tabel yang dibuat dalam lingkup transaksi yang sama
  • Connect #804365 :Deadlock terjadi ketika tipe tabel yang ditentukan pengguna dibuat dan digunakan dalam satu transaksi

    Saya sepenuhnya mengharapkan beberapa klarifikasi akan ditambahkan ke item Connect ini dalam waktu dekat, meskipun saya tidak tahu persis kapan mereka akan didorong.


    1. Database
    2.   
    3. Mysql
    4.   
    5. Oracle
    6.   
    7. Sqlserver
    8.   
    9. PostgreSQL
    10.   
    11. Access
    12.   
    13. SQLite
    14.   
    15. MariaDB
    1. Gunakan PARSENAME() untuk Mengembalikan Bagian dari Nama Objek di SQL Server

    2. 5 Fakta Teratas untuk Menemukan dan Mengganti Teks SQL di SQL Server dengan Fungsi REPLACE

    3. Bangun tanggal dari nomor tahun dan minggu di MSSQL

    4. Mendapatkan nilai pengembalian dari prosedur tersimpan di C #

    5. Gunakan CRYPT_GEN_RANDOM() untuk Membuat Kriptografi, Angka Acak di SQL Server