Di SQL Server, bagaimana cara kerja baca kunci?

17

Misalkan saya memiliki permintaan yang sudah berjalan lama berikut

UPDATE [Table1]
SET [Col1] = 'some value'
WHERE [Col2] -- some clause which selects thousands of rows

dan misalkan kueri berikut dijalankan ketika kueri di atas sedang berjalan

SELECT *
FROM [Table1]

Apakah kueri pertama mencegah kueri kedua agar tidak berjalan hingga kueri pertama selesai? Jika demikian, apakah kueri pertama mencegah kueri kedua dari berjalan di semua baris atau hanya baris yang terlibat dalam klausa WHERE?

EDIT:

Misalkan permintaan kedua adalah

SELECT [Col1], [Col2]
FROM [Table1]
WHERE [Col2] -- some clause whose matching elements overlap those from
             -- the clause in the first query and which has additional matching elements
cm007
sumber

Jawaban:

14

Saya sarankan Anda membaca Memahami bagaimana SQL Server menjalankan kueri , ia memiliki penjelasan tentang cara membaca dan menulis bekerja dan bagaimana penguncian bekerja.

Tampilan 10.000 kaki sebagai berikut:

  • baca operator mendapatkan kunci bersama pada data yang mereka baca, sebelum membaca data
  • operator menulis memperoleh kunci eksklusif pada data yang mereka modifikasi sebelum memodifikasi data
  • kunci data hanyalah string, mis. hash kunci sedang dibaca scoped oleh database dan objek.
  • manajer kunci menyimpan daftar semua kunci yang diberikan dan mendeteksi ketidaksesuaian, menurut matriks Kompatibilitas Kunci
  • permintaan yang tidak kompatibel ditangguhkan sampai hibah yang tidak sesuai memblokirnya dirilis
  • operator menggunakan hierarki kunci untuk menyatakan niat untuk membaca atau memperbarui data di tingkat yang lebih tinggi (tingkat halaman atau tabel, mengabaikan opsi tingkat partisi). Ini memungkinkan operator untuk mengunci seluruh tabel tanpa mengunci setiap baris
  • kunci seumur hidup dan kunci rentang digunakan untuk menegakkan tingkat isolasi yang lebih tinggi

Ini benar-benar hanya ujung dari es. Subjeknya sangat luas. Dalam contoh Anda, tidak ada yang dapat menjawab pertanyaan Anda tentang apa yang sebenarnya dikunci karena akan tergantung pada banyak faktor. Tentu saja, tidak ada aplikasi yang harus mengeluarkan a SELECT * FROM Table1 karena tidak ada klausa WHERE dan sedang digunakan *. Ini adalah praktik buruk karena, di antara hal-hal lain, mereka akan mengarah pada pertikaian kunci.

Jika Anda menemukan kunci baca dan tulis, Anda perlu melihat ke versi baris dan isolasi snapshot. Baca Memahami Tingkat Isolasi Berbasis Versi Baris .

Remus Rusanu
sumber
Bagaimana jika saya membutuhkan semua isi tabel (katakan saya hanya memiliki 14 baris di dalamnya)? Bagaimana praktik buruk SELECT * FROM Table1jika itu yang saya butuhkan?
Azimuth
1
*sendiri adalah praktik yang buruk karena ketika struktur tabel berubah aplikasi biasanya rusak (kolom yang tidak terduga muncul sebagai hasilnya).
Remus Rusanu
3

Sunting: Seperti @MaxVernon tunjukkan, berikut ini sama sekali bukan saran untuk menggunakan NOLOCK , dan saya sangat baik hanya menyebutkan pengaturan tingkat transaksi READ UNCOMMITEDdan membiarkan konotasi negatif berdiri di sana daripada membawa NOLOCKdi tempat pertama. Jadi seperti yang awalnya diposting:

Cepat dan sederhana adalah "Ya, permintaan pertama akan memblokir permintaan kedua kecuali petunjuk indeks tertentu ditentukan ( NOLOCK , kadang-kadang disebut" membaca kotor ") atau tingkat isolasi transaksi permintaan kedua diatur keREAD UNCOMMITED (yang beroperasi secara identik), tidak."

Menanggapi perincian tambahan yang disediakan dalam pertanyaan yang melibatkan dimasukkannya WITHklausa pada klausa yang kedua SELECT, saling eksklusif atau sebaliknya, interaksi antara kedua pertanyaan akan sebagian besar sama.

IF NOT EXISTS ( SELECT  1
                FROM    sys.objects
                WHERE   name = 'Foo'
                    AND type = 'U' )
BEGIN
    --DROP TABLE dbo.Foo;
    CREATE TABLE dbo.Foo
    (
        Foo_PK          BIGINT IDENTITY( 1, 1 ) NOT NULL,
                            PRIMARY KEY ( Foo_PK ),
        Bar             BIT,
        x               BIT,
        y               BIT,
        z               BIT
    );

    CREATE NONCLUSTERED INDEX IX_Foo_x
        ON  dbo.Foo ( x );

    INSERT INTO dbo.Foo ( Bar, x, y, z )
    VALUES ( 1, 1, 1, 1 ), ( 0, 0, 0, 0 );
END;    
GO

BEGIN TRANSACTION;

UPDATE  dbo.Foo
    SET y = 0
WHERE   x = 1;

-- COMMIT TRANSACTION;

Dalam sesi terpisah, jalankan yang berikut:

SELECT  *
FROM    dbo.Foo WITH ( NOLOCK );
GO

SELECT  *
FROM    dbo.Foo;

Anda dapat memeriksa kunci yang saat ini ditahan dengan berlari sp_lock, lebih disukai dalam sesi lain yang terpisah:

EXECUTE dbo.sp_lock;

Anda akan melihat KEYkunci jenis dipegang oleh spid yang melakukan transaksi penyisipan dalam Xmode (eksklusif), jangan dikacaukan dengan IXkunci (Intent-Eksklusif) lainnya. The kunci dokumentasi menunjukkan bahwa sementara KEYkunci adalah rentang tertentu, juga mencegah transaksi lainnya dari memasukkan atau memperbarui kolom yang terkena dengan mengubah data di dalamnya sehingga bisa jatuh dalam rentang dari query asli. Sebagai kunci itu sendiri ditahan adalah eksklusif, pertanyaan pertama adalah mencegah akses ke sumber daya dari setiap transaksi konkuren lainnya. Akibatnya, semua baris kolom dikunci, apakah mereka termasuk dalam rentang yang ditentukan oleh kueri pertama atau tidak.

The Skunci yang dipegang oleh sesi kedua akan demikian WAITsampai XClears kunci, mencegah lainX (atau U) kunci dari yang diambil pada sumber daya yang dari spid bersamaan berbeda sebelum sesi kedua selesai operasi baca nya, membenarkan keberadaan Skunci.

Sekarang edit untuk kejelasan: Kecuali saya salah dalam membaca kotor dari deskripsi singkat tentang risiko yang disebutkan di sini ... Edit 3 : Saya baru sadar saya tidak mempertimbangkan efek dari pos pemeriksaan latar belakang yang menulis sebagai transaksi belum berkomitmen ke disk, jadi ya, penjelasan saya menyesatkan.

Dalam kueri kedua, kumpulan pertama dapat (dan dalam hal ini, akan) mengembalikan data yang tidak dikomit. Batch kedua, berjalan di tingkat isolasi transaksi default READ COMMITEDakan kembali hanya setelah komit atau rollback telah selesai di sesi pertama.

Dari sini Anda dapat melihat rencana kueri Anda dan tingkat kunci terkait, tetapi lebih baik lagi, Anda dapat membaca semua tentang kunci di SQL Server di sini .

Avarkx
sumber
1
Kata peringatan tentang penggunaan WITH (NOLOCK)akan membantu dalam kasus ini. Lihat brentozar.com/archive/2011/11/… dan brentozar.com/archive/2013/02/… untuk bacaan lebih lanjut.
Max Vernon
3
Oh, WITH (NOLOCK)petunjuknya tidak mengembalikan halaman kotor dari memori yang belum dilakukan. Ini benar-benar membaca baris dari tabel (baik di-disk atau cache di-memori) tanpa menghalangi penulis dari memperbarui atau menambahkan baris ke halaman yang digunakan oleh tabel.
Max Vernon
2
Saya bingung. Jika jawaban untuk "apakah kueri ke-1 mencegah ke-2 dari berjalan?" adalah "Tidak", bagaimana jawaban untuk pertanyaan kedua adalah "Ya"? Bisakah Anda mengklarifikasi pertanyaan mana yang Anda jawab, dan memperluas jawaban Anda?
Jon of All Trades
Editan berlimpah, maaf teman-teman! Beri tahu saya jika ada hal lain yang tidak jelas!
Avarkx