Ho un problema con una grande quantità di INSERT che stanno bloccando le mie operazioni SELECT.
Schema
Ho un tavolo come questo:
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)
)
Ho anche questa piccola procedura di supporto, che mi consente di inserire o aggiornare (aggiornamento in caso di conflitto) con il comando 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
uso
Ora ho eseguito istanze di servizio su più server che eseguono enormi aggiornamenti chiamando [InsertOrUpdateInverterData]
rapidamente la procedura.
Esiste anche un sito Web che esegue query SELECT sulla [InverterData]
tabella.
Problema
Se eseguo SELEZIONA le query sul [InverterData]
tavolo, vengono eseguite in diversi intervalli di tempo, a seconda dell'utilizzo di INSERT delle mie istanze del servizio. Se metto in pausa tutte le istanze del servizio, SELECT è estremamente veloce, se l'istanza esegue un inserimento rapido, le SELECT diventano molto lente o addirittura un annullamento del timeout.
tentativi
Ho fatto alcuni SELECT sul [sys.dm_tran_locks]
tavolo per trovare processi di blocco, come questo
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
Questo è il risultato:
S = Condiviso. Alla sessione di mantenimento è concesso l'accesso condiviso alla risorsa.
Domanda
Perché i SELECT sono bloccati dalla [InsertOrUpdateInverterData]
procedura che utilizza solo i comandi MERGE?
Devo usare una sorta di transazione con modalità di isolamento definita all'interno di [InsertOrUpdateInverterData]
?
Aggiornamento 1 (correlato alla domanda di @Paul)
Basati sul reporting interno del server MS-SQL sulle [InsertOrUpdateInverterData]
seguenti statistiche:
- Tempo medio CPU: 0,12 ms
- Processi di lettura medi: 5,76 per / s
- Processi di scrittura medi: 0,4 per / s
Sulla base di questo sembra che il comando MERGE sia principalmente occupato con operazioni di lettura che bloccheranno la tabella! (?)
Aggiornamento 2 (correlato alla domanda di @Paul)
La [InverterData]
tabella contiene le seguenti statistiche di archiviazione:
- Spazio dati: 26.901,86 MB
- Numero di righe: 131.827.749
- Partizionato: vero
- Conteggio partizioni: 62
Ecco il set di risultati sp_WhoIsActive (quasi tutti) completo :
SELECT
comando
- gg hh: mm: ss.mss: 00 00: 01: 01.930
- session_id: 73
- wait_info: (12629ms) LCK_M_S
- CPU: 198
- blocking_session_id: 146
- legge: 99.368
- scrive: 0
- stato: sospeso
- open_tran_count: 0
[InsertOrUpdateInverterData]
Comando di blocco
- gg hh: mm: ss.mss: 00 00: 00: 00.330
- session_id: 146
- wait_info: NULL
- CPU: 3.972
- blocking_session_id: NULL
- legge: 376,95
- scrive: 126
- stato: dormire
- open_tran_count: 1
([TimeStamp] DESC, [InverterID] ASC)
si presenta come una scelta strana per l'indice cluster. Intendo laDESC
parte.