it-swarm.asia

Apa penyebab utama kebuntuan dan dapatkah mereka dicegah?

Baru-baru ini salah satu aplikasi ASP.NET kami menampilkan kesalahan kebuntuan basis data dan saya diminta untuk memeriksa dan memperbaiki kesalahan tersebut. Saya berhasil menemukan penyebab kebuntuan adalah prosedur tersimpan yang secara ketat memperbarui tabel dalam kursor.

Ini adalah pertama kalinya saya melihat kesalahan ini dan tidak tahu cara melacak dan memperbaikinya secara efektif. Saya mencoba semua cara yang mungkin saya tahu, dan akhirnya menemukan bahwa tabel yang sedang diperbarui tidak memiliki kunci utama! untungnya itu adalah kolom identitas.

Saya kemudian menemukan pengembang yang membuat database untuk penyebaran kacau. Saya menambahkan kunci utama dan masalahnya terpecahkan.

Saya merasa bahagia dan kembali ke proyek saya, dan melakukan riset untuk menemukan alasan kebuntuan itu ...

Rupanya, itu adalah kondisi menunggu melingkar yang menyebabkan kebuntuan. Pembaruan tampaknya membutuhkan waktu lebih lama tanpa kunci primer daripada dengan kunci primer.

Saya tahu itu bukan kesimpulan yang jelas, itu sebabnya saya memposting di sini ...

  • Apakah kunci utama yang hilang adalah masalahnya?
  • Apakah ada kondisi lain yang menyebabkan kebuntuan selain (saling pengecualian, tahan dan tunggu, tidak ada preemption dan menunggu bundar)?
  • Bagaimana cara saya mencegah dan melacak kebuntuan?
57
CoderHawk

melacak kebuntuan adalah yang lebih mudah dari keduanya:

Secara default, deadlock tidak ditulis dalam log kesalahan. Anda dapat menyebabkan SQL untuk menulis kebuntuan ke log kesalahan dengan bendera jejak 1204 dan 3605.

Menulis info kebuntuan ke log galat SQL Server: DBCC TRACEON (-1, 1204, 3605)

Matikan: DBCC TRACEOFF (-1, 1204, 3605)

Lihat "Pemecahan Masalah Deadlocks" untuk diskusi tentang jejak flag 1204 dan output yang akan Anda dapatkan ketika dihidupkan. https://msdn.Microsoft.com/en-us/library/ms178104.aspx

Pencegahan lebih sulit, pada dasarnya Anda harus memperhatikan hal-hal berikut:

Kode Blok 1 mengunci sumber A, lalu sumber B, dalam urutan itu.

Kode Blok 2 mengunci sumber B, lalu sumber A, dalam urutan itu.

Ini adalah kondisi klasik di mana kebuntuan dapat terjadi, jika penguncian kedua sumber daya tersebut tidak bersifat atomik, Blok Kode 1 dapat mengunci A dan dikosongkan, lalu Blok Kode 2 mengunci B sebelum A mendapatkan waktu pemrosesan kembali. Sekarang Anda menemui jalan buntu.

Untuk mencegah kondisi ini, Anda dapat melakukan sesuatu seperti berikut ini

Kode Blok A (kode psuedo)

Lock Shared Resource Z
    Lock Resource A
    Lock Resource B
Unlock Shared Resource Z
...

Kode Blok B (kode semu)

Lock Shared Resource Z
    Lock Resource B
    Lock Resource A
Unlock Shared Resource Z
...

tidak lupa untuk membuka kunci A dan B ketika selesai dengan mereka

ini akan mencegah kebuntuan antara blok kode A dan blok kode B

Dari perspektif basis data, saya tidak yakin bagaimana cara mencegah situasi ini, karena kunci ditangani oleh basis data itu sendiri, yaitu baris/tabel mengunci saat memperbarui data. Di mana saya telah melihat sebagian besar masalah terjadi adalah di mana Anda melihat masalah Anda, di dalam kursor. Kursor sangat tidak efisien, hindari jika memungkinkan.

39
BlackICE

artikel favorit saya untuk membaca dan mempelajari tentang kebuntuan adalah: Pembicaraan Sederhana - Melacak kebuntuan dan SQL Server Central - Menggunakan Profiler untuk menyelesaikan kebuntuan . Mereka akan memberi Anda sampel dan saran tentang cara menangani situasi yang sulit.

Singkatnya, untuk memecahkan masalah saat ini, saya akan membuat transaksi yang terlibat lebih pendek, mengambil bagian yang tidak dibutuhkan dari mereka, mengurus urutan penggunaan objek, melihat tingkat isolasi apa yang sebenarnya dibutuhkan, tidak membaca yang tidak dibutuhkan data...

Tetapi lebih baik membaca artikel, mereka akan jauh lebih baik dalam saran.

24
Marian

Kadang-kadang jalan buntu dapat diselesaikan dengan menambahkan pengindeksan, karena memungkinkan database untuk mengunci catatan individu daripada seluruh tabel, sehingga Anda mengurangi pertengkaran dan kemungkinan hal-hal menjadi macet.

Misalnya, dalam InnoDB :

Jika Anda tidak memiliki indeks yang cocok untuk pernyataan Anda dan MySQL harus memindai seluruh tabel untuk memproses pernyataan, setiap baris tabel menjadi terkunci, yang pada gilirannya memblokir semua sisipan oleh pengguna lain ke tabel. Penting untuk membuat indeks yang baik sehingga permintaan Anda tidak perlu memindai banyak baris secara tidak perlu.

Solusi umum lainnya adalah mematikan konsistensi transaksional ketika tidak diperlukan, atau mengubah tingkat isolasi Anda , misalnya, pekerjaan jangka panjang untuk menghitung statistik ... jawaban dekat umumnya cukup, Anda tidak perlu angka yang tepat, karena mereka berubah dari bawah Anda. Dan jika perlu 30 menit untuk menyelesaikannya, Anda tidak ingin itu menghentikan semua transaksi lain pada tabel tersebut.

...

Adapun untuk melacak mereka, itu tergantung pada perangkat lunak database yang Anda gunakan.

16
Joe

Hanya untuk mengembangkan pada hal kursor. ini memang sangat buruk. Itu mengunci seluruh tabel kemudian memproses baris satu per satu.

Yang terbaik adalah melalui baris dengan cara kursor menggunakan loop sementara

Dalam loop sementara, pilih akan dilakukan untuk setiap baris dalam loop dan kunci akan terjadi hanya pada satu baris pada saat itu. Sisa data dalam tabel gratis untuk kueri, sehingga mengurangi kemungkinan kebuntuan terjadi.

Plus lebih cepat. Membuat Anda bertanya-tanya mengapa ada kursor.

Berikut contoh struktur semacam ini:

DECLARE @LastID INT = (SELECT MAX(ID) FROM Tbl)
DECLARE @ID     INT = (SELECT MIN(ID) FROM Tbl)
WHILE @ID <= @LastID
    BEGIN
    IF EXISTS (SELECT * FROM Tbl WHERE ID = @ID)
        BEGIN
        -- Do something to this row of the table
        END

    SET @ID += 1  -- Don't forget this part!
    END

Jika bidang ID Anda jarang, Anda mungkin ingin menarik daftar ID terpisah dan beralih melalui itu:

DECLARE @IDs TABLE
    (
    Seq INT NOT NULL IDENTITY PRIMARY KEY,
    ID  INT NOT NULL
    )
INSERT INTO @IDs (ID)
    SELECT ID
    FROM Tbl
    WHERE 1=1  -- Criteria here

DECLARE @Rec     INT = 1
DECLARE @NumRecs INT = (SELECT MAX(Seq) FROM @IDs)
DECLARE @ID      INT
WHILE @Rec <= @NumRecs
    BEGIN
    SET @ID = (SELECT ID FROM @IDs WHERE Seq = @Seq)

    -- Do something to this row of the table

    SET @Seq += 1  -- Don't forget this part!
    END
7

Kunci primer yang hilang adalah tidak masalahnya. Setidaknya dengan sendirinya. Pertama, Anda tidak perlu primer untuk memiliki indeks. Kedua, bahkan jika Anda melakukan pemindaian tabel (yang harus terjadi jika permintaan khusus Anda tidak menggunakan indeks, kunci tabel tidak akan dengan sendirinya menyebabkan kebuntuan. Proses penulisan akan menunggu untuk dibaca, dan proses membaca akan menunggu untuk menulis, dan tentu saja membaca tidak harus menunggu satu sama lain.

Menambah jawaban lain, Tingkat isolasi transaksi penting, karena pembacaan berulang dan serial adalah penyebab kunci 'baca' ditahan hingga akhir transaksi. Mengunci sumber daya tidak menyebabkan jalan buntu. Tetap terkunci tidak. Operasi tulis selalu menjaga sumber dayanya terkunci sampai akhir transaksi.

Strategi pencegahan kunci favorit saya adalah menggunakan fitur 'snapshot'. Fitur Baca Mengkomit Snapshot berarti membaca tidak menggunakan kunci! Dan jika Anda memerlukan lebih banyak kontrol daripada 'Komitmen baca', ada fitur 'Level isolasi pengambilan gambar'. Yang ini memungkinkan transaksi berseri (menggunakan istilah MS di sini) terjadi tanpa memblokir pemain lain.

Terakhir, satu kelas deadlock dapat dicegah dengan menggunakan kunci pembaruan. Jika Anda membaca dan menahan read (HOLD, atau menggunakan Repeatable Read), dan proses lain melakukan hal yang sama, maka keduanya mencoba memperbarui catatan yang sama, Anda akan menemui jalan buntu. Tetapi jika keduanya meminta kunci pembaruan, proses kedua akan menunggu yang pertama, sementara memungkinkan proses lain untuk membaca data menggunakan kunci bersama sampai data benar-benar ditulis. Ini tentu saja tidak akan berfungsi jika salah satu proses masih meminta kunci HOLD bersama.

6
Gerard ONeill