DMV'lerden, bir bağlantının ApplicationIntent = ReadOnly kullanıp kullanmadığını söyleyebilir misiniz?


23

Her Zaman Kullanılabilirlik Grubunu kurdum ve kullanıcılarımın bağlantı dizelerinde ApplicationIntent = ReadOnly kullandığından emin olmak istiyorum.

DMV'ler üzerinden SQL Server'dan (ya da Extended Events ya da her neyse), ApplicationIntent ile bağlı bir kullanıcının bağlantı dizgisinde = ReadOnly olup olmadığını söyleyebilir miyim?

Lütfen bağlantıları nasıl ÖNLEMEŞİNİZ ile cevap vermeyin - bu sorunun ne anlama geldiği değil. Bağlantıları durduramıyorum, çünkü doğru dize olmadan bağlanan mevcut uygulamalarımız var ve bunların hangileri olduğunu bilmem gerekiyor, böylece zaman içinde yavaş yavaş düzeltilmesi için geliştiriciler ve kullanıcılar ile çalışabilirim.

Kullanıcıların birden fazla uygulaması olduğunu varsayalım. Örneğin, Bob, SQL Server Management Studio ve Excel ile bağlanır. Güncelleme yapması gerektiğinde SSMS'ye, okuma yapması gerektiğinde Excel'e bağlanır. Excel'e bağlandığında ApplicationIntent = ReadOnly kullandığından emin olmam gerekiyor. (Bu tam senaryo değil, ancak açıklamak için yeterince yakın.)


Ben düşünüyorum salt okunur TDS yönlendirme anda karar verilir. Bir kez okunabilir bir ikincil ürüne yönlendirildiğinde, bilgiye artık ihtiyaç duyulmaz, bu nedenle muhtemelen motora girmez.
Remus Rusanu

2
"sadece okuma yönlendirme ilk önce prize bağlanır ve sonra en iyi okunabilir ikincil aranır" ifadesinin ikincil sıradan bir bağlantı olarak göreceği anlaşılıyor. Tetiklenen herhangi bir XEvent varsa, bu birincil olur. Ne hakkında konuştuğumu bilmiyorum ama spekülasyon yapıyorum.
Remus Rusanu

1
@RemusRusanu sqlserver.read_only_route_completesadece birincil olarak tetiklendiğinden bahsediyorsun .
Kin Shah,

@Kin, tam olarak kodladığım gibi gidiyorsunuz;)
Remus Rusanu

2
@RemusRusanu Onunla oynuyordum ve en yakını gotchas ile alabileceğimi tahmin ediyorum - salt okunur URL doğru yapılandırılmış ve bağlantı sorunu yok. Her iki durumda da, bu olay başarılı olacak.
Kin Shah,

Yanıtlar:


10

Üzerinde toplarken sqlserver.read_only_route_completeKin ve Remus tarafından belirtilen Genişletilmiş Olay, bir güzel ayıklama olay, ama onunla bilgilerin büyük bir taşınmaz - sadece route_port(örneğin 1433) ve route_server_name(örneğin sqlserver-0.contoso.com) varsayılan olarak . Bu aynı zamanda salt okunur bir niyet bağlantısının ne zaman başarılı olduğunu belirlemeye yardımcı olacaktır. Bir read_only_route_failolay var, ancak ateşlenemedim, belki yönlendirme URL’si ile ilgili bir sorun varsa, ikincil örnek kullanamadığım / kullanamadığım kadarıyla patlayabiliyordu.

Bununla birlikte, sqlserver.loginolayı etkinleştirmek ve nedensellik izlemenin etkin olması ve buna yardımcı olacak bazı eylemlerin (benzeri sqlserver.username) katılmasıyla bir miktar başarı elde ettim.

Reprodüksiyon Adımları

Alakalı olayları, ayrıca yararlı eylemleri ve nedenselliği izlemek için bir Genişletilmiş Olaylar oturumu oluşturun:

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 oturumunu çalıştırın (bu bir Debug olayı gibi örneklemeyi düşünün) ve bazı girişler toplayın:

sqlcmd bağlantıları

Buradaki not sqlserver-0 okunabilir ikincil ve sqlserver-1 birincildir. Burada salt okunur uygulama amaçlı girişleri ve bazı SQL girişlerini simüle etmek için -Kanahtarını kullanıyorum sqlcmd. Salt okunur olay, salt okunur amaçlı başarılı bir oturum açma işlemine başlar.

Oturumu duraklatmak veya durdurmak üzerine sorgulayabilirim ve iki olayı birleştirmeyi deneyebilirim, örneğin:

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

Sorgu, uygulama salt okunur amacı olan ve olmayan girişleri göstermelidir:

Sorgu Sonuçları

  • read_only_route_completeBu bir Debug olayıdır, bu nedenle dikkatli kullanın. Örneğin örneklemeyi düşünün.
  • İki yolun nedenselliğiyle birlikte bu olay, ihtiyacınızı yerine getirme potansiyeli sunuyor - bu basit teçhizat için daha ileri testler gerekli
  • Bağlantıda veritabanı adı belirtilmemişse, işler işe yaramadıysa fark ettim.
  • pair_matchingÇalışmayı hedeflemeye çalıştım ama zamanım tükendi. Burada gelişme için bazı potansiyeller var:

    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

Hayır, herhangi bir DMV'ye maruz kalmış bağlantı özelliği olduğu görünmüyor sys.dm_exec_connections veya sys.dm_exec_sessions içinde ) veya hatta ConnectionString anahtar kelimesiyle ilgili CONNECTIONPROPERTY olduğuApplicationIntent .

Ancak, Microsoft Connect aracılığıyla bu özelliğin eklenmesi için buna değer sys.dm_exec_connections sayfada DMV’ye , MSDN sayfasında bulunan aşağıdaki bilgilere dayanarak, SQL Server’ın belleğinde bir yerde saklanan bir bağlantının özelliği olarak göründüğü için DMV’ye istenebilir . Yüksek Kullanılabilirlik, Olağanüstü Durum Kurtarması için SqlClient Desteği (italik vurgu madeni):

Uygulama Amacını Belirleme

Ne zaman ApplicationIntent = SaltOkunur bir AlwaysOn veritabanını etkin bağlanırken, istemci okundu iş yükünü ister. Sunucu, bağlantı süresini ve bir USE veritabanı ifadesi sırasında amacını zorlar, ancak yalnızca Her Zaman Açık etkin bir veritabanına koyar.

Bir USEifade doğrulanabilirse, ApplicationIntentilk bağlantı girişiminin ötesinde olması gerekir. Ancak, bu davranışı şahsen doğrulamamıştım.


PS: Aşağıdaki gerçeklerden faydalanabileceğimizi düşünüyordum:

  • Bir Birincil Çoğaltma, bir veya daha fazla Veritabanına ReadOnly erişimine izin vermeyecek şekilde ayarlanabilir ve
  • "niyet" ne zaman uygulanacak USE ifade yürütüldüğünde uygulanacaktır.

Buradaki amaç yalnızca bu ayarı test etmek ve izlemek amacıyla yeni bir Veri Tabanı oluşturmaktı. Yeni DB, sadece READ_WRITEbağlantılara izin verecek şekilde ayarlanmış yeni bir Kullanılabilirlik Grubunda kullanılacak . Teori, Logon Trigger'ın EXEC(N'USE [ReadWriteOnly]; INSERT INTO LogTable...;');içinde, bir TRY...CATCHyapının içindeki , aslında CATCHblokta hiçbir şey olmayan bir yapı içinde ya ReadWrite bağlantıları (kendilerini yeni DB'ye giriş yapacak) ya da USEReadOnly bağlantılarında hata yapacaktı. o zaman hata yakalanıp dikkate alınmadığı için hiçbir şey olmaz (ve INSERTifadeye asla ulaşılmaz). Her iki durumda da, gerçek oturum açma olayı olur değil yalanladı / önlenebilir. Oturum Açma Tetikleyici kodu etkin bir şekilde olacaktır:

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;

Bir çıkaran etkisini test Ne yazık ki, USEbir dahilinde deyimi EXEC()bir dahilinde TRY...CATCHbir İşlemin iç, ben erişim ihlali bir toplu düzey durdurma değil, bir açıklama düzey iptal olduğu bulundu. Ve ayar XACT_ABORT OFFhiçbir şeyi değiştirmedi. Hatta kullanmak için basit bir SQLCLR Saklı Prosedürü oluşturdum Context Connection = true;ve daha sonra SqlConnection.ChangeDatabase()birtry...catch ve İşlem hala iptal edildi. Ve Enlist=falseBağlam Bağlantısında kullanamazsınız . İşlem dışına çıkmak için SQLCLR'de normal / harici bir bağlantı kullanmak, tamamen yeni bir Bağlantı olacağı için yardımcı olmaz.

HAS_DBACCESS'in çok, çok zayıf bir olasılığı var ifade yerine kullanabileceğiUSE , ancak mevcut Bağlantı bilgilerini çeklerine dahil edebilmesi için gerçekten yüksek bir yok. Ama benim de test etmem mümkün değil.

Tabii ki, erişim ihlali işleminin parti iptalini engellemesine neden olabilecek bir İzleme Bayrağı varsa, yukarıda belirtilen plan işe yaramalı ;-).


Ne yazık ki, onları inkar edemem - okunabilir diğer kopyalar aşağı olabilir. Öncelikle çalışmak için hala okuma sorgularına ihtiyacım var - ne zaman olduklarını bilmem gerekiyor.
Brent Ozar

@BrentOzar Cevabımı, bu durumu kontrol edecek yeni bir Adım 3 içerecek şekilde güncelledim ve uygun İkincil yoksa, Bağlantıya izin verecek. Ayrıca, amaç hala sadece "ne zaman olduğunuzu bilmek" ise, aynı kurulum kullanılabilirse, sadece ROLLBACKOturum Açma Tetikleyicisini INSERTbir günlük tablosuna dönüştürün :-)
Solomon Rutzky

1
bu harika bir cevap, ama bu soru için değil. Kullanıcıları durdurmam gerekmiyor, ne zaman olacağını izlemem gerekiyor. Kademeli olarak belirlemek ve düzeltmek için ihtiyaç duyduğumuz uygulamalarımız var. Kullanıcıların giriş yapmasını engellersem, acil isyan çıkarır. Bunun için ayrı bir soru oluşturmak ve cevabını oraya göndermek istersen, bu harika olurdu - ama lütfen cevabını burada asıl soruma odakla. Teşekkürler.
Brent Ozar

@BrentOzar Üzgünüz, Tom'a yaptığınız yorumunuzu, sadece takip / kayıttan biraz daha güçlü bir şey olarak yanlış anladım. Cevabımın erişimi engelleme ile ilgili olan kısmını kaldırdım.
Solomon Rutzky

@BrentOzar Çizginin altına (PS bölümünde) çözüme yakın olan ancak en sonunda engelleyen bazı notlar ekledim. Bu bulmacayı, eksik bulmacayı, hatta bu bulmacayı çözebilecek tamamen farklı bir şeyi ortaya çıkarmak için (veya başka biri) bir fikir ortaya çıkarması durumunda gönderdim.
Solomon Rutzky

2

Ne kadar hasta olmak istiyorsun? TDS akışı proxy'ye zor gelmiyor, SaaS uygulamamız için yaptık. Aradığınız bit (tam anlamıyla biraz) login7 mesajında. Kullanıcılarınızın bir proxy üzerinden bağlanmalarını sağlayın ve orada bit log / zorla. Kahretsin, onlar için açabilirsin. :)


Kesinlikle olmak istediğimden daha hasta, ama teşekkürler hahaha.
Brent Ozar

-1

Uygulamanız bir servis hesabı mı, yoksa birden fazla servis hesabı kullanıyor mu? Öyleyse, giriş trafiğinizi izlemek ancak birincil her zaman açık sunucunuzdaki hizmet hesaplarınızı hariç tutmak için Genişletilmiş Etkinlik kullanın. Artık, birincil her zaman açık sunucuya kimin giriş yaptığını ve salt okunur ikincil bağlantı dizesini kullanmadığınızı görebiliyor olmalısınız. Always-On'u yüklemeye hazırlanıyorum ve bunun işe yaramayacağını söylemediğiniz sürece yapacağım budur.


1
Tom - kullanıcıların birden fazla uygulamaya sahip olduğunu varsayın. Örneğin, Bob, SQL Server Management Studio ve Excel ile bağlanır. Güncelleme yapması gerektiğinde SSMS'ye, okuma yapması gerektiğinde Excel'e bağlanır. Excel'e bağlandığında ApplicationIntent = ReadOnly kullandığından emin olmam gerekiyor. (Bu tam senaryo değil, ancak açıklamak için yeterince yakın.)
Brent Ozar

Ayrıca üretim sunucuma Excel'le çok sınırlı erişime sahip olan insanlar var. Haklarıyla bağlanırlar. Umarım onları görebilirim. Kısa sürede Daima Açılışı getireceğiz.
ArmorDba

-1

Maalesef, aşağıdakileri test edecek ortamım yok ve şüphesiz başarısız olabileceği birkaç nokta var, ama buna değecek şey için onu dışarı atacağım.

Bir CLR saklı yordamı new SqlConnection("context connection=true")yapı üzerinden mevcut bağlantıya erişebilir ( buradan alınır ). SqlConnection tipi bir ConnectionString özelliğini gösterir. ApplicationIntent ilk bağlantı dizesinde olduğundan, bu özellikte kullanılabilir olacağını ve ayrıştırılabileceğini farz ediyorum. Elbette, bu zincirde bir sürü el sıkıntısı var, hepsinin armut biçimli olması için çok fazla fırsat var.

Bu, bir Oturum Açma Tetikleyicisinden çalıştırılır ve gereken değerler gerektiği gibi devam eder.


1
Bu işe yaramaz. SQLCLR kodunun mevcut bağlantıya erişimi yok, mevcut bağlantıya içerik bağlantısı üzerinden erişebiliyor. .NET kodundaki SqlConnection nesnesi, orijinal istemci yazılımından SQL Server'a yapılan gerçek bağlantıya dokunmuyor. Bunlar iki ayrı şey.
Solomon Rutzky,

Ah, boşver o zaman.
Michael Green,

Hayır, bu işe yaramıyor.
Brent Ozar
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.