Ich habe ein Problem mit einer großen Anzahl von INSERTs, die meine SELECT-Operationen blockieren.
Schema
Ich habe einen Tisch wie diesen:
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)
)
Ich habe auch diese kleine Hilfsprozedur, die es mir ermöglicht, mit dem Befehl MERGE einzufügen oder zu aktualisieren (bei Konflikt zu aktualisieren):
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
Verwendung
Ich habe jetzt Dienstinstanzen auf mehreren Servern ausgeführt, die massive Aktualisierungen durchführen, indem sie die [InsertOrUpdateInverterData]
Prozedur schnell aufrufen .
Es gibt auch eine Website, die SELECT-Abfragen für die [InverterData]
Tabelle ausführt .
Problem
Wenn ich SELECT-Abfragen für die [InverterData]
Tabelle durchführe, werden sie in unterschiedlichen Zeiträumen abgearbeitet, abhängig von der INSERT-Verwendung meiner Dienstinstanzen. Wenn ich alle Dienstinstanzen pausiere, ist das SELECT blitzschnell, wenn die Instanz schnell einfügt, werden die SELECTs sehr langsam oder es kommt sogar zu einem Timeout-Abbruch.
Versuche
Ich habe einige SELECTs auf dem [sys.dm_tran_locks]
Tisch durchgeführt, um Sperrprozesse wie diesen zu finden
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
Das ist das Ergebnis:
S = geteilt. Die Wartesitzung erhält gemeinsamen Zugriff auf die Ressource.
Frage
Warum werden die SELECTs durch die [InsertOrUpdateInverterData]
Prozedur blockiert, die nur MERGE-Befehle verwendet?
Muss ich eine Transaktion mit definiertem Isolationsmodus innerhalb von verwenden [InsertOrUpdateInverterData]
?
Update 1 (im Zusammenhang mit der Frage von @Paul)
Basis für interne MS-SQL Server-Berichte zu [InsertOrUpdateInverterData]
folgenden Statistiken:
- Durchschnittliche CPU-Zeit: 0.12ms
- Durchschnittliche Lesevorgänge: 5,76 pro / s
- Durchschnittliche Schreibvorgänge: 0,4 pro / s
Auf dieser Basis sieht es so aus, als ob der Befehl MERGE hauptsächlich mit Leseoperationen beschäftigt ist, die die Tabelle sperren! (?)
Update 2 (im Zusammenhang mit der Frage von @Paul)
Die [InverterData]
Tabelle hat folgende Speicherstatistiken:
- Datenraum: 26.901,86 MB
- Zeilenanzahl: 131.827.749
- Aufgeteilt: wahr
- Anzahl der Partitionen: 62
Hier ist die (fast) vollständige Ergebnismenge von sp_WhoIsActive :
SELECT
Befehl
- TT HH: MM: SS: 00 00: 01: 01.930
- session_id: 73
- wait_info: (12629ms) LCK_M_S
- CPU: 198
- Blocking_Session_ID: 146
- liest: 99,368
- schreibt: 0
- Status: ausgesetzt
- open_tran_count: 0
[InsertOrUpdateInverterData]
Blockierbefehl
- TT HH: MM: SS: 00 00: 00: 00.330
- session_id: 146
- wait_info: NULL
- CPU: 3.972
- blocking_session_id: NULL
- liest: 376,95
- schreibt: 126
- Status: schlafen
- open_tran_count: 1
([TimeStamp] DESC, [InverterID] ASC)
sieht nach einer seltsamen Wahl für den Clustered-Index aus. Ich meine dasDESC
Teil.