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
([TimeStamp] DESC, [InverterID] ASC)
terlihat seperti pilihan yang aneh untuk indeks berkerumun. MaksudkuDESC
bagian itu.