J'ai un problème avec une quantité massive d'INSERT qui bloquent mes opérations SELECT.
Schéma
J'ai une table comme celle-ci:
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)
)
J'ai également cette petite procédure d'aide, qui me permet d'insérer ou de mettre à jour (mise à jour en cas de conflit) avec la commande 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
Usage
J'ai maintenant exécuté des instances de service sur plusieurs serveurs qui effectuent des mises à jour massives en appelant la [InsertOrUpdateInverterData]
procédure rapidement.
Il existe également un site Web qui effectue des requêtes SELECT sur [InverterData]
table.
Problème
Si je fais des requêtes SELECT sur le [InverterData]
table, elles sont traitées dans des intervalles de temps différents, en fonction de l'utilisation INSERT de mes instances de service. Si je mets en pause toutes les instances de service, le SELECT est ultra-rapide, si l'instance effectue une insertion rapide, les SELECT deviennent vraiment lents ou même une annulation de timeout.
Tentatives
J'ai fait quelques SELECT sur la [sys.dm_tran_locks]
table pour trouver des processus de verrouillage, comme celui-ci
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
Voici le résultat:
S = partagé. La session d'attente bénéficie d'un accès partagé à la ressource.
Question
Pourquoi les SELECT sont-ils bloqués par la [InsertOrUpdateInverterData]
procédure qui utilise uniquement les commandes MERGE?
Dois-je utiliser une sorte de transaction avec un mode d'isolement défini à l'intérieur de [InsertOrUpdateInverterData]
?
Mise à jour 1 (liée à la question de @Paul)
Base sur les rapports internes du serveur MS-SQL sur [InsertOrUpdateInverterData]
les statistiques suivantes:
- Temps CPU moyen: 0,12 ms
- Processus de lecture moyens: 5,76 par / s
- Processus d'écriture moyens: 0,4 par / s
Sur cette base, il semble que la commande MERGE soit principalement occupée à lire des opérations qui verrouillent la table! (?)
Mise à jour 2 (liée à la question de @Paul)
La [InverterData]
table a les statistiques de stockage suivantes:
- Espace de données: 26901,86 Mo
- Nombre de lignes: 131 827 749
- Partitionné: vrai
- Nombre de partitions: 62
Voici l' ensemble de résultats sp_WhoIsActive (le plus) complet :
SELECT
commander
- jj hh: mm: ss.mss: 00 00: 01: 01.930
- session_id: 73
- wait_info: (12629ms) LCK_M_S
- CPU: 198
- blocking_session_id: 146
- lit: 99,368
- écrit: 0
- statut: suspendu
- open_tran_count: 0
[InsertOrUpdateInverterData]
Commande de blocage
- jj hh: mm: ss.mss: 00 00: 00: 00.330
- session_id: 146
- wait_info: NULL
- Processeur: 3 972
- blocking_session_id: NULL
- lit: 376,95
- écrit: 126
- statut: dormir
- open_tran_count: 1
([TimeStamp] DESC, [InverterID] ASC)
ressemble à un choix étrange pour l'index clusterisé. Je veux dire laDESC
partie.