Saya memiliki masalah dengan sejumlah besar INSERT yang memblokir operasi SELECT saya.
Skema
Saya punya tabel seperti ini:
CREATE TABLE [InverterData](
[InverterID] [bigint] NOT NULL,
[TimeStamp] [datetime] NOT NULL,
[ValueA] [decimal](18, 2) NULL,
[ValueB] [decimal](18, 2) NULL
CONSTRAINT [PrimaryKey_e149e28f-5754-4229-be01-65fafeebce16] PRIMARY KEY CLUSTERED
(
[TimeStamp] DESC,
[InverterID] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF
, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON
, ALLOW_PAGE_LOCKS = ON)
)
Saya juga memiliki prosedur pembantu kecil ini, yang memungkinkan saya untuk memasukkan atau memperbarui (pembaruan saat konflik) dengan perintah MERGE:
CREATE PROCEDURE [InsertOrUpdateInverterData]
@InverterID bigint, @TimeStamp datetime
, @ValueA decimal(18,2), @ValueB decimal(18,2)
AS
BEGIN
MERGE [InverterData] AS TARGET
USING (VALUES (@InverterID, @TimeStamp, @ValueA, @ValueB))
AS SOURCE ([InverterID], [TimeStamp], [ValueA], [ValueB])
ON TARGET.[InverterID] = @InverterID AND TARGET.[TimeStamp] = @TimeStamp
WHEN MATCHED THEN
UPDATE
SET [ValueA] = SOURCE.[ValueA], [ValueB] = SOURCE.[ValueB]
WHEN NOT MATCHED THEN
INSERT ([InverterID], [TimeStamp], [ValueA], [ValueB])
VALUES (SOURCE.[InverterID], SOURCE.[TimeStamp], SOURCE.[ValueA], SOURCE.[ValueB]);
END
Pemakaian
Saya sekarang telah menjalankan instance layanan pada beberapa server yang melakukan pembaruan besar-besaran dengan memanggil [InsertOrUpdateInverterData]
prosedur dengan cepat.
Ada juga situs web yang melakukan kueri SELECT di atas [InverterData]
meja.
Masalah
Jika saya melakukan SELECT kueri di atas [InverterData]
meja, mereka diproses dalam rentang waktu yang berbeda, tergantung pada penggunaan INSERT dari layanan saya. Jika saya menjeda semua instance layanan SELECT sangat cepat, jika instance melakukan insert cepat, SELECT menjadi sangat lambat atau bahkan membatalkan waktu tunggu.
Mencoba
Saya sudah melakukan beberapa SELECT di atas [sys.dm_tran_locks]
meja untuk menemukan proses penguncian, seperti ini
SELECT
tl.request_session_id,
wt.blocking_session_id,
OBJECT_NAME(p.OBJECT_ID) BlockedObjectName,
h1.TEXT AS RequestingText,
h2.TEXT AS BlockingText,
tl.request_mode
FROM sys.dm_tran_locks AS tl
INNER JOIN sys.dm_os_waiting_tasks AS wt ON tl.lock_owner_address = wt.resource_address
INNER JOIN sys.partitions AS p ON p.hobt_id = tl.resource_associated_entity_id
INNER JOIN sys.dm_exec_connections ec1 ON ec1.session_id = tl.request_session_id
INNER JOIN sys.dm_exec_connections ec2 ON ec2.session_id = wt.blocking_session_id
CROSS APPLY sys.dm_exec_sql_text(ec1.most_recent_sql_handle) AS h1
CROSS APPLY sys.dm_exec_sql_text(ec2.most_recent_sql_handle) AS h2
Ini hasilnya:
S = Dibagikan. Sesi holding diberikan akses bersama ke sumber daya.
Pertanyaan
Mengapa SELECT diblokir oleh [InsertOrUpdateInverterData]
prosedur yang hanya menggunakan perintah MERGE?
Apakah saya harus menggunakan semacam transaksi dengan mode isolasi yang ditentukan di dalam [InsertOrUpdateInverterData]
?
Pembaruan 1 (terkait dengan pertanyaan dari @Paul)
Berdasarkan pelaporan internal MS-SQL server tentang [InsertOrUpdateInverterData]
statistik berikut:
- Waktu CPU Rata-rata: 0.12 ms
- Proses Baca Rata-rata: 5.76 per / s
- Rata-rata proses Menulis: 0,4 per / s
Berdasarkan ini sepertinya perintah MERGE sebagian besar sibuk dengan operasi pembacaan yang akan mengunci tabel! (?)
Pembaruan 2 (terkait dengan pertanyaan dari @Paul)
The [InverterData]
tabel seperti yang berikut statistik penyimpanan:
- Ruang data: 26.901,86 MB
- Jumlah baris: 131.827.749
- Dipartisi: benar
- Jumlah partisi: 62
Berikut adalah set hasil sp_WhoIsActive lengkap (allmost) :
SELECT
perintah
- dh jj: mm: ss.mss: 00 00: 01: 01.930
- session_id: 73
- wait_info: (12629ms) LCK_M_S
- CPU: 198
- blocking_session_id: 146
- berbunyi: 99.368
- menulis: 0
- status: ditangguhkan
- open_tran_count: 0
[InsertOrUpdateInverterData]
Perintah pemblokiran
- dh jj: mm: ss.mss: 00 00: 00: 00.330
- session_id: 146
- wait_info: NULL
- CPU: 3.972
- blocking_session_id: NULL
- berbunyi: 376,95
- menulis: 126
- status: tidur
- open_tran_count: 1
sumber
([TimeStamp] DESC, [InverterID] ASC)
terlihat seperti pilihan yang aneh untuk indeks berkerumun. MaksudkuDESC
bagian itu.Jawaban:
Pertama, meskipun sedikit tidak terkait dengan pertanyaan utama, Anda
MERGE
pernyataan berpotensi berisiko kesalahan karena kondisi balapan . Masalahnya, singkatnya, adalah mungkin untuk beberapa utas konkuren untuk menyimpulkan bahwa baris target tidak ada, menghasilkan upaya bertabrakan untuk memasukkan. Akar penyebabnya adalah bahwa tidak mungkin untuk mengambil kunci bersama atau memperbarui pada baris yang tidak ada. Solusinya adalah menambahkan petunjuk:The tingkat isolasi serializable petunjuk memastikan kisaran utama dimana baris akan dikunci. Anda memiliki indeks unik untuk mendukung penguncian rentang, jadi petunjuk ini tidak akan memiliki efek buruk pada penguncian, Anda hanya akan mendapatkan perlindungan terhadap kondisi lomba potensial ini.
Pertanyaan utama
Di bawah standar penguncian baca komitmen terisolasi, kunci (S) bersama diambil saat membaca data, dan biasanya (meskipun tidak selalu) dilepaskan segera setelah pembacaan selesai. Beberapa kunci bersama ditahan di akhir pernyataan.
SEBUAH
MERGE
memodifikasi pernyataan data, sehingga akan memperoleh S atau memperbarui (U) kunci ketika mencari data untuk perubahan, yang dikonversi ke eksklusif (X) kunci sebelum melakukan modifikasi yang sebenarnya. Baik kunci U dan X harus ditahan hingga akhir transaksi.Ini benar di bawah semua level isolasi kecuali isolasi snapshot (SI) optimis ' tidak - dikacaukan dengan versi yang dilakukan, juga dikenal sebagai read berkomitmen snapshot isolation (RCSI).
Tidak ada dalam pertanyaan Anda yang menunjukkan sesi menunggu kunci S diblokir oleh sesi memegang kunci U. Kunci-kunci ini kompatibel . Setiap pemblokiran hampir pasti disebabkan oleh pemblokiran pada kunci X yang ditahan. Ini bisa agak sulit untuk ditangkap ketika sejumlah besar kunci jangka pendek diambil, dikonversi, dan dirilis dalam interval waktu singkat.
Itu
open_tran_count: 1
pada InsertOrUpdateInverterData layak diselidiki. Meskipun perintah belum berjalan sangat lama, Anda harus memeriksa bahwa Anda tidak memiliki transaksi yang berisi (dalam aplikasi atau prosedur tersimpan tingkat yang lebih tinggi) yang terlalu panjang. Praktik terbaik adalah menjaga transaksi sesingkat mungkin. Ini mungkin bukan apa-apa, tetapi Anda harus memeriksa.Solusi potensial
Seperti yang disarankan Kin dalam komentar, Anda bisa mengaktifkan level isolasi versi baris (RCSI atau SI) pada database ini. RCSI adalah yang paling sering digunakan, karena biasanya tidak memerlukan banyak perubahan aplikasi. Setelah diaktifkan, level isolasi komitmen baca default menggunakan versi baris alih-alih mengambil kunci S untuk dibaca, sehingga pemblokiran SX dikurangi atau dihilangkan. Beberapa operasi (mis. Pemeriksaan kunci asing) masih memperoleh kunci S di bawah RCSI.
Sadarilah bahwa versi baris mengkonsumsi ruang tempdb, secara umum proporsional dengan tingkat aktivitas perubahan dan lamanya transaksi. Anda perlu menguji implementasi Anda secara menyeluruh di bawah beban untuk memahami dan merencanakan dampak RCSI (atau SI) dalam kasus Anda.
Jika Anda ingin melokalisasi penggunaan versi Anda, daripada mengaktifkannya untuk seluruh beban kerja, SI mungkin masih menjadi pilihan yang lebih baik. Dengan menggunakan SI untuk transaksi baca, Anda akan menghindari pertentangan antara pembaca dan penulis, dengan biaya pembaca melihat versi baris sebelum setiap modifikasi bersamaan dimulai (lebih tepatnya, operasi baca di bawah SI akan selalu melihat status komitmen dari baris pada saat transaksi SI dimulai). Ada sedikit manfaat atau tidak sama sekali untuk menggunakan SI untuk transaksi penulisan, karena kunci tulis masih akan diambil, dan Anda harus menangani setiap konflik tulis. Kecuali jika itu yang Anda inginkan :)
Catatan: Tidak seperti RCSI (yang pernah diaktifkan berlaku untuk semua transaksi yang berjalan dengan komitmen baca), SI harus secara eksplisit diminta menggunakan
SET TRANSACTION ISOLATION SNAPSHOT;
.Perilaku halus yang bergantung pada pembaca yang memblokir penulis (termasuk dalam kode pemicu!) Menjadikan pengujian ini penting. Lihat seri artikel saya yang ditautkan dan Buku Daring untuk detailnya. Jika Anda memutuskan untuk RCSI, pastikan untuk meninjau Modifikasi Data di bawah Baca Komitmen Snapshot Khusus.
Akhirnya, Anda harus memastikan instance Anda ditambal ke SQL Server 2008 Paket Layanan 4.
sumber
Dengan rendah hati, saya tidak akan menggunakan penggabungan. Saya akan menggunakan IF Exists (UPDATE) ELSE (INSERT) - Anda memiliki kunci berkerumun dengan dua kolom yang Anda gunakan untuk mengidentifikasi baris sehingga ini adalah tes yang mudah.
Anda menyebutkan sisipan MASSIVE dan belum melakukan 1 dengan 1 ... memikirkan batching data dalam tabel pementasan dan menggunakan POWER OVERWHELMING SQL data mengatur daya untuk melakukan lebih dari 1 pembaruan / masukkan sekaligus? Seperti memiliki pengujian rutin untuk konten dalam tabel pementasan, dan meraih 10.000 teratas sekaligus, bukan 1 sekaligus ...
Saya akan melakukan sesuatu seperti ini di pembaruan saya
Anda mungkin dapat menjalankan beberapa pekerjaan untuk membuka kumpulan pembaruan, dan Anda akan membutuhkan pekerjaan terpisah yang menjalankan penghapusan menetes
untuk membersihkan meja pementasan.
sumber