DMV से, क्या आप बता सकते हैं कि क्या एक कनेक्शन ApplicationIntent = ReadOnly का उपयोग करता है?


23

मेरे पास ऑलवेज ऑन उपलब्धता ग्रुप सेट अप है, और मैं यह सुनिश्चित करना चाहता हूं कि मेरे उपयोगकर्ता अपने पुराने तार में ApplicationIntent = ReadOnly का उपयोग कर रहे हैं।

डीएमवी (या विस्तारित इवेंट या जो भी) के माध्यम से SQL सर्वर से, क्या मैं बता सकता हूं कि क्या कोई उपयोगकर्ता ApplicationIntent = ReadOnly उनके कनेक्शन स्ट्रिंग में जुड़ा हुआ है?

कृपया कनेक्शनों के साथ कैसे जवाब दें - यह सवाल इस बारे में नहीं है। मैं बस कनेक्शन को रोक नहीं सकता, क्योंकि हमारे पास मौजूदा एप्लिकेशन हैं जो सही स्ट्रिंग के बिना कनेक्ट हो रहे हैं, और मुझे यह जानना होगा कि वे कौन से हैं इसलिए मैं डेवलपर्स और उपयोगकर्ताओं के साथ काम कर सकता हूं ताकि समय के साथ धीरे-धीरे इसे ठीक किया जा सके।

मान लें कि उपयोगकर्ताओं के पास कई एप्लिकेशन हैं। उदाहरण के लिए, बॉब SQL सर्वर प्रबंधन स्टूडियो और एक्सेल के साथ जोड़ता है। वह SSMS से जुड़ता है जब उसे अपडेट करने की आवश्यकता होती है, और एक्सेल को जब उसे पढ़ने की आवश्यकता होती है। मुझे यह सुनिश्चित करने की आवश्यकता है कि जब वह एक्सेल के साथ जुड़ता है तो वह ApplicationIntent = ReadOnly का उपयोग कर रहा है। (यह सटीक परिदृश्य नहीं है, लेकिन यह वर्णन करने के लिए पर्याप्त करीब है।)


मुझे लगता है कि केवल टीडीएस रूटिंग समय पर रीड-ओनली का निर्णय लिया जाता है। एक बार पढ़ने योग्य माध्यमिक में स्थानांतरित हो जाने के बाद, जानकारी की आवश्यकता नहीं रह जाती है, इसलिए शायद यह इंजन में नहीं आता है।
रेमस रूसु

2
"केवल राउटिंग पहले प्राथमिक से जुड़ता है और फिर सबसे अच्छा उपलब्ध पठनीय माध्यमिक की तलाश करता है" ऐसा प्रतीत होता है कि माध्यमिक इसे एक साधारण कनेक्शन के रूप में देखेगा। यदि कोई XEvent ट्रिगर है, तो यह प्राथमिक पर होगा। मुझे नहीं पता कि मैं किस बारे में बात कर रहा हूं, लेकिन मैं अटकलें लगा रहा हूं।
रेमस रूसु

1
@RemusRusanu आप के बारे sqlserver.read_only_route_completeमें बात कर रहे हैं क्योंकि यह केवल प्राथमिक पर चालू है।
परिजन शाह

@ वहाँ तुम जाओ, बिल्कुल के रूप में मैं इसे कोड होता;)
रेमस रुसानु

2
@RemusRusanu मैं इसके साथ खेल रहा था और मुझे इसके सबसे नज़दीक होने का अनुमान है कि आप गोच के साथ प्राप्त कर सकते हैं - आसानी से URL को सही तरीके से कॉन्फ़िगर किया गया है और कोई कनेक्टिविटी समस्याएं नहीं हैं। उन दोनों मामलों के तहत, वह घटना सफल होगी।
परिजन शाह

जवाबों:


10

sqlserver.read_only_route_completeपरिजनों और रेमस द्वारा उल्लिखित विस्तारित घटना पर उठाते हुए , यह एक अच्छी डेब्यू घटना है, लेकिन यह इसके साथ बहुत अधिक जानकारी नहीं रखता है - बस route_port(उदाहरण के लिए 1433) और route_server_name(जैसे sqlserver-0.contoso.com) डिफ़ॉल्ट रूप से । यह केवल यह निर्धारित करने में भी मदद करेगा कि एक रीड-ओनली इंटेंस कनेक्शन सफल था। एक read_only_route_failघटना है, लेकिन मैं इसे फायर नहीं कर सका, हो सकता है कि रूटिंग यूआरएल के साथ कोई समस्या हो, जब तक मैं बता सकता हूं तब तक माध्यमिक उदाहरण अनुपलब्ध / बंद होने पर आग नहीं लगती थी।

हालाँकि, मुझे कुछ सफलता मिली है जो इस 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 लॉगिन का अनुकरण करने के लिए स्विच का उपयोग कर रहा हूं । एक सफल रीड-ओनली इरादे लॉगिन पर आसानी से आग लगने की घटना।

सत्र रोकने या रोकने पर मैं इसे क्वेरी कर सकता हूं और दो घटनाओं को लिंक करने का प्रयास कर सकता हूं, जैसे:

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एक डीबग घटना है, इसलिए संयम से उपयोग करें। उदाहरण के लिए नमूने पर विचार करें।
  • ट्रैक कारण के साथ दो घटनाएँ आपकी आवश्यकता को पूरा करने की क्षमता प्रदान करती हैं - इस सरल रिग पर आवश्यक परीक्षण
  • यदि कनेक्शन में डेटाबेस का नाम निर्दिष्ट नहीं किया गया था, तो मैंने नोटिस किया था कि चीजें काम नहीं करती थीं
  • मैंने 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-उजागर कनेक्शन संपत्ति (या तो sysinos_exec_connections या sysinos_exec_session ) या यहां तक कि CONNECTIONPROPERTY से संबंधित है।ApplicationIntent कीवर्ड ।

हालाँकि, यह Microsoft कनेक्ट के माध्यम से अनुरोध करने योग्य हो सकता है, क्योंकि यह गुण sys.dm_exec_connectionsDMV में जोड़ा जाता है क्योंकि यह कनेक्शन का गुण प्रतीत होता है जो SQL Server की मेमोरी में कहीं संग्रहीत होता है, जो MSDN पृष्ठ के लिए निम्न जानकारी के आधार पर होता है। उच्च उपलब्धता, आपदा वसूली के लिए SqlClient समर्थन (इटैलिकाइज़्ड जोर मेरा):

स्पेसिफिक एप्लिकेशन इंटेंट

जब ApplicationIntent = ReadOnly , क्लाइंट एक AlwaysOn सक्षम डेटाबेस से कनेक्ट करते समय रीड वर्कलोड का अनुरोध करता है। सर्वर कनेक्शन समय पर और एक USE डेटाबेस स्टेटमेंट के दौरान आशय को लागू करेगा लेकिन केवल ऑलवेज ऑन एनेबल डेटाबेस को।

यदि एक USEबयान को सत्यापित किया जा सकता है, तो ApplicationIntentप्रारंभिक कनेक्शन प्रयास से परे मौजूद होने की आवश्यकता है। हालाँकि, मैंने इस व्यवहार का व्यक्तिगत सत्यापन नहीं किया है।


पुनश्च मैं सोच रहा था कि हम तथ्यों का उपयोग कर सकते हैं:

  • एक या एक से अधिक डेटाबेस तक ReadOnly पहुंच को अस्वीकार करने के लिए एक प्राथमिक प्रतिकृति सेट की जा सकती है
  • "आशय" को तब लागू किया जाएगा जब कोई 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 संग्रहित प्रक्रिया बनाई Context Connection = true;और फिर SqlConnection.ChangeDatabase()एक के भीतर बुलाया try...catchऔर लेन-देन अभी भी निरस्त किया गया था। और आप उपयोग नहीं कर सकतेEnlist=false संदर्भ कनेक्शन पर । और SQLCLR में एक नियमित / बाहरी कनेक्शन का उपयोग करके लेन-देन से बाहर कदम रखने में मदद नहीं मिलेगी क्योंकि यह एक नया कनेक्शन होगा।

एक बहुत ही पतली संभावना है कि HAS_DBACCESSUSE कथन के बजाय उपयोग किया जा सकता है , लेकिन मुझे वास्तव में इसके लिए उच्च उम्मीदें नहीं हैं, जो कि वर्तमान कनेक्शन की जानकारी को अपने चेक में शामिल करने में सक्षम है। लेकिन मेरे पास इसका परीक्षण करने का कोई तरीका नहीं है।

बेशक, अगर कोई ट्रेस फ्लैग है जो एक्सेस उल्लंघन का कारण बैच-गर्भपात नहीं हो सकता है, तो ऊपर दी गई योजना काम करना चाहिए ;-)।


दुर्भाग्य से, मैं उन्हें अस्वीकार नहीं कर सकता - अन्य पठनीय प्रतिकृतियां नीचे हो सकती हैं। मुझे अभी भी प्राथमिक पर काम करने के लिए पढ़ने के प्रश्नों की आवश्यकता है - मुझे बस यह जानना होगा कि वे कब हो रहे हैं।
ब्रेंट ओजर

@BrentOzar मैंने एक नया चरण 3 शामिल करने के लिए अपने उत्तर को अपडेट किया है जो उस स्थिति के लिए जाँच करेगा और यदि कोई दूसरा उपलब्ध नहीं है, तो यह कनेक्शन की अनुमति देगा। इसके अलावा, अगर इरादा अभी भी है "बस यह जान लें कि जब आपका काम हो रहा है", तो उसी सेटअप का उपयोग किया जा सकता है, बस ROLLBACKलॉग इन ट्रिगर INSERTमें लॉग टेबल में बदल दें :-)
सोलोमन रटज़की

1
यह एक महान जवाब है, लेकिन यह इस सवाल के लिए नहीं है। मुझे उपयोगकर्ताओं को रोकने की आवश्यकता नहीं है, मुझे यह देखने की आवश्यकता है कि यह कब हो रहा है। हमारे पास मौजूदा ऐप हैं जिन्हें हमें धीरे-धीरे पिनपाइंट और फिक्स करने की आवश्यकता है। अगर मैंने उपयोगकर्ताओं को लॉग इन करने से रोका, तो यह तत्काल विद्रोह का कारण बनेगा। यदि आप इसके लिए एक अलग प्रश्न बनाना चाहते हैं, और वहां अपना उत्तर पोस्ट करें, तो यह बहुत अच्छा होगा - लेकिन कृपया अपने उत्तर को यहां मेरे वास्तविक प्रश्न पर केंद्रित करें। धन्यवाद।
ब्रेंट ओजर

@BrentOzar क्षमा करें, मैं टॉम को आपकी टिप्पणी को गलत समझ रहा हूं जिसका अर्थ केवल ट्रैकिंग / लॉगिंग से कुछ अधिक मजबूत है। मैंने अपने उत्तर के उस हिस्से को हटा दिया है जो पहुँच को रोकने से निपटा है।
सोलोमन रटज़की

@BrentOzar मैंने लाइन के नीचे (पीएस सेक्शन में) कुछ नोट्स जोड़े जो एक समाधान होने के करीब थे, लेकिन बहुत अंत में विफल हो गए। मैंने उन नोटों को पोस्ट किया है कि यह आपके (या किसी और) में एक विचार को गायब टुकड़े के साथ आने के लिए स्पार्क करता है, या यहां तक ​​कि कुछ पूरी तरह से अलग है, जो इस पहेली को हल कर सकता है।
सोलोमन रटज़की

2

आप कितने बीमार होना चाहते हैं? TDS स्ट्रीम प्रॉक्सी के लिए उतना कठिन नहीं है, हमने इसे अपने सास ऐप के लिए किया है। वह बिट जिसे आप खोज रहे हैं (शाब्दिक रूप से थोड़ा सा) लॉगिन 7 संदेश में। आप अपने उपयोगकर्ताओं को प्रॉक्सी के माध्यम से कनेक्ट कर सकते हैं और वहां थोड़ा लॉग इन / लॉग इन कर सकते हैं। नरक, आप भी उनके लिए इसे चालू कर सकते हैं। :)


मैं निश्चित रूप से अधिक बीमार होना चाहता हूं, लेकिन धन्यवाद, हाहाहा।
ब्रेंट ओजर

-1

क्या आपका एप्लिकेशन किसी सेवा खाते या शायद कई सेवा खातों का उपयोग करता है? यदि ऐसा है, तो अपने लॉगिन ट्रैफ़िक की निगरानी के लिए विस्तारित ईवेंट का उपयोग करें लेकिन अपने प्राथमिक हमेशा सर्वर पर अपने सेवा खातों को बाहर रखें। अब आपको यह देखने में सक्षम होना चाहिए कि कौन हमेशा प्राथमिक सर्वर पर लॉग इन कर रहा है और आसानी से माध्यमिक कनेक्शन स्ट्रिंग का उपयोग नहीं कर रहा है। मैं ऑलवेज-ऑन को स्थापित करने के लिए तैयार हो रहा हूं और यह मैं तब तक करने जा रहा हूं जब तक आप मुझे यह नहीं बताएंगे कि यह काम नहीं करेगा।


1
टॉम - मान लें कि उपयोगकर्ताओं के पास कई अनुप्रयोग हैं। उदाहरण के लिए, बॉब SQL सर्वर प्रबंधन स्टूडियो और एक्सेल के साथ जोड़ता है। वह SSMS से जुड़ता है जब उसे अपडेट करने की आवश्यकता होती है, और एक्सेल को जब उसे पढ़ने की आवश्यकता होती है। मुझे यह सुनिश्चित करने की आवश्यकता है कि जब वह एक्सेल के साथ जुड़ता है तो वह ApplicationIntent = ReadOnly का उपयोग कर रहा है। (यह सटीक परिदृश्य नहीं है, लेकिन यह स्पष्ट करने के लिए पर्याप्त करीब है।)
ब्रेंट ओजर

मेरे पास बहुत सीमित पहुंच वाले एक्सेल के साथ मेरे प्रोडक्शन सर्वर से जुड़े लोग भी हैं। वे अपने अधिकारों से जुड़ते हैं। मुझे उम्मीद है कि मैं उन्हें देख पाऊंगा। हम शीघ्र ही हमारे ऑलवेज ऑन को लाएंगे।
2

-1

दुर्भाग्य से मेरे पास निम्नलिखित परीक्षण करने का वातावरण नहीं है, और निस्संदेह कई बिंदु हैं जिन पर यह विफल हो सकता है, लेकिन मैं इसे वहां फेंक दूंगा जो इसके लायक है।

सीएलआर संग्रहित प्रक्रिया में new SqlConnection("context connection=true")निर्माण के माध्यम से वर्तमान कनेक्शन तक पहुंच होती है ( यहां से ली गई है )। SqlConnection प्रकार एक ConnectionString गुण को उजागर करता है । चूंकि ApplicationIntent प्रारंभिक कनेक्शन स्ट्रिंग में है, इसलिए मैं इसे इस संपत्ति में उपलब्ध होगा और इसे पार्स किया जा सकता है। उस श्रृंखला में बहुत सारे हैंड-ऑफ हैं, बेशक, इसके लिए बहुत सारे अवसर नाशपाती के आकार के हैं।

यह एक लॉगऑन ट्रिगर से चलेगा और आवश्यक मान आवश्यकतानुसार बना रहेगा ।


1
यह काम नहीं करेगा। SQLCLR कोड की वर्तमान कनेक्शन तक पहुंच नहीं है, यह प्रसंग कनेक्शन के माध्यम से वर्तमान सत्र तक पहुंच है। .NET कोड में SqlConnection ऑब्जेक्ट मूल क्लाइंट सॉफ़्टवेयर से SQL सर्वर में किए गए वास्तविक कनेक्शन में टैप नहीं कर रहा है। वे दो अलग चीजें हैं।
सोलोमन रटज़की

अरे अच्छा, फिर कभी मन नहीं भरा।
माइकल ग्रीन

नहीं, यह काम नहीं करता है।
ब्रेंट ओजर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.