Reprenant l' sqlserver.read_only_route_complete
événement prolongé mentionné par Kin et Remus, il est un bon débogage événement, mais il ne porte pas beaucoup d'informations avec - juste route_port
(par exemple 1433) et route_server_name
(par exemple sqlserver-0.contoso.com) par défaut . Cela aiderait également à déterminer quand une connexion intentionnelle en lecture seule a réussi. Il y a un read_only_route_fail
événement mais je n'ai pas pu le déclencher, peut-être qu'en cas de problème avec l'URL de routage, il ne semblait pas se déclencher lorsque l'instance secondaire n'était pas disponible / arrêtée pour autant que je sache.
J'ai cependant eu un certain succès en joignant cela à l' sqlserver.login
événement et au suivi de la causalité activé, ainsi que certaines actions (comme sqlserver.username
) pour le rendre utile.
Étapes à reproduire
Créez une session d'événements étendus pour suivre les événements pertinents, ainsi que des actions utiles et suivre le lien de causalité:
CREATE EVENT SESSION [xe_watchLoginIntent] ON SERVER
ADD EVENT sqlserver.login
( ACTION ( sqlserver.username ) ),
ADD EVENT sqlserver.read_only_route_complete
( ACTION (
sqlserver.client_app_name,
sqlserver.client_connection_id,
sqlserver.client_hostname,
sqlserver.client_pid,
sqlserver.context_info,
sqlserver.database_id,
sqlserver.database_name,
sqlserver.username
) ),
ADD EVENT sqlserver.read_only_route_fail
( ACTION (
sqlserver.client_app_name,
sqlserver.client_connection_id,
sqlserver.client_hostname,
sqlserver.client_pid,
sqlserver.context_info,
sqlserver.database_id,
sqlserver.database_name,
sqlserver.username
) )
ADD TARGET package0.event_file( SET filename = N'xe_watchLoginIntent' )
WITH (
MAX_MEMORY = 4096 KB,
EVENT_RETENTION_MODE = ALLOW_SINGLE_EVENT_LOSS,
MAX_DISPATCH_LATENCY = 30 SECONDS,
MAX_EVENT_SIZE = 0 KB,
MEMORY_PARTITION_MODE = NONE,
TRACK_CAUSALITY = ON, --<-- relate events
STARTUP_STATE = ON --<-- ensure sessions starts after failover
)
Exécutez la session XE (pensez à l'échantillonnage car il s'agit d'un événement de débogage) et collectez quelques connexions:
Notez ici que sqlserver-0 est mon secondaire lisible et sqlserver-1 le principal. Ici, j'utilise le -K
commutateur de sqlcmd
pour simuler les connexions d'intention d'application en lecture seule et certaines connexions SQL. L'événement en lecture seule se déclenche sur une connexion d'intention en lecture seule réussie.
En mettant en pause ou en arrêtant la session, je peux l'interroger et tenter de lier les deux événements, par exemple:
DROP TABLE IF EXISTS #tmp
SELECT IDENTITY( INT, 1, 1 ) rowId, file_offset, CAST( event_data AS XML ) AS event_data
INTO #tmp
FROM sys.fn_xe_file_target_read_file( 'xe_watchLoginIntent*.xel', NULL, NULL, NULL )
ALTER TABLE #tmp ADD PRIMARY KEY ( rowId );
CREATE PRIMARY XML INDEX _pxmlidx_tmp ON #tmp ( event_data );
-- Pair up the login and read_only_route_complete events via xxx
DROP TABLE IF EXISTS #users
SELECT
rowId,
event_data.value('(event/@timestamp)[1]', 'DATETIME2' ) AS [timestamp],
event_data.value('(event/action[@name="username"]/value/text())[1]', 'VARCHAR(100)' ) AS username,
event_data.value('(event/action[@name="attach_activity_id_xfer"]/value/text())[1]', 'VARCHAR(100)' ) AS attach_activity_id_xfer,
event_data.value('(event/action[@name="attach_activity_id"]/value/text())[1]', 'VARCHAR(100)' ) AS attach_activity_id
INTO #users
FROM #tmp l
WHERE l.event_data.exist('event[@name="login"]') = 1
AND l.event_data.exist('(event/action[@name="username"]/value/text())[. = "SqlUserShouldBeReadOnly"]') = 1
DROP TABLE IF EXISTS #readonly
SELECT *,
event_data.value('(event/@timestamp)[1]', 'DATETIME2' ) AS [timestamp],
event_data.value('(event/data[@name="route_port"]/value/text())[1]', 'INT' ) AS route_port,
event_data.value('(event/data[@name="route_server_name"]/value/text())[1]', 'VARCHAR(100)' ) AS route_server_name,
event_data.value('(event/action[@name="username"]/value/text())[1]', 'VARCHAR(100)' ) AS username,
event_data.value('(event/action[@name="client_app_name"]/value/text())[1]', 'VARCHAR(100)' ) AS client_app_name,
event_data.value('(event/action[@name="attach_activity_id_xfer"]/value/text())[1]', 'VARCHAR(100)' ) AS attach_activity_id_xfer,
event_data.value('(event/action[@name="attach_activity_id"]/value/text())[1]', 'VARCHAR(100)' ) AS attach_activity_id
INTO #readonly
FROM #tmp
WHERE event_data.exist('event[@name="read_only_route_complete"]') = 1
SELECT *
FROM #users u
LEFT JOIN #readonly r ON u.attach_activity_id_xfer = r.attach_activity_id_xfer
SELECT u.username, COUNT(*) AS logins, COUNT( DISTINCT r.rowId ) AS records
FROM #users u
LEFT JOIN #readonly r ON u.attach_activity_id_xfer = r.attach_activity_id_xfer
GROUP BY u.username
La requête doit afficher les connexions avec et sans intention de lecture seule de l'application:
read_only_route_complete
est un événement de débogage, utilisez-le avec parcimonie. Prenons l'exemple de l'échantillonnage.
- les deux événements ainsi que le lien de causalité offrent la possibilité de répondre à vos besoins - des tests supplémentaires sont nécessaires sur cette plate-forme simple
- J'ai remarqué que si le nom de la base de données n'était pas spécifié dans la connexion, les choses ne semblaient pas fonctionner
J'ai essayé de faire fonctionner la pair_matching
cible mais j'ai manqué de temps. Il y a un potentiel de développement ici, quelque chose comme:
ALTER EVENT SESSION [xe_watchLoginIntent] ON SERVER
ADD TARGET package0.pair_matching (
SET begin_event = N'sqlserver.login',
begin_matching_actions = N'sqlserver.username',
end_event = N'sqlserver.read_only_route_complete',
end_matching_actions = N'sqlserver.username'
)