Estou com um problema com uma quantidade enorme de INSERTs que estão bloqueando minhas operações SELECT.
Esquema
Eu tenho uma tabela como esta:
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)
)
Eu também tenho esse pequeno procedimento auxiliar, que me permite inserir ou atualizar (atualização em conflito) com o 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
Agora, executei instâncias de serviço em vários servidores que executam atualizações massivas chamando o [InsertOrUpdateInverterData]
procedimento rapidamente.
Há também um site que faz consultas SELECT na [InverterData]
tabela.
Problema
Se eu fizer consultas SELECT na [InverterData]
tabela, elas serão realizadas em intervalos de tempo diferentes, dependendo do uso INSERT das minhas instâncias de serviço. Se eu pausar todas as instâncias de serviço, o SELECT é extremamente rápido, se a instância executar uma inserção rápida, os SELECTs ficarão muito lentos ou até um cancelamento de tempo limite.
Tentativas
Concluí alguns SELECTs na [sys.dm_tran_locks]
tabela para encontrar processos de bloqueio, como este
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
Este é o resultado:
S = compartilhado. A sessão de espera tem acesso compartilhado ao recurso.
Questão
Por que os SELECTs estão bloqueados pelo [InsertOrUpdateInverterData]
procedimento que está usando apenas os comandos MERGE?
Preciso usar algum tipo de transação com o modo de isolamento definido dentro de [InsertOrUpdateInverterData]
?
Atualização 1 (relacionada à pergunta de @Paul)
Baseie-se nos relatórios internos do servidor MS-SQL sobre as [InsertOrUpdateInverterData]
seguintes estatísticas:
- Tempo médio da CPU: 0,12ms
- Processos médios de leitura: 5,76 por / s
- Processos médios de gravação: 0,4 por / s
Com base nisso, parece que o comando MERGE está ocupado principalmente com operações de leitura que bloquearão a tabela! (?)
Atualização 2 (relacionada à pergunta de @Paul)
A [InverterData]
tabela possui as seguintes estatísticas de armazenamento:
- Espaço de dados: 26.901,86 MB
- Contagem de linhas: 131,827,749
- Particionado: true
- Contagem de partições: 62
Aqui está o conjunto de resultados sp_WhoIsActive ( máximo ) completo :
SELECT
comando
- dd hh: mm: ss.mss: 00 00: 01: 01.930
- session_id: 73
- wait_info: (12629ms) LCK_M_S
- CPU: 198
- blocking_session_id: 146
- lê: 99.368
- escreve: 0
- status: suspenso
- open_tran_count: 0
[InsertOrUpdateInverterData]
Comando de bloqueio
- dd hh: mm: ss.mss: 00 00: 00: 00.330
- session_id: 146
- wait_info: NULL
- CPU: 3.972
- blocking_session_id: NULL
- leituras: 376,95
- escreve: 126
- status: dormindo
- open_tran_count: 1
([TimeStamp] DESC, [InverterID] ASC)
parece uma escolha estranha para o índice agrupado. Eu quero dizer aDESC
parte.