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 masalah 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 saya mencegah dan melacak kebuntuan?
sumber
Jawaban:
melacak kebuntuan adalah yang lebih mudah dari keduanya:
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 memiliki jalan buntu.
Untuk mencegah kondisi ini, Anda dapat melakukan sesuatu seperti berikut ini
Kode Blok A (kode psuedo)
Kode Blok B (kode semu)
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.
sumber
artikel favorit saya untuk membaca dan belajar tentang kebuntuan adalah: Bicara Sederhana - Lacak 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 menyelesaikan masalah saat ini, saya akan membuat transaksi menjadi lebih pendek, mengambil bagian yang tidak dibutuhkan dari mereka, mengurus urutan penggunaan objek, melihat tingkat isolasi apa yang sebenarnya diperlukan, tidak membaca yang tidak dibutuhkan data...
Tetapi lebih baik membaca artikel, mereka akan jauh lebih baik dalam saran.
sumber
Kadang-kadang jalan buntu dapat diselesaikan dengan menambahkan pengindeksan, karena memungkinkan database untuk mengunci catatan individual daripada seluruh tabel, sehingga Anda mengurangi pertengkaran dan kemungkinan hal-hal menjadi macet.
Misalnya, dalam InnoDB :
Solusi umum lainnya adalah mematikan konsistensi transaksional ketika tidak diperlukan, atau mengubah tingkat isolasi Anda , misalnya, pekerjaan jangka panjang untuk menghitung statistik ... jawaban yang 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.
sumber
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 ini contoh struktur semacam ini:
Jika bidang ID Anda jarang, Anda mungkin ingin menarik daftar ID terpisah dan beralih melalui itu:
sumber
Kehilangan kunci primer bukan 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 tulisan, dan tentu saja membaca tidak harus menunggu satu sama lain sama sekali.
Menambah jawaban lain, Tingkat isolasi transaksi penting, karena pembacaan berulang dan serial adalah penyebab kunci 'baca' ditahan sampai 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 serial (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.
sumber
Sementara kursor lambat dalam SQL Server, Anda bisa menghindari kebuntuan dalam kursor dengan menarik data sumber untuk kursor ke dalam tabel Temp dan menjalankan kursor di atasnya. Ini menjaga kursor dari mengunci tabel data aktual dan satu-satunya kunci yang Anda dapatkan adalah untuk pembaruan atau sisipan yang dilakukan di dalam kursor yang hanya ditahan selama durasi penyisipan / pembaruan dan bukan untuk durasi kursor.
sumber