Cara mendapatkan SQL insert dan / atau pembaruan agar tidak mengunci seluruh tabel di MS SQL Server

13

Sangat banyak pemula di pekerjaan DB, jadi menghargai kesabaran Anda dengan pertanyaan dasar. Saya menjalankan SQL Server 2014 pada mesin lokal saya, dan saya punya meja kecil dan aplikasi klien dasar untuk menguji berbagai pendekatan dengan. Saya mendapatkan apa yang tampaknya menjadi kunci tabel selama keduanya INSERT INTOdan UPDATEpernyataan. Klien adalah aplikasi ASP.NET dengan kode berikut:

OleDbConnection cn = new OleDbConnection("Provider=SQLNCLI11; server=localhost\\SQLEXPRESS; Database=<my db>; user id=<my uid>; password=<my pwd>");
cn.Open();
OleDbTransaction tn = cn.BeginTransaction();
OleDbCommand cmd = new OleDbCommand("INSERT INTO LAYOUTSv2 (LAYOUTS_name_t, LAYOUTS_enabled_b, LAYOUTS_data_m) VALUES ('name', '-1', 'data')", cn, tn);
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT SCOPE_IDENTITY()";
int newkey = Decimal.ToInt32((decimal)cmd.ExecuteScalar());
Console.WriteLine("Created index " + newkey);
Thread.Sleep(15000);
tn.Commit();
tn = cn.BeginTransaction();
cmd.CommandText = "UDPATE LAYOUTSv2 SET LAYOUTS_enabled_b='-3' WHERE LAYOUTS_key='" + newkey + "'";
cmd.Transaction = tn;
cmd.ExecuteNonQuery();
Console.WriteLine("updated row");
Thread.Sleep(15000);
tn.Rollback();
cn.Close();

Saya menjalankan kode ini, lalu dari studio manajemen yang saya jalankan SELECT * FROM LAYOUTSv2. Selama kedua kasus ketika utas klien dijeda (yaitu sebelum komit / rollback) kueri SELECT hang sampai komit / rollback terjadi.

Tabel memiliki bidang LAYOUTS_key yang ditetapkan sebagai kunci utama. Di jendela properti itu menunjukkan bahwa itu unik dan berkerumun, dengan kunci halaman dan kunci baris keduanya diizinkan. Pengaturan eskalasi kunci untuk tabel adalah Nonaktifkan ... Saya sudah mencoba kedua pengaturan lain yang tersedia dari Tabel dan AUTO tanpa perubahan. Saya sudah mencoba SELECT ... WITH (NOLOCK)dan itu mengembalikan hasilnya segera, tetapi seperti yang diperingatkan dengan baik di sini dan di tempat lain, bukan itu yang seharusnya saya lakukan. Saya sudah mencoba memberi ROWLOCKpetunjuk pada kedua pernyataan INSERTdan UPDATE, tetapi tidak ada yang berubah.

Perilaku yang saya cari adalah ini: sebelum melakukan suatu INSERT, kueri dari utas lain membaca semua baris kecuali yang sedang INSERTdiedit. Sebelum melakukan UPDATEkueri dari utas lain, baca versi awal dari baris yang sedang UPDATEdiedit. Apakah ada cara saya bisa melakukan ini? Jika saya perlu memberikan informasi lain untuk mengklarifikasi kasus penggunaan saya, beri tahu saya. Terima kasih.

John Riehl
sumber
3
Ngomong-ngomong WHERE LAYOUTS_key='" + newkey + "'adalah no-no lengkap untuk berbagai alasan termasuk injeksi SQL, Anda harus menggunakan query parameterised.
Martin Smith
1
@ MartinSmith Terima kasih atas informasi ini ... tidak pernah mendengar tentang kueri parameterisasi atau serangan SQL Injection.
John Riehl
@JohnRiehl, re: serangan injeksi, bayangkan jika pengguna Anda menyetel newkeyke " something';DELETE FROM LAYOUTSv2 --". Pembaruan Anda akan selesai dengan sukses, dan kemudian mengosongkan tabel karena pengguna memanipulasi kueri dengan memasukkan tanda kutip. Biasanya, permintaan parameterisasi terlihat seperti UDPATE LAYOUTSv2 SET LAYOUTS_enabled_b='-3' WHERE LAYOUTS_key=?, setelah itu Anda secara terpisah memberikan nilai ke ?(parameter) dalam kode Anda.
Daniel Hutmacher

Jawaban:

10

Kemungkinannya adalah tidak mengunci "seluruh tabel".

Mengunci baris dalam tabel tetapi Anda SELECT * FROM LAYOUTSv2mencoba membaca seluruh tabel sehingga harus diblokir oleh kunci itu.

Untuk kasing, Anda dapat menentukan READPASTpetunjuk untuk melewati baris yang terkunci - namun itu tidak akan memberikan hasil yang diinginkan untuk UPDATEkasing (kasing akan melewati baris lagi dan tidak membaca versi awal dari baris).

Jika Anda mengonfigurasi database untuk isolasi snapshot yang sudah dibaca, ini akan memberikan efek yang Anda inginkan untuk kedua kasus (dengan mengorbankan penggunaan yang lebih besar dari tempdb)

Martin Smith
sumber
Saya mengubah "Is Read Committed Snapshot On" menjadi True dan sekarang berfungsi dengan sempurna tanpa petunjuk. Terima kasih! Satu tindak lanjut ... Saya meninggalkan "Izinkan Isolasi Snapshot" disetel ke False ... apakah itu OK? Terima kasih.
John Riehl
@JohnRiehl - Ya jika Anda tidak secara eksplisit menggunakan SNAPSHOTisolasi untuk membiarkannya dinonaktifkan dan kemudian mengaktifkannya jika Anda kemudian memutuskan ini akan berguna untuk Anda.
Martin Smith
7

Pernyataan memasukkan dan memperbarui seharusnya membuat kunci tingkat baris. Namun, ketika jumlah kunci dalam transaksi apa pun adalah 5.000 atau lebih maka eskalasi kunci terjadi dan itu menciptakan kunci tingkat tabel. Silahkan lihat di bawah ini.

https://technet.microsoft.com/en-us/library/ms184286(v=sql.105).aspx

Suraj
sumber
Tidak relevan dengan pertanyaan ini karena pernyataan INSERT dan UPDATE menulis satu baris
Martin Smith