web-dev-qa-db-fra.com

Comment savoir pourquoi le statut d'un spid est suspendu? Quelles ressources le spid attend-il?

Je lance EXEC sp_who2 78 et j'obtiens les résultats suivants:

results of sp_who2 for spid 78

Comment trouver pourquoi son statut est suspendu?

Ce processus est une INSERT lourde basée sur une requête coûteuse. Une grosse SELECT qui récupère les données de plusieurs tables et écrit quelques 3-4 millions de lignes dans une autre table.

Il n'y a pas de serrures/blocs.

La waittype à laquelle elle est liée est CXPACKET. ce que je peux comprendre, car il y a 9 78 comme vous pouvez le voir sur la photo ci-dessous.

Ce qui me préoccupe et ce que j'aimerais vraiment savoir, c'est pourquoi le numéro 1 de la SPID 78 est suspendu.

Je comprends que lorsque le statut de la variable SPID est suspendu, cela signifie que le processus attend une ressource et qu’il reprendra dès qu’il obtiendra sa ressource.

Comment puis-je trouver plus de détails à ce sujet? quelle ressource? pourquoi n'est-il pas disponible?

J'utilise beaucoup le code ci-dessous et ses variantes, mais puis-je faire autre chose pour savoir pourquoi la variable SPID est suspendue?

select * 
from sys.dm_exec_requests r
join sys.dm_os_tasks t on r.session_id = t.session_id
where r.session_id = 78

J'ai déjà utilisé sp_whoisactive. Le résultat obtenu pour ce spid78 est le suivant: (divisé en 3 images pour correspondre à l'écran)

enter image description here

30
marcello miorelli

SUSPENDED: Cela signifie que la demande n'est pas active actuellement car elle attend une ressource. La ressource peut être une E/S pour lire une page, une WAITit peut être une communication sur le réseau, ou elle attend un verrou ou un verrou. Il deviendra actif une fois la tâche attendue terminée. Par exemple, si la requête a envoyé une demande d'E/S pour lire les données d'une table complète tblStudents, cette tâche sera suspendue jusqu'à ce que l'E/S soit terminée. Une fois les E/S terminées (les données de la table tblStudents sont disponibles en mémoire), la requête est transférée dans la file d'attente RUNNABLE.

Par conséquent, s'il attend, consultez la colonne wait_type pour comprendre ce qu'il attend et résolvez-le en fonction du wait_time.

J'ai développé la procédure suivante qui m'aide avec ceci, elle inclut le WAIT_TYPE.

use master
go

CREATE PROCEDURE [dbo].[sp_radhe] 

AS
BEGIN

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

SELECT es.session_id AS session_id
,COALESCE(es.original_login_name, '') AS login_name
,COALESCE(es.Host_name,'') AS hostname
,COALESCE(es.last_request_end_time,es.last_request_start_time) AS last_batch
,es.status
,COALESCE(er.blocking_session_id,0) AS blocked_by
,COALESCE(er.wait_type,'MISCELLANEOUS') AS waittype
,COALESCE(er.wait_time,0) AS waittime
,COALESCE(er.last_wait_type,'MISCELLANEOUS') AS lastwaittype
,COALESCE(er.wait_resource,'') AS waitresource
,coalesce(db_name(er.database_id),'No Info') as dbid
,COALESCE(er.command,'AWAITING COMMAND') AS cmd
,sql_text=st.text
,transaction_isolation =
    CASE es.transaction_isolation_level
    WHEN 0 THEN 'Unspecified'
    WHEN 1 THEN 'Read Uncommitted'
    WHEN 2 THEN 'Read Committed'
    WHEN 3 THEN 'Repeatable'
    WHEN 4 THEN 'Serializable'
    WHEN 5 THEN 'Snapshot'
END
,COALESCE(es.cpu_time,0) 
    + COALESCE(er.cpu_time,0) AS cpu
,COALESCE(es.reads,0) 
    + COALESCE(es.writes,0) 
    + COALESCE(er.reads,0) 
    + COALESCE(er.writes,0) AS physical_io
,COALESCE(er.open_transaction_count,-1) AS open_tran
,COALESCE(es.program_name,'') AS program_name
,es.login_time
FROM sys.dm_exec_sessions es
    LEFT OUTER JOIN sys.dm_exec_connections ec ON es.session_id = ec.session_id
    LEFT OUTER JOIN sys.dm_exec_requests er ON es.session_id = er.session_id
    LEFT OUTER JOIN sys.server_principals sp ON es.security_id = sp.sid
    LEFT OUTER JOIN sys.dm_os_tasks ota ON es.session_id = ota.session_id
    LEFT OUTER JOIN sys.dm_os_threads oth ON ota.worker_address = oth.worker_address
    CROSS APPLY sys.dm_exec_sql_text(er.sql_handle) AS st
where es.is_user_process = 1 
  and es.session_id <> @@spid
ORDER BY es.session_id

end 

Cette requête ci-dessous peut également afficher des informations de base pour vous aider lorsque le spid est suspendu, en indiquant la ressource attendue par le spid.

SELECT  wt.session_id, 
    ot.task_state, 
    wt.wait_type, 
    wt.wait_duration_ms, 
    wt.blocking_session_id, 
    wt.resource_description, 
    es.[Host_name], 
    es.[program_name] 
FROM  sys.dm_os_waiting_tasks  wt  
INNER  JOIN sys.dm_os_tasks ot ON ot.task_address = wt.waiting_task_address 
INNER JOIN sys.dm_exec_sessions es ON es.session_id = wt.session_id 
WHERE es.is_user_process =  1 

S'il vous plaît voir l'image ci-dessous à titre d'exemple:

enter image description here

29
marcello miorelli

J'utilise sp_whoIsActive pour examiner ce type d'informations car il s'agit d'un outil gratuit prêt à l'emploi qui vous fournit de bonnes informations pour le dépannage des requêtes lentes:

Comment utiliser sp_WhoIsActive pour rechercher des requêtes SQL Server lentes

Avec cela, vous pouvez obtenir le texte de la requête, le plan utilisé, la ressource attendue par la requête, ce qui la bloque, ce qui la verrouille et bien plus encore.

Beaucoup plus facile que d'essayer de rouler le vôtre.

12
steoleary

Vous pouvez le résoudre avec les moyens suivants:

  1. Corrigez l'index de cluster.
  2. Utilisez des tables temporelles pour obtenir une partie de toutes les tables et les utiliser.

J'ai le même problème avec une table avec 400 000 000 lignes et j'utilise une table temporelle pour en obtenir une partie, puis j'utilise mes filtres et mon inner, car changer l'index n'était pas une option.

Quelques exemples:

--
--this is need be cause DECLARE @TEMPORAL are not well for a lot of data.
CREATE TABLE #TEMPORAL
(
    ID BIGINT,
    ID2 BIGINT,
    DATA1 DECIMAL,
    DATA2 DECIMAL
);

WITH TABLE1 AS
(
    SELECT
        L.ID,
        L.ID2,
        L.DATA
    FROM LARGEDATA L
    WHERE L.ID = 1
), WITH TABLE2 AS
(
    SELECT
        L.ID,
        L.ID2,
        L.DATA
    FROM LARGEDATA L
    WHERE L.ID = 2
) INSERT INTO #TEMPORAL SELECT
    T1.ID,
    T2.ID,
    T1.DATA,
    T2.DATA
FROM TABLE1 T1
    INNER JOIN TABLE2 T2
        ON T2.ID2 = T2.ID2;
--
--this take a lot of resources proces and time and be come a status suspend, this why i need a temporal table.
SELECT
    *
FROM #TEMPORAL T
WHERE T.DATA1 < T.DATA2
--
--IMPORTANT DROP THE TABLE.
DROP TABLE #TEMPORAL
0
Moises Conejo