Das sqlserver.read_only_route_complete
von Kin und Remus erwähnte Extended Event ist ein nettes Debug- Ereignis, das jedoch nicht viele Informationen enthält - standardmäßig nur route_port
(z. B. 1433) und route_server_name
(z. B. sqlserver-0.contoso.com) . Dies würde auch nur dazu beitragen, festzustellen, wann eine Nur-Lese-Verbindung erfolgreich war. Es gibt ein read_only_route_fail
Ereignis, aber ich konnte es nicht auslösen. Wenn ein Problem mit der Routing-URL aufgetreten ist, schien es nicht ausgelöst zu werden, als die sekundäre Instanz nicht verfügbar war / heruntergefahren wurde, soweit ich das beurteilen konnte.
Ich hatte jedoch einige Erfolge, als ich das sqlserver.login
Ereignis- und Kausalitäts-Tracking aktivierte und einige Aktionen (wie sqlserver.username
), um es nützlich zu machen.
Schritte zum Reproduzieren
Erstellen Sie eine erweiterte Ereignissitzung, um relevante Ereignisse sowie nützliche Aktionen und die Kausalität zu verfolgen:
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
)
Führen Sie die XE-Sitzung aus (betrachten Sie das Sampling als Debug-Ereignis) und sammeln Sie einige Anmeldungen:
Beachten Sie, dass sqlserver-0 meine lesbare sekundäre und sqlserver-1 die primäre ist. Hier verwende ich den -K
Schalter von sqlcmd
, um schreibgeschützte Anmeldungen mit Anwendungsabsicht und einige SQL-Anmeldungen zu simulieren. Das schreibgeschützte Ereignis wird bei einer erfolgreichen Nur-Lese-Anmeldung ausgelöst.
Wenn ich die Sitzung pausiere oder stoppe, kann ich sie abfragen und versuchen, die beiden Ereignisse zu verknüpfen, z.
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
Die Abfrage sollte die Anmeldungen mit und ohne Nur-Lese-Absicht der Anwendung anzeigen:
read_only_route_complete
ist ein Debug-Ereignis, verwenden Sie es daher sparsam. Betrachten Sie zum Beispiel Stichproben.
- Die beiden Events bieten zusammen mit der Track-Kausalität das Potenzial, Ihre Anforderungen zu erfüllen - weitere Tests auf diesem einfachen Rig sind erforderlich
- Ich habe bemerkt, wenn der Datenbankname nicht in der Verbindung angegeben wurde, die Dinge schienen nicht zu funktionieren
Ich habe versucht, das pair_matching
Ziel zum Arbeiten zu bringen , aber mir ging die Zeit davon. Hier gibt es ein gewisses Entwicklungspotential, so etwas wie:
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'
)