DMVから、接続がApplicationIntent = ReadOnlyを使用したかどうかを確認できますか?


23

Always On可用性グループがセットアップされており、ユーザーが接続文字列でApplicationIntent = ReadOnlyを使用していることを確認したい。

DMV(または拡張イベントなど)を介してSQL Serverから、ユーザーが接続文字列でApplicationIntent = ReadOnlyで接続したかどうかを確認できますか?

接続を防止する方法については答えないでください-それはこの質問の目的ではありません。適切な文字列なしで接続している既存のアプリケーションがあるため、単純に接続を停止することはできません。どのアプリケーションであるかを知る必要があります。

ユーザーが複数のアプリケーションを持っていると仮定します。たとえば、ボブはSQL Server Management StudioおよびExcelに接続します。彼は、更新を行う必要があるときにSSMSに接続し、読み取りを行う必要があるときにExcelに接続します。彼がExcelに接続するときにApplicationIntent = ReadOnlyを使用していることを確認する必要があります。(これは正確なシナリオではありませんが、説明するには十分です。)


私が思う読み取り専用TDSルーティング時に決定されます。読み取り可能なセカンダリにルーティングされると、情報は不要になりますので、おそらくエンジンには入りません。
レムスルサヌ

2
「読み取り専用ルーティングは最初にプライマリに接続し、次に利用可能な最適なセカンダリを探します」セカンダリは通常の接続として認識します。トリガーされるXEventがある場合、それはプライマリーにあります。何を言っているのかわかりませんが、推測しています。
レムスルサヌ

1
@RemusRusanuはsqlserver.read_only_route_complete、プライマリでのみトリガーされるため、あなたが話しているのです。
キンシャー

@Kinそこに行く、ちょうど私がそれをコーディングしていたように;)
レマスルサヌ

2
@RemusRusanu私はそれで遊んでいたので、gotchasで取得できるものに最も近いと思います-読み取り専用URLは正しく設定されており、接続の問題はありません。両方のケースで、そのイベントは成功します。
キンシャー

回答:


10

sqlserver.read_only_route_completeKinとRemusが言及したExtended Eventを取り上げてみると、これは素晴らしいデバッグイベントですが、大量の情報は持ちません- 既定ではroute_port(たとえば1433)およびroute_server_name(たとえばsqlserver-0.contoso.com)だけです。 。これは、読み取り専用インテント接続がいつ成功したかを判断するのにも役立ちます。read_only_route_failイベントはありますが、起動することができませんでした。おそらく、ルーティングURLに問題がある場合、セカンダリインスタンスが利用できない/シャットダウンされたときに起動するようには見えませんでした。

ただし、sqlserver.loginイベントや因果関係の追跡を有効にして、いくつかのアクション(などsqlserver.username)を有効にすることで、それを成功させることができました。

再現する手順

関連イベントに加えて有用なアクションを追跡し、因果関係を追跡する拡張イベントセッションを作成します。

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
)

XEセッションを実行し(これはデバッグイベントであるためサンプリングを検討してください)、いくつかのログインを収集します。

sqlcmd接続

ここで、sqlserver-0が読み取り可能なセカンダリであり、sqlserver-1がプライマリであることに注意してください。ここでは、-Kスイッチを使用して、sqlcmd読み取り専用のアプリケーションインテントログインといくつかのSQLログインをシミュレートしています。読み取り専用イベントは、読み取り専用のインテントログインが成功すると起動します。

セッションを一時停止または停止すると、クエリを実行して、次の2つのイベントをリンクできます。

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

クエリは、アプリケーションの読み取り専用インテントを使用した場合と使用しない場合のログインを表示する必要があります。

クエリ結果

  • read_only_route_completeデバッグイベントなので、控えめに使用してください。たとえばサンプリングを検討してください。
  • 2つのイベントとトラックの因果関係は、要件を満たす可能性を提供します-この単純なリグでさらにテストが必要です
  • データベース名が接続で指定されていない場合、私は気づきました、物事はうまくいかないようでした
  • pair_matchingターゲットを機能させようとしましたが、時間を使い果たしました。ここには、次のような開発の可能性があります。

    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'
        )

5

いいえ、DMVに公開された接続プロパティ(sys.dm_exec_connectionsまたはsys.dm_exec_sessionsのいずれか)またはConnectionStringキーワードに関連するCONNECTIONPROPERTYさえ存在しないようですApplicationIntent

ただし、Microsoft Connectを介して、MSDNページにある次の情報に基づいて、SQL Serverのメモリのどこかに保存されている接続のプロパティであるようsys.dm_exec_connections見えるため、このプロパティをDMVに追加するように要求する価値があるかもしれません高可用性、災害復旧のためのSqlClientサポート(イタリック体の強調鉱山):

アプリケーションの意図を指定する

ときApplicationIntent =読み取り専用のAlwaysOnに接続するとき、クライアントは、読み取り作業負荷を要求し、データベースを有効に。サーバーは、接続時およびUSEデータベースステートメント中にインテントを強制しますが、Always Onが有効なデータベースに対してのみです。

USEステートメントを検証できる場合ApplicationIntent、最初の接続試行を超えて存在する必要があります。ただし、この動作を個人的に確認したことはありません。


PS私は、次の事実を利用できると考えていました。

  • プライマリレプリカは、1つ以上のデータベースへの読み取り専用アクセスを許可しないように設定できます。
  • USEステートメントが実行されると、「意図」が実施されます。

この設定のテストと追跡のみを目的として、新しいデータベースを作成するというアイデアがありました。新しいDBは、READ_WRITE接続のみを許可するように設定される新しい可用性グループで使用されます。理論上は、ブロックEXEC(N'USE [ReadWriteOnly]; INSERT INTO LogTable...;');内にTRY...CATCH本質的に何もないログオントリガーの内部のコンストラクト内ではCATCH、ReadWrite接続ではエラーが生成されない(新しいDBに記録される)か、USEReadOnly接続ではエラーになるエラーがキャッチされ無視されるため、何も起こりません(そしてINSERTステートメントに到達することはありません)。どちらの場合でも、実際のログオンイベントは防止/拒否されません。ログオントリガーコードは次のようになります。

BEGIN TRY
    EXEC(N'
        USE [ApplicationIntentTracking];
        INSERT INTO dbo.ReadWriteLog (column_list)
          SELECT sess.some_columns, conn.other_columns
          FROM   sys.dm_exec_connections conn
          INNER JOIN sys.dm_exec_sessions sess
                  ON sess.[session_id] = conn.[session_id]
          WHERE   conn.[session_id] = @@SPID;
        ');
END TRY
BEGIN CATCH
    DECLARE @DoNothing INT;
END CATCH;

発行の効果テストする場合残念ながら、USE内のステートメントをEXEC()TRY...CATCHトランザクションの内部を、私がアクセス違反がバッチレベルのアボートではなく、文レベルのアボートであることがわかりました。そして、設定XACT_ABORT OFFは何も変わりませんでした。使用する簡単なSQLCLRストアドプロシージャを作成し、a 内でContext Connection = true;呼び出しSqlConnection.ChangeDatabase()try...catchも、トランザクションは中断されました。使用できませんEnlist=false、コンテキスト接続で。また、SQLCLRで通常の/外部接続を使用してトランザクションの外に出ると、まったく新しい接続になるため、役に立ちません。

HAS_DBACCESSの代わりにHAS_DBACCESSを使用できる可能性は非常にわずかです。USEステートメントの、現在の接続情報をチェックに組み込むことができることを期待していません。しかし、私はそれをテストする方法もありません。

もちろん、アクセス違反がバッチ中止されない原因となるトレースフラグがある場合は、上記のプランが機能するはずです;-)。


残念ながら、それらを否定することはできません-他の読み取り可能なレプリカがダウンしている可能性があります。プライマリで動作するためには、読み取りクエリがまだ必要です-それらがいつ発生するかを知る必要があります。
ブレントオザー

@BrentOzar回答を更新して、その状態をチェックし、使用可能なセカンダリがない場合は接続を許可する新しいステップ3を含めるようにしました。また、目的はただ単に変更し、同じ設定を使用することができ、「thy're起こったときに知っている」に残っている場合ROLLBACKにログイン・トリガ内のINSERTログテーブルに:-)
ソロモンRutzky

1
これはすばらしい答えですが、この質問のためではありません。ユーザーを停止する必要はありません。いつ発生するかを監視する必要があります。既存のアプリがあり、それらを徐々に特定して修正する必要があります。ユーザーのログインを停止すると、すぐに反乱が起こります。このために別の質問を作成し、そこに答えを投稿したい場合、それは素晴らしいことですが、ここで私の実際の質問に答えを集中してください。ありがとう。
ブレントオザー

@BrentOzar申し訳ありませんが、トムへのコメントは、単なる追跡/ログよりも少し強力なものであると誤解しています。アクセスの防止に関する回答の一部を削除しました。
ソロモンラッツキー

@BrentOzar(PSセクションの)行の下にいくつかのメモを追加しましたが、これは解決策に近いものでしたが、最後には妨害しました。このパズルを解決するかもしれない行方不明のピース、あるいはまったく異なるものを思いつくためにあなた(または他の誰か)のアイデアを刺激する場合に備えて、私はそれらのメモを投稿しました。
ソロモンラッツキー

2

どのくらい病気になりたいですか?TDSストリームをプロキシするのはそれほど難しくありません。SaaSアプリのために行いました。探しているビット(文字通り少し)はlogin7メッセージにあります。ユーザーにプロキシを介して接続させ、そこにビットを記録/強制することができます。地獄、あなたも彼らのためにそれをオンにすることができます。:)


それは間違いなく私がなりたいよりも病気ですが、ありがとう、ハハハ。
ブレントオザー

-1

アプリケーションでサービスアカウントを使用していますか、それとも複数のサービスアカウントを使用していますか?その場合、拡張イベントを使用してログイントラフィックを監視しますが、プライマリ常時接続サーバー上のサービスアカウントを除外します。これで、誰がプライマリ常時接続サーバーにログインしていて、読み取り専用のセカンダリ接続文字列を使用していないかを確認できるはずです。Always-Onをインストールする準備ができていますが、これが機能しないと言わない限り、これを実行します。


1
トム-ユーザーが複数のアプリケーションを持っていると仮定します。たとえば、ボブはSQL Server Management StudioおよびExcelに接続します。更新が必要な場合はSSMSに接続し、読み取りが必要な場合はExcelに接続します。彼がExcelに接続するときに、ApplicationIntent = ReadOnlyを使用していることを確認する必要があります。(これは正確なシナリオではありませんが、説明するには十分です。)
ブレントオザー

また、非常に限られたアクセスでExcelを使用して実稼働サーバーに接続するユーザーもいます。彼らは彼らの権利とつながります。私はそれらを見ることができることを望みます。Always Onを近日中に公開します。
ArmorDba

-1

残念ながら、以下をテストする環境がなく、間違いなく失敗する可能性のあるいくつかのポイントがありますが、価値があるもののためにそこに捨てます。

CLRストアドプロシージャは、new SqlConnection("context connection=true")コンストラクト(ここから取得)を介して現在の接続にアクセスできます。SqlConnection型は、ConnectionStringプロパティを公開します。ApplicationIntentは最初の接続文字列にあるため、このプロパティで使用でき、解析できると想定しています。もちろん、そのチェーンには多くのハンドオフがありますので、すべてが洋ナシ型になるチャンスがたくさんあります。

これはログオントリガーから実行され、必要に応じて必要な値が保持されます。


1
これは機能しません。SQLCLRコードは現在の接続にアクセスできません。コンテキスト接続を介して現在のセッションにアクセスできます。.NETコードのSqlConnectionオブジェクトは、元のクライアントソフトウェアからSQL Serverへの実際の接続を利用していません。これらは2つの別個のものです。
ソロモンラツキー

まあ、気にしないで。
マイケルグリーン

いいえ、これは機能しません。
ブレントオザー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.