Kami berusaha memperbarui / menghapus sejumlah besar catatan dalam tabel baris multi-miliar. Karena ini adalah tabel yang populer, ada banyak aktivitas di berbagai bagian tabel ini. Setiap aktivitas pembaruan / penghapusan besar sedang diblokir untuk periode waktu yang lama (karena menunggu untuk mendapatkan kunci pada semua baris atau kunci halaman atau kunci tabel) yang mengakibatkan timeout atau mengambil beberapa hari untuk menyelesaikan tugas.
Jadi, kami mengubah pendekatan untuk menghapus sejumlah kecil baris sekaligus. Tetapi kami ingin memeriksa apakah baris yang dipilih (katakanlah 100 atau 1000 atau 2000) saat ini dikunci oleh proses yang berbeda atau tidak.
- Jika tidak, maka lanjutkan dengan menghapus / memperbarui.
- Jika dikunci, maka pindah ke grup catatan berikutnya.
- Pada akhirnya, kembali ke awal dan berusaha memperbarui / menghapus yang ditinggalkan.
Apakah ini bisa dilakukan?
Terima kasih, ToC
Jawaban:
Jika saya memahami permintaan dengan benar, tujuannya adalah untuk menghapus kumpulan baris, sementara pada saat yang sama, operasi DML terjadi pada baris di seluruh tabel. Tujuannya adalah untuk menghapus kumpulan; namun, jika ada baris mendasar yang terkandung dalam kisaran yang ditentukan oleh kumpulan tersebut dikunci, maka kita harus melewati kumpulan itu dan pindah ke kumpulan berikutnya. Kami kemudian harus kembali ke kumpulan mana pun yang sebelumnya tidak dihapus dan coba lagi logika hapus asli kami. Kita harus mengulangi siklus ini sampai semua kumpulan baris yang diperlukan dihapus.
Seperti yang telah disebutkan, masuk akal untuk menggunakan petunjuk READPAST dan tingkat isolasi BACA (default), untuk melewati rentang masa lalu yang mungkin berisi baris yang diblokir. Saya akan melangkah lebih jauh dan merekomendasikan menggunakan tingkat isolasi SERIALIZABLE dan menggigit menghapus.
SQL Server menggunakan kunci Rentang Kunci untuk melindungi serangkaian baris yang secara implisit termasuk dalam kumpulan catatan yang sedang dibaca oleh pernyataan Transact-SQL saat menggunakan tingkat isolasi transaksi yang dapat disesuaikan ... temukan lebih lanjut di sini: https://technet.microsoft.com /en-US/library/ms191272(v=SQL.105).aspx
Dengan menggigit menghapus, tujuan kami adalah untuk mengisolasi berbagai baris dan memastikan bahwa tidak ada perubahan yang terjadi pada baris tersebut saat kami menghapusnya, artinya, kami tidak ingin pembacaan atau penyisipan phantom. Tingkat isolasi serializable dimaksudkan untuk menyelesaikan masalah ini.
Sebelum saya mendemonstrasikan solusi saya, saya ingin menambahkan bahwa saya juga tidak merekomendasikan untuk beralih tingkat isolasi standar database Anda ke SERIALIZABLE juga saya merekomendasikan solusi saya adalah yang terbaik. Saya hanya ingin menyampaikannya dan melihat ke mana kita bisa pergi dari sini.
Beberapa catatan perawatan rumah:
Untuk memulai percobaan saya, saya akan membuat basis data pengujian, tabel sampel, dan saya akan mengisi tabel dengan 2.000.000 baris.
Pada titik ini, kita akan memerlukan satu atau lebih indeks di mana mekanisme penguncian tingkat isolasi SERIALIZABLE dapat bertindak.
Sekarang, mari kita periksa untuk melihat apakah 2.000.000 baris kita telah dibuat
Jadi, kami memiliki basis data, tabel, indeks, dan baris kami. Jadi, mari kita siapkan percobaan untuk menghapus gigitan. Pertama, kita harus memutuskan cara terbaik untuk membuat mekanisme penghapusan yang biasa-biasa saja.
Seperti yang Anda lihat, saya menempatkan transaksi eksplisit di dalam loop sementara. Jika Anda ingin membatasi flush log, silakan letakkan di luar loop. Selain itu, karena kita berada dalam model pemulihan LENGKAP, Anda mungkin ingin membuat cadangan log transaksi lebih sering saat menjalankan operasi penghapusan gigitan Anda, untuk memastikan bahwa log transaksi Anda dapat dicegah agar tidak bertambah besar.
Jadi, saya punya beberapa tujuan dengan pengaturan ini. Pertama, saya ingin kunci rentang kunci saya; jadi, saya mencoba untuk menjaga batch sekecil mungkin. Saya juga tidak ingin berdampak negatif pada konkurensi di atas meja "raksasa" saya; jadi, saya ingin mengambil kunci saya dan meninggalkannya secepat mungkin. Jadi, saya sarankan Anda membuat ukuran batch Anda kecil.
Sekarang, saya ingin memberikan contoh singkat tentang tindakan penghapusan rutin ini. Kami harus membuka jendela baru dalam SSMS dan menghapus satu baris dari tabel kami. Saya akan melakukan ini dalam transaksi implisit menggunakan tingkat isolasi BACA KOMITMEN standar.
Apakah baris ini benar-benar dihapus?
Ya, sudah dihapus.
Sekarang, untuk melihat kunci kami, mari kita buka jendela baru dalam SSMS dan tambahkan satu atau dua potongan kode. Saya menggunakan sp_whoisactive dari Adam Mechanic, yang dapat ditemukan di sini: sp_whoisactive
Sekarang, kita siap untuk memulai. Di jendela SSMS baru, mari kita mulai transaksi eksplisit yang akan mencoba memasukkan kembali satu baris yang kita hapus. Pada saat yang sama, kami akan mematikan operasi penghapusan gigitan kami.
Kode yang dimasukkan:
Mari kita mulai kedua operasi yang dimulai dengan sisipan dan diikuti oleh penghapusan kita. Kita dapat melihat kunci rentang kunci dan kunci eksklusif.
Sisipan menghasilkan kunci-kunci ini:
The nibbling delete / select memegang kunci-kunci ini:
Sisipan kami mencekal penghapusan kami seperti yang diharapkan:
Sekarang, mari kita lakukan transaksi insert dan lihat apa yang terjadi.
Dan seperti yang diharapkan, semua transaksi selesai. Sekarang, kita harus memeriksa untuk melihat apakah sisipan itu adalah hantu atau apakah operasi penghapusan juga menghapusnya.
Bahkan, sisipan telah dihapus; jadi, tidak ada sisipan hantu yang diizinkan.
Jadi, sebagai kesimpulan, saya pikir maksud sebenarnya dari latihan ini bukan untuk mencoba dan melacak setiap baris, halaman, atau kunci level-tabel dan mencoba untuk menentukan apakah elemen batch dikunci dan karenanya akan memerlukan operasi penghapusan kami untuk Tunggu. Itu mungkin maksud para penanya; Namun, tugas itu sangat besar dan pada dasarnya tidak praktis jika tidak mustahil. Tujuan sebenarnya adalah untuk memastikan bahwa tidak ada fenomena yang tidak diinginkan muncul setelah kami mengisolasi kisaran batch kami dengan kunci kami sendiri dan kemudian mendahului untuk menghapus batch. Level isolasi SERIALIZABLE mencapai tujuan ini. Kuncinya adalah untuk menjaga camilan Anda tetap kecil, log transaksi Anda terkendali, dan menghilangkan fenomena yang tidak diinginkan.
Jika Anda menginginkan kecepatan, maka jangan buat tabel raksasa yang tidak dapat dipartisi dan oleh karena itu tidak dapat menggunakan pengalihan partisi untuk hasil tercepat. Kunci kecepatan adalah partisi dan paralelisme; kunci penderitaan adalah mengunyah dan mengunci hidup.
Tolong beritahu saya bagaimana menurut anda.
Saya membuat beberapa contoh lebih lanjut dari tingkat isolasi SERIALIZABLE dalam aksi. Mereka harus tersedia di tautan di bawah ini.
Hapus Operasi
Masukkan Operasi
Operasi Kesetaraan - Kunci-Rentang Kunci pada Nilai-nilai Kunci Berikutnya
Operasi Kesetaraan - Pengambilan Singleton dari Data yang Ada
Operasi Kesetaraan - Pengambilan Singleton dari Data Tidak Ada
Operasi Ketimpangan - Kunci-Rentang Kunci pada Rentang dan Nilai-nilai Kunci Berikutnya
sumber
Ini adalah ide yang sangat bagus untuk dihapus dalam kumpulan atau potongan kecil yang cermat . Saya akan menambahkan kecil
waitfor delay '00:00:05'
dan tergantung pada model pemulihan dari database - jikaFULL
, maka lakukanlog backup
dan jikaSIMPLE
kemudian lakukanmanual CHECKPOINT
untuk menghindari kembung dari transaksi log - antar batch.Apa yang Anda katakan tidak sepenuhnya mungkin di luar kotak (ingat 3 poin utama Anda). Jika saran di atas -
small batches + waitfor delay
tidak berfungsi (asalkan Anda melakukan pengujian yang tepat), maka Anda dapat menggunakanquery HINT
.Jangan gunakan
NOLOCK
- lihat kb / 308886 , SQL Server Read-Consistency Problem oleh Itzik Ben-Gan , Menempatkan NOLOCK di mana-mana - Oleh Aaron Bertrand dan SQL Server NOLOCK Petunjuk & ide-ide buruk lainnya .READPAST
petunjuk akan membantu dalam skenario Anda. Inti dariREADPAST
petunjuk adalah - jika ada kunci tingkat baris maka SQL server tidak akan membacanya.Selama pengujian terbatas saya, saya menemukan throughput yang sangat bagus ketika menggunakan
DELETE from schema.tableName with (READPAST, READCOMMITTEDLOCK)
dan mengatur tingkat isolasi sesi permintaan untukREAD COMMITTED
menggunakanSET TRANSACTION ISOLATION LEVEL READ COMMITTED
yang tingkat isolasi standar pula.sumber
Merangkum pendekatan-pendekatan lain yang semula ditawarkan dalam komentar atas pertanyaan.
Gunakan
NOWAIT
jika perilaku yang diinginkan adalah untuk gagal seluruh potongan segera setelah kunci yang tidak kompatibel ditemukan.Dari
NOWAIT
dokumentasi :Gunakan
SET LOCK_TIMEOUT
untuk mencapai hasil yang serupa, tetapi dengan batas waktu yang dapat dikonfigurasi:Dari
SET LOCK_TIMEOUT
dokumentasisumber
Untuk mengasumsikan kami memiliki 2 permintaan paralel:
terhubung / sesi 1: akan mengunci baris = 777
menghubungkan / sesi 2: akan mengabaikan baris terkunci = 777
ATAU hubungkan / sesi 2: akan melempar pengecualian
sumber
Cobalah memfilter sesuatu seperti ini - ini bisa menjadi rumit jika Anda ingin benar-benar spesifik. Lihat di BOL untuk deskripsi sys.dm_tran_locks
sumber
Anda dapat menggunakan NoLOCK saat Anda menghapus dan jika baris dikunci, mereka tidak akan dihapus. Ini tidak ideal tetapi dapat melakukan trik untuk Anda.
sumber
Msg 1065, Level 15, State 1, Line 15 The NOLOCK and READUNCOMMITTED lock hints are not allowed for target tables of INSERT, UPDATE, DELETE or MERGE statements.
, saya sudah tidak digunakan lagi sejak 2005