Archives pour la catégorie vues de gestion dynamique

optimize for adhoc workloads

SQL Server maintient un cache de plans d’exécution. Ces plans permettent au moteur de ne pas ré-optimiser les requêtes si elles sont exécutées une deuxième fois. C’est valable bien sûr pour les procédures stockées, mais aussi pour les requêtes ad-hoc. Le problème avec les requêtes ad-hoc (celles qui sont générées dans le code de l’application au lieu de faire un appel de procédure stockée), c’est que le plan concerne une requête, et non pas une requête paramétrée. En d’autres termes, ces deux requêtes :

SELECT * FROM dbo.Client WHERE Nom = 'MAUGHAN';
SELECT * FROM dbo.Client WHERE Nom = 'LLOYD';

Vont générer deux plans en mémoire (probablement identiques), parce que SQL Server ne paramétrise pas la requête automatiquement, car le plan pourrait être différent selon le nombre de valeur à retourner par paramètre.
Nous pouvons voir la taille occupée par ces plans qui ne sont utilisés qu’une fois à l’aide de la requête suivante :

SELECT SUM(CAST(cp.size_in_bytes as bigint)) / 1024 / 1024 as adhoc_once_mb
FROM sys.dm_exec_cached_plans AS cp
WHERE cp.cacheobjtype = N'Compiled Plan'
AND cp.objtype = N'Adhoc'
AND cp.usecounts = 1
OPTION (RECOMPILE);

Pour économiser cette mémoire, une option de l’instance disponible à partir de SQL Server 2008 peut être activée. Son nom est « optimize for adhoc workloads ». Vous la trouvez dans les propriétés de l’instance, ou vous pouvez la modifier comme suit :

SP_CONFIGURE 'show advanced options',1
RECONFIGURE
GO

SP_CONFIGURE 'optimize for ad hoc workloads',1
RECONFIGURE
GO

Cette option modifie le comportement de SQL Server de la manière suivante : lorsqu’un plan de requête ad-hoc est calculé, il n’est plus directement stocké dans le cache, seul l’est un résumé du plan (un stub) qui permettra de reconnaître le même plan ensuite. Si le même plan doit être calculé une deuxième fois, il sera cette fois gardé en cache.
Je vous recommande d’activer cette option systématiquement sur tous vos serveurs SQL.
Voici une requête synoptique pour vous rendre compte de la situation sur votre serveur:

SELECT
    cacheobjtype,
    objtype,
    'total' as [type],
    SUM(CAST(size_in_bytes as bigint)) / 1024 / 1014 as size_in_mb
FROM sys.dm_exec_cached_plans
GROUP BY cacheobjtype, objtype
UNION ALL
SELECT
    cacheobjtype,
    objtype,
    '1x',
    SUM(CAST(size_in_bytes as bigint)) / 1024 / 1014 as size_in_mb
FROM sys.dm_exec_cached_plans
WHERE usecounts = 1
GROUP BY cacheobjtype, objtype
ORDER BY cacheobjtype, objtype, [type];

Surveillance de l’activité disque

La requête suivante retourne l’activité disque enregistrée par SQL Server depuis le démarrage de l’instance, par fichier de base de données.

SELECT
    DB_NAME(mf.database_id) as db,
    mf.name,
    LEFT(mf.physical_name, 2) as disque,
    fs.num_of_reads, fs.num_of_bytes_read,
    fs.num_of_writes, fs.num_of_bytes_written,
    fs.io_stall, fs.io_stall_read_ms, fs.io_stall_write_ms
FROM sys.dm_io_virtual_file_stats(null,null) AS fs
INNER JOIN sys.master_files AS mf
ON fs.database_id = mf.database_id
AND fs.[file_id] = mf.[file_id]

informations de session

La requête suivante vous indique les sessions ouvertes sur SQL Server, avec nom de la machine client, le login, type d’authentification (SQL ou Windows), et le nom du programme tel qu’indiqué dans la chaîne de connexion.

SELECT
    c.session_id,
    s.login_name,
    CASE c.auth_scheme
        WHEN 'sql' THEN 'SQL'
        ELSE 'Windows'
    END as Authentification_mode,
    s.host_name,
    s.program_name
FROM sys.dm_exec_connections c
JOIN sys.dm_exec_sessions s ON c.session_id = s.session_id

voir les requêtes en cours d’exécution

Cette requête utilise la vue de gestion dynamique sys.dm_exec_requests pour lister les requêtes en cours d’exécution avec quelques informations utiles.

SELECT
    s.host_name,
    t.text,
    r.start_time,
    r.status,
    r.total_elapsed_time,
    r.logical_reads,
    r.granted_query_memory
FROM sys.dm_exec_requests r
JOIN sys.dm_exec_sessions s ON r.session_id = s.session_id
CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) t
WHERE s.session_id > 50;

recherche dans les plans en mémoire

Voici quelques requêtes permettant de faire des recherches dans les plans d’exécution gardés en mémoire dans le cache de plans. Ces plans sont représentés en XML, donc nous utilisons du XQuery pour effectuer les recherches.

Recherche des plans parallélisés :

SELECT TOP 10
p.*,
q.*,
qs.*,
cp.plan_handle
FROM
sys.dm_exec_cached_plans cp
CROSS apply sys.dm_exec_query_plan(cp.plan_handle) p
CROSS apply sys.dm_exec_sql_text(cp.plan_handle) AS q
JOIN sys.dm_exec_query_stats qs
ON qs.plan_handle = cp.plan_handle
WHERE
cp.cacheobjtype = 'Compiled Plan' AND
p.query_plan.value('declare namespace p="http://schemas.microsoft.com/sqlserver/2004/07/showplan";
max(//p:RelOp/@Parallel)', 'float') > 0
OPTION (MAXDOP 1)

Recherche de tous les plans qui utilisent un index:
inspiré de http://stackoverflow.com/questions/17572261/how-to-filter-xml-execution-plan-data-in-a-where-clause-using-tsql

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

DECLARE @IndexName nvarchar(100) = '[I_CDRRating_Status_SubscriberId_BillingPopulationId]';

WITH XMLNAMESPACES (default 'http://schemas.microsoft.com/sqlserver/2004/07/showplan')
SELECT OBJECT_NAME(qp.objectid, qp.dbid) as obj,
    st.text,
    cp.usecounts,
    cp.objtype,
    qp.query_plan.value('(//RelOp[IndexScan/Object/@Index = sql:variable("@IndexName")]/@PhysicalOp)[1]', 'varchar(50)') as usage,
    qp.query_plan
FROM sys.dm_exec_cached_plans cp
CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) qp
CROSS APPLY sys.dm_exec_sql_text(cp.plan_handle) st
where --cp.objtype = 'Proc' and
      qp.query_plan.exist('//RelOp[
                                  (@PhysicalOp = "Index Seek" or @PhysicalOp = "Index Scan") and
                                  IndexScan/Object/@Index = sql:variable("@IndexName")
                                  ]') = 1
ORDER BY cp.usecounts DESC
OPTION (MAXDOP 1);

Retrouver les index manquants dans les plans d’exécution en cache:
Un requête est disponible pour ce faire sur cette entrée de blog de Jason Strate.
http://www.jasonstrate.com/2010/12/can-you-dig-it-missing-indexes/

inspection des verrous posés sur une table

requête sur les vues de gestion dynamique pour voir quels verrous sont posés à l’instant, sur une table, en indiquant le code des requêtes qui verrouillent.

SELECT
    l.resource_type,
    l.resource_subtype,
    l.resource_lock_partition,
    l.request_mode,
    l.request_type,
    l.request_session_id,
    t.text
FROM sys.dm_tran_locks l
JOIN sys.dm_exec_requests r ON l.request_request_id = r.request_id
    AND l.request_session_id = r.session_id
CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) t
WHERE resource_database_id = DB_ID('MaBase')
AND resource_associated_entity_id = OBJECT_ID('MaTable');