Database
 sql >> डेटाबेस >  >> RDS >> Database

बजट पर पठनीय माध्यमिक

SQL सर्वर 2012 में पेश किया गया उपलब्धता समूह, हमारे डेटाबेस के लिए उच्च उपलब्धता और आपदा पुनर्प्राप्ति दोनों के बारे में सोचने के तरीके में एक मौलिक बदलाव का प्रतिनिधित्व करता है। यहां संभव हुई महान चीजों में से एक माध्यमिक प्रतिकृति के लिए केवल-पढ़ने के संचालन को ऑफ़लोड करना है, ताकि प्राथमिक पढ़ने/लिखने का उदाहरण अंतिम उपयोगकर्ता रिपोर्टिंग जैसी अजीब चीजों से परेशान न हो। इसे सेट करना आसान नहीं है, लेकिन पिछले समाधानों की तुलना में बहुत आसान और अधिक रखरखाव योग्य है (यदि आप मिररिंग और स्नैपशॉट सेट करना पसंद करते हैं, और इससे जुड़े सभी स्थायी रखरखाव पसंद करते हैं तो अपना हाथ उठाएं)।

उपलब्धता समूहों के बारे में सुनते ही लोग बहुत उत्साहित हो जाते हैं। फिर वास्तविकता हिट:सुविधा के लिए SQL सर्वर के एंटरप्राइज़ संस्करण की आवश्यकता होती है (वैसे भी SQL सर्वर 2014 के अनुसार)। एंटरप्राइज़ संस्करण महंगा है, खासकर यदि आपके पास बहुत सारे कोर हैं, और विशेष रूप से सीएएल-आधारित लाइसेंसिंग के उन्मूलन के बाद से (जब तक कि आप 2008 आर 2 से दादा नहीं थे, इस मामले में आप पहले 20 कोर तक सीमित हैं)। इसके लिए विंडोज सर्वर फेलओवर क्लस्टरिंग (डब्लूएसएफसी) की भी आवश्यकता होती है, जो न केवल लैपटॉप पर तकनीक का प्रदर्शन करने के लिए एक जटिलता है, बल्कि क्लस्टरिंग का समर्थन करने के लिए विंडोज़ के एंटरप्राइज़ संस्करण, एक डोमेन नियंत्रक और कॉन्फ़िगरेशन के पूरे समूह की भी आवश्यकता होती है। और सॉफ़्टवेयर एश्योरेंस के आसपास भी नई आवश्यकताएं हैं; एक अतिरिक्त लागत यदि आप चाहते हैं कि आपके स्टैंडबाय इंस्टेंस का अनुपालन हो।

कुछ ग्राहक कीमत को सही नहीं ठहरा सकते। अन्य लोग मूल्य देखते हैं, लेकिन बस इसे वहन नहीं कर सकते। तो ये उपयोगकर्ता क्या करें?

आपका नया हीरो:लॉग शिपिंग

लॉग शिपिंग युगों के आसपास रहा है। यह आसान है और यह सिर्फ काम करता है। लगभग हमेशा। और उपलब्धता समूहों द्वारा प्रस्तुत लाइसेंसिंग लागत और कॉन्फ़िगरेशन बाधाओं को दरकिनार करने के अलावा, यह 14-बाइट पेनल्टी से भी बच सकता है, जिसके बारे में पॉल रैंडल (@PaulRandal) ने इस सप्ताह के SQLskills Insider न्यूज़लेटर (अक्टूबर 13, 2014) में बात की थी।

लोगों को एक पठनीय माध्यमिक के रूप में लॉग शिप की गई प्रतिलिपि का उपयोग करने के साथ चुनौतियों में से एक यह है कि आपको किसी भी नए लॉग को लागू करने के लिए सभी मौजूदा उपयोगकर्ताओं को बाहर निकालना होगा - इसलिए या तो आप नाराज हो रहे हैं क्योंकि वे बार-बार बाधित होते हैं चल रहे प्रश्नों से, या आप उपयोगकर्ताओं को परेशान कर रहे हैं क्योंकि उनका डेटा पुराना है। ऐसा इसलिए है क्योंकि लोग खुद को एक पठनीय माध्यमिक तक सीमित रखते हैं।

इसे उस तरह से नहीं किया जाना है; मुझे लगता है कि यहां एक सुंदर समाधान है, और जबकि उपलब्धता समूहों को चालू करने की तुलना में इसके लिए बहुत अधिक लेग वर्क की आवश्यकता हो सकती है, यह निश्चित रूप से कुछ के लिए एक आकर्षक विकल्प होगा।

मूल रूप से, हम कई सेकेंडरी सेट कर सकते हैं, जहां हम शिप लॉग करेंगे और राउंड-रॉबिन दृष्टिकोण का उपयोग करके उनमें से केवल एक को "सक्रिय" माध्यमिक बना देंगे। लॉग को शिप करने वाला कार्य जानता है कि वर्तमान में कौन सा सक्रिय है, इसलिए यह WITH STANDBY का उपयोग करके "अगले" सर्वर पर केवल नए लॉग को पुनर्स्थापित करता है विकल्प। रिपोर्टिंग एप्लिकेशन रनटाइम पर यह निर्धारित करने के लिए समान जानकारी का उपयोग करता है कि उपयोगकर्ता द्वारा चलाई जाने वाली अगली रिपोर्ट के लिए कनेक्शन स्ट्रिंग क्या होनी चाहिए। जब अगला लॉग बैकअप तैयार हो जाता है, तो सब कुछ एक के बाद एक शिफ्ट हो जाता है, और इंस्टेंस जो अब नया पठनीय माध्यमिक बन जाएगा, WITH STANDBY का उपयोग करके पुनर्स्थापित हो जाता है। ।

मॉडल को सरल रखने के लिए, मान लें कि हमारे पास चार उदाहरण हैं जो पठनीय सेकेंडरी के रूप में काम करते हैं, और हम हर 15 मिनट में लॉग बैकअप लेते हैं। किसी भी समय, हमारे पास स्टैंडबाय मोड में एक सक्रिय सेकेंडरी होगा, जिसमें डेटा 15 मिनट से अधिक पुराना नहीं होगा, और स्टैंडबाय मोड में तीन सेकेंडरी होंगे जो नई क्वेरी की सेवा नहीं दे रहे हैं (लेकिन फिर भी पुराने प्रश्नों के परिणाम लौटा सकते हैं)।

यह सबसे अच्छा काम करेगा यदि कोई प्रश्न 45 मिनट से अधिक समय तक चलने की उम्मीद नहीं है। (आपको अपने केवल-पढ़ने के संचालन की प्रकृति के आधार पर इन चक्रों को समायोजित करने की आवश्यकता हो सकती है, कितने समवर्ती उपयोगकर्ता लंबी क्वेरी चला रहे हैं, और क्या सभी को बाहर निकालकर उपयोगकर्ताओं को बाधित करना संभव है।)

यह भी सबसे अच्छा काम करेगा यदि एक ही उपयोगकर्ता द्वारा चलाए जा रहे लगातार प्रश्न उनके कनेक्शन स्ट्रिंग को बदल सकते हैं (यह तर्क है जिसे एप्लिकेशन में होने की आवश्यकता होगी, हालांकि आप आर्किटेक्चर के आधार पर समानार्थक शब्द या विचारों का उपयोग कर सकते हैं), और इसमें अलग-अलग डेटा शामिल हैं इस बीच बदल गया (जैसे कि वे लाइव, लगातार बदलते डेटाबेस को क्वेरी कर रहे थे)।

इन सभी मान्यताओं को ध्यान में रखते हुए, हमारे कार्यान्वयन के पहले 75 मिनट के लिए घटनाओं का एक उदाहरण क्रम है:

<थ>दृश्य
समय घटनाओं
12:00 (t0)
  • बैकअप लॉग t0
  • उपयोगकर्ताओं को उदाहरण ए से बाहर कर दें
  • लॉग t0 को उदाहरण A (STANDBY) में पुनर्स्थापित करें
  • नई केवल-पढ़ने के लिए प्रश्न उदाहरण ए पर जाएंगे
12:15 (t1)
  • बैकअप लॉग t1
  • उपयोगकर्ताओं को उदाहरण से बाहर करें B
  • लॉग t0 को इंस्टेंस B (NORECOVERY) में पुनर्स्थापित करें
  • लॉग t1 को इंस्टेंस B (STANDBY) में पुनर्स्थापित करें
  • नई केवल-पढ़ने के लिए क्वेरी इंस्टेंस B पर जाएगी
  • उदाहरण A के लिए मौजूदा केवल-पढ़ने के लिए क्वेरी चलना जारी रख सकती हैं, लेकिन ~15 मिनट पीछे
12:30 (t2)
  • बैकअप लॉग t2
  • किक आउट ऑफ इंस्टेंस C
  • लॉग को पुनर्स्थापित करें t0 -> t1 उदाहरण के लिए C (NORECOVERY)
  • लॉग t2 को उदाहरण C (STANDBY) में पुनर्स्थापित करें
  • नई केवल-पढ़ने के लिए क्वेरी इंस्टेंस C पर जाएगी
  • ए और बी उदाहरणों के लिए मौजूदा केवल-पढ़ने के लिए क्वेरी चलना जारी रख सकती हैं (15-30 मिनट पीछे)
12:45 (t3)
  • बैकअप लॉग t3
  • प्रयोक्ताओं को किक आउट ऑफ इंस्टेंस डी
  • लॉग को पुनर्स्थापित करें t0 -> t2 उदाहरण के लिए D (NORECOVERY)
  • लॉग t3 को इंस्टेंस D (STANDBY) में पुनर्स्थापित करें
  • नई केवल-पढ़ने के लिए क्वेरी इंस्टेंस डी पर जाएगी
  • ए, बी और सी इंस्टेंस के लिए मौजूदा रीड ओनली क्वेश्चन चलना जारी रख सकते हैं (15-45 मिनट पीछे)
13:00 (t4)
  • बैकअप लॉग t4
  • उपयोगकर्ताओं को उदाहरण ए से बाहर कर दें
  • लॉग को पुनर्स्थापित करें t1 -> t3 उदाहरण के लिए A (NORECOVERY)
  • लॉग t4 को उदाहरण A (STANDBY) में पुनर्स्थापित करें
  • नई केवल-पढ़ने के लिए प्रश्न उदाहरण ए पर जाएंगे
  • इंस्टेंस बी, सी और डी के लिए मौजूदा रीड-ओनली क्वेरी चलना जारी रख सकती हैं (15-45 मिनट पीछे)
  • t0 से उदाहरण A पर अभी भी चल रही क्वेरी -> ~t1 (45-60 मिनट) रद्द कर दी जाएगी


यह काफी आसान लग सकता है; जो कुछ अधिक कठिन है उसे संभालने के लिए कोड लिखना। एक मोटा रूपरेखा:

  1. प्राथमिक सर्वर पर (मैं इसे BOSS कहूंगा ), एक डेटाबेस बनाएँ। आगे जाने के बारे में सोचने से पहले, सफल बैकअप संदेशों को SQL सर्वर के त्रुटि लॉग में डालने से रोकने के लिए ट्रेस फ्लैग 3226 चालू करें।
  2. BOSS पर , प्रत्येक माध्यमिक के लिए एक लिंक किया गया सर्वर जोड़ें (मैं उन्हें PEON1 . कहूंगा -> PEON4 )।
  3. कहीं भी सभी सर्वरों के लिए सुलभ, डेटाबेस/लॉग बैकअप स्टोर करने के लिए एक फ़ाइल शेयर बनाएं, और सुनिश्चित करें कि प्रत्येक उदाहरण के लिए सेवा खातों में पढ़ने/लिखने की पहुंच है। साथ ही, प्रत्येक सेकेंडरी इंस्टेंस में स्टैंडबाय फ़ाइल के लिए निर्दिष्ट स्थान होना आवश्यक है।
  4. एक अलग उपयोगिता डेटाबेस (या MSDB, यदि आप चाहें) में, ऐसी तालिकाएँ बनाएँ जो डेटाबेस (ओं), सभी सेकेंडरी, और लॉग बैकअप और इतिहास को पुनर्स्थापित करने के बारे में कॉन्फ़िगरेशन जानकारी रखें।
  5. संग्रहीत कार्यविधियां बनाएं जो डेटाबेस का बैकअप लें और सेकेंडरी WITH NORECOVERY पर पुनर्स्थापित करें , और फिर एक लॉग लागू करें WITH STANDBY , और एक उदाहरण को वर्तमान स्टैंडबाय सेकेंडरी के रूप में चिह्नित करें। कुछ भी गलत होने की स्थिति में इन प्रक्रियाओं का उपयोग पूरे लॉग शिपिंग सेटअप को फिर से शुरू करने के लिए भी किया जा सकता है।
  6. एक ऐसा कार्य बनाएं जो ऊपर वर्णित कार्यों को करने के लिए हर 15 मिनट में चलेगा:
    • लॉग का बैकअप लें
    • यह निर्धारित करें कि किस द्वितीयक लॉग बैकअप को लागू नहीं किया गया है
    • उपयुक्त सेटिंग्स के साथ उन लॉग को पुनर्स्थापित करें
  7. एक संग्रहित प्रक्रिया बनाएं (और/या एक दृश्य?) जो कॉलिंग एप्लिकेशन को बताएगी कि उन्हें किसी भी नए केवल-पढ़ने के लिए किस माध्यमिक का उपयोग करना चाहिए।
  8. लॉग के लिए लॉग बैकअप इतिहास को साफ़ करने के लिए एक क्लीनअप प्रक्रिया बनाएं जो सभी सेकेंडरी पर लागू किया गया है (और शायद फ़ाइलों को स्वयं स्थानांतरित या शुद्ध करने के लिए भी)।
  9. समाधान को मजबूत त्रुटि प्रबंधन और सूचनाओं के साथ बढ़ाएं।

चरण 1 - एक डेटाबेस बनाएं

मेरा प्राथमिक उदाहरण मानक संस्करण है, जिसका नाम .\BOSS है . उस उदाहरण पर मैं एक टेबल के साथ एक साधारण डेटाबेस बनाता हूं:

यूज [मास्टर];GOCREATE DATABASE UserData;GOALTER DATABASE UserData SET रिकवरी फुल;GOUSE UserData;GOCREATE TABLE dbo.LastUpdate(EventTime DATETIME2); INSERT dbo.LastUpdate(EventTime) SELECT SYSDATETIME();

फिर मैं एक SQL सर्वर एजेंट जॉब बनाता हूं जो हर मिनट केवल उस टाइमस्टैम्प को अपडेट करता है:

अपडेट UserData.dbo.LastUpdate SET EventTime =SYSDATETIME();

यह केवल प्रारंभिक डेटाबेस बनाता है और गतिविधि का अनुकरण करता है, जिससे हमें यह सत्यापित करने की अनुमति मिलती है कि लॉग शिपिंग कार्य प्रत्येक पठनीय सेकेंडरी के माध्यम से कैसे घूमता है। मैं स्पष्ट रूप से बताना चाहता हूं कि इस अभ्यास का उद्देश्य परीक्षण लॉग शिपिंग पर जोर देना या यह साबित करना नहीं है कि हम कितनी मात्रा में पंच कर सकते हैं; यह पूरी तरह से एक अलग व्यायाम है।

चरण 2 - लिंक किए गए सर्वर जोड़ें

मेरे पास .\PEON1 . नाम के चार सेकेंडरी एक्सप्रेस एडिशन इंस्टेंस हैं , .\PEON2 , .\PEON3 , और .\PEON4 . इसलिए मैंने @s . बदलते हुए इस कोड को चार बार चलाया हर बार:

उपयोग [मास्टर];GODECLARE @s NVARCHAR(128) =N'.\PEON1', -- .\PEON2, .\PEON3, .\PEON4 @t NVARCHAR(128) =N'true' के लिए दोहराएं; EXEC [मास्टर] .dbo.sp_addlinkedserver @server =@s, @srvproduct =N'SQL सर्वर'; EXEC [मास्टर] .dbo.sp_addlinkedsrvlogin @rmtsrvname =@s, @useself =@t;EXEC [मास्टर] .dbo। sp_serveroption @server =@s, @optname =N'collation संगत', @optvalue =@t;EXEC [मास्टर].dbo.sp_serveroption @server =@s, @optname =N'data access', @optvalue =@t;EXEC [मास्टर]। ', @optvalue =@t;

चरण 3 - फ़ाइल साझाकरण सत्यापित करें

मेरे मामले में, सभी 5 उदाहरण एक ही सर्वर पर हैं, इसलिए मैंने प्रत्येक उदाहरण के लिए एक फ़ोल्डर बनाया है:C:\temp\Peon1\ , C:\temp\Peon2\ , और इसी तरह। याद रखें कि यदि आपकी सेकेंडरी विभिन्न सर्वरों पर हैं, तो स्थान उस सर्वर के सापेक्ष होना चाहिए, लेकिन फिर भी प्राथमिक से पहुंच योग्य होना चाहिए (इसलिए आमतौर पर एक यूएनसी पथ का उपयोग किया जाएगा)। आपको यह सत्यापित करना चाहिए कि प्रत्येक इंस्टेंस उस शेयर को लिख सकता है, और आपको यह भी सत्यापित करना चाहिए कि प्रत्येक इंस्टेंस स्टैंडबाय फ़ाइल के लिए निर्दिष्ट स्थान पर लिख सकता है (मैंने स्टैंडबाय के लिए समान फ़ोल्डर्स का उपयोग किया है)। आप प्रत्येक उदाहरण से इसके प्रत्येक निर्दिष्ट स्थान पर एक छोटे डेटाबेस का बैकअप लेकर इसे सत्यापित कर सकते हैं - जब तक यह काम न करे तब तक आगे न बढ़ें।

चरण 4 - टेबल बनाएं

मैंने इस डेटा को msdb में रखने का फैसला किया है , लेकिन मैं वास्तव में एक अलग डेटाबेस बनाने के पक्ष या विपक्ष में कोई मजबूत भावना नहीं रखता। मुझे जिस पहली तालिका की आवश्यकता है वह वह है जो डेटाबेस के बारे में जानकारी रखती है, मैं लॉग शिपिंग करने जा रहा हूं:

तालिका dbo.PMAG_Databases(DatabaseName SYSNAME, LogBackupFrequency_Minutes SMALLINT NOT NULL DEFAULT (15), CONSTRAINT PK_DBS प्राथमिक कुंजी (डेटाबेसनाम)) बनाएं; GO INSERT dbo.PMAG_Databases(DatabaseName) SELECT N'UserData';

(यदि आप नामकरण योजना के बारे में उत्सुक हैं, तो PMAG का अर्थ "गरीब आदमी की उपलब्धता समूह" है।)

लॉग शिपिंग अनुक्रम में उनके व्यक्तिगत फ़ोल्डर और उनकी वर्तमान स्थिति सहित सेकेंडरी के बारे में जानकारी रखने के लिए एक और तालिका की आवश्यकता है।

टेबल dbo.PMAG_Secondaries(डेटाबेसनाम SYSNAME, ServerInstance SYSNAME, CommonFolder VARCHAR(512) NOT NULL, DataFolder VARCHAR(512) NOT NULL, LogFolder VARCHAR(512) NOT NULL, स्टैंडबायलोकेशन VARCHAR(512) NOT NULL, IsCurrentStandby BIT NULL DEFAULT 0, CONSTRAINT PK_Sec PRIMARY KEY(DatabaseName, ServerInstance), CONSTRAINT FK_Sec_DBs FOREIGN KEY(DatabaseName) REFERENCES dbo.PMAG_Databases(DatabaseName));

यदि आप स्थानीय रूप से स्रोत सर्वर से बैकअप लेना चाहते हैं, और सेकेंडरी को दूरस्थ रूप से पुनर्स्थापित करना चाहते हैं, या इसके विपरीत, आप CommonFolder को विभाजित कर सकते हैं दो कॉलम में (BackupFolder और RestoreFolder ), और कोड में प्रासंगिक परिवर्तन करें (इतना नहीं होगा)।

चूंकि मैं sys.servers . में दी गई जानकारी के आधार पर इस तालिका को कम से कम आंशिक रूप से पॉप्युलेट कर सकता हूं - इस तथ्य का लाभ उठाते हुए कि डेटा / लॉग और अन्य फ़ोल्डरों का नाम उदाहरण के नाम पर रखा गया है:

INSERT dbo.PMAG_Secondaries( DatabaseName, ServerInstance, CommonFolder, DataFolder, LogFolder, StandByLocation) Select DatabaseName =N'UserData', ServerInstance =name, CommonFolder ='C:\temp\Peon' + RIGHT(name, 1) + '\', DataFolder ='C:\Program Files\Microsoft SQL Server\MSSQL12.PEON' + RIGHT(name, 1) + '\MSSQL\DATA\', LogFolder ='C:\Program Files\Microsoft SQL Server\ MSSQL12.PEON' + RIGHT(name, 1) + '\MSSQL\DATA\', StandByLocation ='C:\temp\Peon' + RIGHT(name, 1) + '\' sys.servers से जहां नाम LIKE N' .\PEON[1-4]';

मुझे व्यक्तिगत लॉग बैकअप को ट्रैक करने के लिए एक तालिका की भी आवश्यकता है (केवल अंतिम नहीं), क्योंकि कई मामलों में मुझे अनुक्रम में एकाधिक लॉग फ़ाइलों को पुनर्स्थापित करने की आवश्यकता होगी। मुझे यह जानकारी msdb.dbo.backupset . से मिल सकती है , लेकिन स्थान जैसी चीज़ों को प्राप्त करना कहीं अधिक जटिल है - और हो सकता है कि अन्य कार्यों पर मेरा नियंत्रण न हो जो बैकअप इतिहास को साफ़ कर सकते हैं।

टेबल dbo.PMAG_LogBackupHistory (डेटाबेसनाम SYSNAME, ServerInstance SYSNAME, BackupSetID INT NOT NULL, लोकेशन VARCHAR (2000) नॉट न्यूल, बैकअपटाइम डेटाटाइम नॉट न्यूल डिफॉल्ट SYSDATETIME (), CONSTRAINT PK_LBH प्राथमिक कुंजी (डेटाबेसनाम, सेट सर्वर इंस्टेंस) FK_LBH_DBs विदेशी कुंजी (डेटाबेसनाम) संदर्भ dbo.PMAG_Databases(DatabaseName), CONSTRAINT FK_LBH_Sec FOREIGN KEY(DatabaseName, ServerInstance) संदर्भ dbo.PMAG_Secondaries(DatabaseName, ServerInstance));

आप सोच सकते हैं कि प्रत्येक सेकेंडरी के लिए एक पंक्ति को स्टोर करना और प्रत्येक बैकअप के स्थान को स्टोर करना बेकार है, लेकिन यह भविष्य के प्रूफिंग के लिए है - उस मामले को संभालने के लिए जहां आप किसी सेकेंडरी के लिए कॉमनफोल्डर को स्थानांतरित करते हैं।

और अंत में लॉग पुनर्स्थापित करने का इतिहास, किसी भी बिंदु पर, मैं देख सकता हूं कि कौन से लॉग पुनर्स्थापित किए गए हैं और कहां, और पुनर्स्थापना कार्य केवल उन लॉग को पुनर्स्थापित करना सुनिश्चित कर सकता है जिन्हें पहले से पुनर्स्थापित नहीं किया गया है:

टेबल dbo.PMAG_LogRestoreHistory (डेटाबेसनाम SYSNAME, ServerInstance SYSNAME, BackupSetID INT, रिस्टोरटाइम DATETIME, CONSTRAINT PK_LRH प्राथमिक कुंजी (डेटाबेसनाम, सर्वरइंस्टेंस, बैकअपसेटआईडी), CONSTRAINT FK_LRH_DBIN FOREIGN KEY(DatabaseName, ServerInstance) संदर्भ dbo.PMAG_Secondaries(DatabaseName, ServerInstance));

चरण 5 - सेकेंडरी प्रारंभ करें

हमें एक संग्रहीत प्रक्रिया की आवश्यकता है जो एक बैकअप फ़ाइल उत्पन्न करेगी (और इसे विभिन्न उदाहरणों के लिए आवश्यक किसी भी स्थान पर मिरर करें), और हम उन सभी को स्टैंडबाय में रखने के लिए प्रत्येक सेकेंडरी में एक लॉग को पुनर्स्थापित करेंगे। इस बिंदु पर वे सभी केवल-पढ़ने के लिए उपलब्ध होंगे, लेकिन किसी एक समय में केवल एक ही "वर्तमान" स्टैंडबाय होगा। यह संग्रहीत कार्यविधि है जो पूर्ण और लेन-देन लॉग बैकअप दोनों को संभालेगी; जब एक पूर्ण बैकअप का अनुरोध किया जाता है, और @init 1 पर सेट है, यह स्वचालित रूप से लॉग शिपिंग को फिर से प्रारंभ करता है।

CREATE PROCEDURE [dbo].[PMAG_Backup] @dbname SYSNAME, @type CHAR(3) ='bak', -- या 'trn' @init BIT =0 -- केवल 'bak' ASBEGIN SET NOCOUNT ON; - एक फ़ाइल नाम पैटर्न उत्पन्न करें DECLARE @now DATETIME =SYSDATETIME (); DECLARE @fn NVARCHAR(256) =@dbname + N'_' + CONVERT(CHAR(8), @now, 112) + RIGHT(REPLICATE('0',6) + CONVERT(VARCHAR(32), DATEDIFF(Second) , CONVERT(दिनांक, @now), @now)), 6) + N'.' + @ प्रकार; -- प्रत्येक विशिष्ट कॉमनफोल्डर DECLARE @sql NVARCHAR(MAX) =N'BACKUP' + CASE @type के लिए MIRROR TO के साथ बैकअप कमांड जेनरेट करें जब 'बेक' तब N' DATABASE 'ELSE N' LOG ' END + QUOTENAME(@dbname) + '' + स्टफ ((अलग-अलग चार्ज चुनें(13) + चार्ज(10) + एन' मिरर टू डिस्क ='''' + s.CommonFolder + @fn + '''' dbo से। PMAG_Secondaries AS जहां s.DatabaseName =@dbname फॉर XML पाथ(''), TYPE).value(N'.[1]',N'nvarchar(max)'),1,9,N'') + N' with NAME =N'' ' + @dbname + CASE @type जब 'बेक' तब N'_PMAGFull' ELSE N'_PMAGLog' END + ''', INIT, FORMAT' + केस जब लेफ्ट (कन्वर्ट (NVARCHAR(128)), सर्वरप्रॉपर्टी (एन'संस्करण') )), 3) IN (N'Dev', N'Ent') THEN N', COMPRESSION;' अन्य एन';' अंत; EXEC [मास्टर] .sys.sp_executesql @sql; IF @type ='bak' और @init =1 -- लॉग शिपिंग प्रारंभ करें EXEC dbo.PMAG_InitializeSecondaries @dbname =@dbname, @fn =@fn; END IF @type ='trn' BEGIN -- इस तथ्य को रिकॉर्ड करें कि हमने एक लॉग INSERT dbo का बैकअप लिया है। .backup_set_id), स्थान =s.CommonFolder + @fn से msdb.dbo.backupset AS b क्रॉस जॉइन dbo.PMAG_Secondaries AS जहाँ b.name =@dbname + N'_PMAGLog' और s.DatabaseName =@dbname ग्रुप बाय एस। सर्वर इंस्टेंस, s.CommonFolder + @fn; -- एक बार जब हम लॉग का बैकअप ले लेते हैं, -- उन्हें अगले माध्यमिक EXEC dbo.PMAG_RestoreLogs @dbname =@dbname पर पुनर्स्थापित करें; ENDEND

यह बदले में दो प्रक्रियाओं को कॉल करता है जिन्हें आप अलग से कॉल कर सकते हैं (लेकिन सबसे अधिक संभावना नहीं होगी)। सबसे पहले, वह प्रक्रिया जो पहले रन पर सेकेंडरी को इनिशियलाइज़ करेगी:

ALTER PROCEDURE dbo.PMAG_InitializeSecondaries @dbname SYSNAME, @fn VARCHAR(512)ASBEGIN SET NOCOUNT ON; -- मौजूदा इतिहास/सेटिंग्स को साफ़ करें (चूंकि यह एक पुन:init हो सकता है) DELETE dbo.PMAG_LogBackupHistory WHERE DatabaseName =@dbname; DELETE dbo.PMAG_LogRestoreHistory जहां डेटाबेसनाम =@dbname; अद्यतन dbo.PMAG_Secondaries SET IsCurrentStandby =0 जहाँ डेटाबेसनाम =@dbname; DECLARE @sql NVARCHAR(MAX) =N'', @files NVARCHAR(MAX) =N''; -- तार्किक फ़ाइल नामों को जानने की आवश्यकता है - दो से अधिक SET @sql =N'SELECT @files =(चयन N'', MOVE N'''''' + name + '''''' N से अधिक हो सकते हैं ''''$'' + मामला [प्रकार] जब 0 तब N''df'' जब 1 फिर N''lf'' END + ''$''''' से '+ QUOTENAME(@dbname) + '.sys.database_files जहां [टाइप] IN (0,1) XML पाथ के लिए, TYPE).value(N''.[1]'',N''nvarchar(max)'');'; EXEC Master.sys.sp_executesql @sql, N'@files NVARCHAR(MAX) OUTPUT', @files =@files OUTPUT; सेट @ एसक्यूएल =एन ''; - पुनर्स्थापित करें - मूव के साथ डेटा/लॉग फ़ाइलों के भौतिक पथ की आवश्यकता है - यह असफल हो सकता है, जाहिर है, अगर वे पथ + नाम पहले से ही किसी अन्य डीबी के लिए मौजूद हैं @sql + =N'EXEC '+ QUOTENAME (सर्वर इंस्टेंस) + एन' .master.sys.sp_executesql N''RESTORE DATABASE' + QUOTENAME(@dbname) + N' डिस्क से =N''''' + CommonFolder + @fn + N'''''' + N' रिप्लेस के साथ, NORECOVERY ' + REPLACE(REPLACE(@files, N'$df$', DataFolder + @dbname + N'.mdf'), N'$lf$', LogFolder + @dbname + N'.ldf'), N '''', एन ''''''') + एन';'';' + CHAR(13) + CHAR(10) dbo.PMAG_Secondaries से जहां DatabaseName =@dbname; EXEC [मास्टर] .sys.sp_executesql @sql; -- इस डेटाबेस के लिए एक लॉग बैकअप करें EXEC dbo.PMAG_Backup @dbname =@dbname, @type ='trn'; -- लॉग पुनर्स्थापित करें EXEC dbo.PMAG_RestoreLogs @dbname =@dbname, @PrepareAll =1;END

और फिर वह प्रक्रिया जो लॉग को पुनर्स्थापित करेगी:

CREATE PROCEDURE dbo.PMAG_RestoreLogs @dbname SYSNAME, @PrepareAll BIT =0ASBEGIN SET NOCOUNT ON; DECLARE @StandbyInstance SYSNAME, @CurrentInstance SYSNAME, @BackupSetID INT, @Location VARCHAR (512), @StandByLocation VARCHAR (512), @sql NVARCHAR (MAX), @rn INT; - "अगला" स्टैंडबाय इंस्टेंस प्राप्त करें dbo.PMAG_Secondaries से @StandbyInstance =MIN (सर्वर इंस्टेंस) चुनें, जहां IsCurrentStandby =0 और ServerInstance> (dbo से सर्वर इंस्टेंस चुनें। PMAG_Secondaries जहां IsCurrentStandBy =1); IF @StandbyInstance IS NULL है - या तो यह अंतिम था या फिर से init BEGIN SELECT @StandbyInstance =MIN(ServerInstance) FROM dbo.PMAG_Secondaries; END - उस उदाहरण को ऊपर और स्टैंडबाय में प्राप्त करें - लॉगबैकुफिस्टोरी में प्रत्येक लॉग के लिए लॉगरेस्टोरहिस्ट्री में नहीं:- पुनर्स्थापित करें, और इसे लॉगरेस्टोरहिस्ट्री में डालें - अंतिम को स्टैंडबाय के रूप में चिह्नित करें - यदि @prepareAll सत्य है, तो अन्य सभी को चिह्नित करें NORECOVERY -- इस मामले में केवल एक ही होना चाहिए, लेकिन केवल चयन bh.BackupSetID, s.ServerInstance, bh.Location, s.StandbyLocation, rn =ROW_NUMBER() OVER (पार्टिशन द्वारा s. सर्वर इंस्टेंस ऑर्डर bh.BackupSetID DESC द्वारा) dbo से। @PrepareAll जब 1 तब s.ServerInstance ELSE @StandbyInstance END और मौजूद नहीं है (dbo.PMAG_LogRestoreHistory AS rh से चुनें जहां DatabaseName =@dbname और ServerInstance =s.ServerInstance और BackupSetID =bh.B ackupSetID ) मामले के अनुसार आदेश s.ServerInstance जब @StandbyInstance तब 1 अन्य 2 समाप्त, bh.BackupSetID; खुला सी; फ़ेच सी में @BackupSetID, @CurrentInstance, @Location, @StandbyLocation, @rn; जबकि @@FETCH_STATUS -1 BEGIN -- किक आउट यूज़र्स - सिंगल_यूसर पर सेट करें और फिर मल्टी SET @sql =N'EXEC ' + QUOTENAME(@CurrentInstance) + N' पर वापस सेट करें। [मास्टर] .sys.sp_executesql '+'N' 'अगर मौजूद है (sys.databases से 1 चुनें जहां नाम =एन''''' + @dbname + ''''' और [राज्य] 1) डेटाबेस बदलना शुरू करें '+ QUOTENAME(@dbname) + N' SINGLE_USER सेट करें ' + तत्काल रोलबैक के साथ; वैकल्पिक डेटाबेस '+ QUOTENAME(@dbname) + एन' सेट MULTI_USER; अंत;'';'; EXEC [मास्टर] .sys.sp_executesql @sql; - लॉग को पुनर्स्थापित करें (यदि यह अंतिम है तो स्टैंडबाय में):SET @sql =N'EXEC ' + QUOTENAME(@CurrentInstance) + N'। [मास्टर] .sys.sp_executesql '+ N'N''RESTORE LOG ' + QUOTENAME(@dbname) + N' डिस्क से =N''''' + @Location + N'''' with' + केस जब @rn =1 और (@CurrentInstance =@StandbyInstance या @PrepareAll =1) फिर N'STANDBY =N''''' + @StandbyLocation + @dbname + N'.standby''''' ELSE N'NORECOVERY' END + N';'';'; EXEC [मास्टर] .sys.sp_executesql @sql; -- इस तथ्य को रिकॉर्ड करें कि हमने लॉग्स INSERT dbo.PMAG_LogRestoreHistory (डेटाबेसनाम, सर्वर इंस्टेंस, बैकअपसेटआईडी, रिस्टोरटाइम) को पुनर्स्थापित किया है, @dbname, @CurrentInstance, @BackupSetID, SYSDATETIME() चुनें; -- नए स्टैंडबाय को चिह्नित करें IF @rn =1 और @CurrentInstance =@StandbyInstance -- यह नया स्टैंडबाय BEGIN UPDATE dbo.PMAG_Secondaries SET IsCurrentStandby =CASE ServerInstance जब @StandbyInstance तब 1 ELSE 0 समाप्त होता है, जहां DatabaseName =@dbname; END FETCH c INTO @BackupSetID, @CurrentInstance, @Location, @StandbyLocation, @rn; अंत बंद सी; रद्द करें c;END

(मुझे पता है कि यह बहुत सारे कोड हैं, और बहुत सारे गुप्त गतिशील एसक्यूएल हैं। मैंने टिप्पणियों के साथ बहुत उदार होने की कोशिश की; अगर कोई ऐसा टुकड़ा है जिससे आपको परेशानी हो रही है, तो कृपया मुझे बताएं।)

तो अब, सिस्टम को चालू करने और चलाने के लिए आपको केवल दो प्रक्रिया कॉल करना है:

EXEC dbo.PMAG_Backup @dbname =N'UserData', @type ='bak', @init =1;EXEC dbo.PMAG_Backup @dbname =N'UserData', @type ='trn';

अब आपको प्रत्येक उदाहरण को डेटाबेस की स्टैंडबाय कॉपी के साथ देखना चाहिए:

और आप देख सकते हैं कि वर्तमान में किसको केवल-पढ़ने के लिए स्टैंडबाय के रूप में काम करना चाहिए:

सेलेक्ट सर्वर इंस्टेंस, IsCurrentStandby फ्रॉम dbo.PMAG_Secondaries WHERE DatabaseName =N'UserData';

चरण 6 - एक ऐसा कार्य बनाएं जो लॉग का बैकअप लेता है / पुनर्स्थापित करता है

आप इस आदेश को हर 15 मिनट में आपके द्वारा निर्धारित कार्य में लगा सकते हैं:

EXEC dbo.PMAG_Backup @dbname =N'UserData', @type ='trn';

यह हर 15 मिनट में सक्रिय माध्यमिक को स्थानांतरित कर देगा, और इसका डेटा पिछले सक्रिय माध्यमिक की तुलना में 15 मिनट ताज़ा होगा। यदि आपके पास अलग-अलग शेड्यूल पर कई डेटाबेस हैं, तो आप कई जॉब बना सकते हैं, या जॉब को अधिक बार शेड्यूल कर सकते हैं और dbo.PMAG_Databases चेक कर सकते हैं। प्रत्येक व्यक्ति के लिए तालिका LogBackupFrequency_Minutes मूल्य निर्धारित करने के लिए कि क्या आपको उस डेटाबेस के लिए बैकअप/पुनर्स्थापना चलाना चाहिए।

चरण 7 - एप्लिकेशन को यह बताने के लिए देखें और प्रक्रिया कि कौन सा स्टैंडबाय सक्रिय है

 क्रिएट व्यू dbo.PMAG_ActiveSecondariesAS सेलेक्ट डेटाबेसनाम, सर्वर इंस्टेंस फ्रॉम dbo.PMAG_Secondaries जहां हैCurrentStandby =1; GO CREATE PROCEDURE dbo.PMAG_GetActiveSecondary @dbname SYSNAMEASBEGIN SET NOCOUNT ON; dbo.PMAG_ActiveSecondaries से सर्वर इंस्टेंस चुनें जहां डेटाबेसनाम =@dbname;ENDGO

मेरे मामले में, मैंने मैन्युअल रूप से सभी UserData . में एक दृश्य संघ बनाया है डेटाबेस ताकि मैं प्रत्येक सेकेंडरी के साथ प्राथमिक डेटा की रीसेंसी की तुलना कर सकूं।

 क्रिएट व्यू dbo.PMAG_CompareRecency_UserDataAS विथ x(ServerInstance, EventTime) AS (चुनें @@SERVERNAME, EventTime From UserData.dbo.LastUpdate UNION ALL SELECT N'.\PEON1', [.\PEON1].UserData.dbo से इवेंटटाइम .LastUpdate UNION ALL SELECT N'.\PEON2', EventTime From [.\PEON2]। N'.\PEON4', इवेंटटाइम [.\PEON4] से चुनें। Age_Seconds =DATEDIFF(SECOND, x.EventTime, SYSDATETIME ()) x बाएँ बाहरी जॉइन dbo से। 

सप्ताहांत से नमूना परिणाम:

चुनें [अभी] =SYSDATETIME (); dbo.PMAG_CompareRecency_UserData ORDER से Age_Seconds DESC;

चरण 8 - सफाई प्रक्रिया

लॉग बैकअप को साफ करना और इतिहास को पुनर्स्थापित करना बहुत आसान है।

CREATE PROCEDURE dbo.PMAG_CleanupHistory @dbname SYSNAME, @DaysOld INT =7ASBEGIN SET NOCOUNT ON; DECLARE @cutoff INT; - यह मानता है कि लॉग बैकअप या तो - सभी सेकेंडरी पर सफल या विफल रहा dbo.PMAG_LogBackupHistory AS bh से चुनें @cutoff =MAX(BackupSetID) जहां डेटाबेसनाम =@dbname और बैकअपटाइम  

अब, आप इसे मौजूदा नौकरी में एक कदम के रूप में जोड़ सकते हैं, या आप इसे पूरी तरह से अलग से या अन्य सफाई दिनचर्या के हिस्से के रूप में शेड्यूल कर सकते हैं।

मैं एक और पोस्ट के लिए फाइल सिस्टम को साफ करना छोड़ दूंगा (और शायद पूरी तरह से एक अलग तंत्र, जैसे पावरशेल या सी # - यह आमतौर पर उस तरह की चीज नहीं है जिसे आप टी-एसक्यूएल करना चाहते हैं)।

चरण 9 - समाधान में वृद्धि करें

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

चर और सीमाएं

ध्यान दें कि मेरे मामले में मैंने प्राथमिक संस्करण के रूप में मानक संस्करण और सभी सेकेंडरी के लिए एक्सप्रेस संस्करण का उपयोग किया था। आप बजट पैमाने पर एक कदम आगे जा सकते हैं और यहां तक ​​कि एक्सप्रेस संस्करण को प्राथमिक के रूप में उपयोग कर सकते हैं - बहुत से लोग सोचते हैं कि एक्सप्रेस संस्करण लॉग शिपिंग का समर्थन नहीं करता है, जबकि वास्तव में यह केवल विज़ार्ड है जो प्रबंधन स्टूडियो के संस्करणों में मौजूद नहीं था SQL सर्वर 2012 सर्विस पैक 1 से पहले एक्सप्रेस। उसने कहा, चूंकि एक्सप्रेस संस्करण SQL सर्वर एजेंट का समर्थन नहीं करता है, इस परिदृश्य में इसे एक प्रकाशक बनाना मुश्किल होगा - आपको संग्रहीत प्रक्रियाओं को कॉल करने के लिए अपने स्वयं के शेड्यूलर को कॉन्फ़िगर करना होगा (सी # विंडोज टास्क शेड्यूलर, पॉवरशेल जॉब्स, या एसक्यूएल सर्वर एजेंट जॉब्स द्वारा एक और इंस्टेंस पर चलाया जाने वाला कमांड लाइन ऐप)। किसी भी छोर पर एक्सप्रेस का उपयोग करने के लिए, आपको यह भी आश्वस्त होना होगा कि आपकी डेटा फ़ाइल 10GB से अधिक नहीं होगी, और आपके प्रश्न उस संस्करण की मेमोरी, सीपीयू और फीचर सीमाओं के साथ ठीक काम करेंगे। मैं किसी भी तरह से यह सुझाव नहीं दे रहा हूं कि एक्सप्रेस आदर्श है; मैंने इसे केवल यह प्रदर्शित करने के लिए उपयोग किया है कि बहुत लचीली पठनीय सेकेंडरी मुफ्त में (या इसके बहुत करीब) होना संभव है।

इसके अलावा, मेरे परिदृश्य में ये अलग-अलग उदाहरण सभी एक ही वीएम पर रहते हैं, लेकिन इसे उस तरह से काम करने की ज़रूरत नहीं है - आप कई सर्वरों में इंस्टेंस फैला सकते हैं; या, आप दूसरी तरफ जा सकते हैं, और एक ही उदाहरण पर, अलग-अलग नामों के साथ डेटाबेस की विभिन्न प्रतियों को पुनर्स्थापित कर सकते हैं। इन विन्यासों के लिए मेरे द्वारा ऊपर निर्धारित किए गए न्यूनतम परिवर्तनों की आवश्यकता होगी। और आप कितने डेटाबेस को पुनर्स्थापित करते हैं, और कितनी बार, यह पूरी तरह आप पर निर्भर है - हालांकि एक व्यावहारिक ऊपरी सीमा होगी (जहां [average query time] > [number of secondaries] x [log backup interval] )।

अंत में, इस दृष्टिकोण के साथ निश्चित रूप से कुछ सीमाएं हैं। एक गैर-विस्तृत सूची:

  1. जबकि आप अपने स्वयं के समय पर पूर्ण बैकअप लेना जारी रख सकते हैं, लॉग बैकअप को आपके एकमात्र लॉग बैकअप तंत्र के रूप में कार्य करना चाहिए। यदि आपको अन्य उद्देश्यों के लिए लॉग बैकअप को संग्रहीत करने की आवश्यकता है, तो आप इस समाधान से अलग से लॉग का बैकअप नहीं ले पाएंगे, क्योंकि वे लॉग श्रृंखला में हस्तक्षेप करेंगे। इसके बजाय, आप अतिरिक्त MIRROR TO adding जोड़ने पर विचार कर सकते हैं मौजूदा लॉग बैकअप स्क्रिप्ट के लिए तर्क, यदि आपको कहीं और उपयोग किए गए लॉग की प्रतियां रखने की आवश्यकता है।
  2. While "Poor Man's Availability Groups" may seem like a clever name, it can also be a bit misleading. This solution certainly lacks many of the HA/DR features of Availability Groups, including failover, automatic page repair, and support in the UI, Extended Events and DMVs. This was only meant to provide the ability for non-Enterprise customers to have an infrastructure that supports multiple readable secondaries.
  3. I tested this on a very isolated VM system with no concurrency. This is not a complete solution and there are likely dozens of ways this code could be made tighter; as a first step, and to focus on the scaffolding and to show you what's possible, I did not build in bulletproof resiliency. You will need to test it at your scale and with your workload to discover your breaking points, and you will also potentially need to deal with transactions over linked servers (always fun) and automating the re-initialization in the event of a disaster.

The "Insurance Policy"

Log shipping also offers a distinct advantage over many other solutions, including Availability Groups, mirroring and replication:a delayed "insurance policy" as I like to call it. At my previous job, I did this with full backups, but you could easily use log shipping to accomplish the same thing:I simply delayed the restores to one of the secondary instances by 24 hours. This way, I was protected from any client "shooting themselves in the foot" going back to yesterday, and I could get to their data easily on the delayed copy, because it was 24 hours behind. (I implemented this the first time a customer ran a delete without a where clause, then called us in a panic, at which point we had to restore their database to a point in time before the delete – which was both tedious and time consuming.) You could easily adapt this solution to treat one of these instances not as a read-only secondary but rather as an insurance policy. More on that perhaps in another post.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. टी-एसक्यूएल सेट ऑपरेटर्स पार्ट 2:इंटरसेक्ट और एक्सेप्ट

  2. शुरुआती के लिए एसक्यूएल नहीं ऑपरेटर

  3. बजट पर पठनीय माध्यमिक

  4. ऑप्टिमाइज़ेशन थ्रेशोल्ड - डेटा को समूहीकृत और एकत्र करना, भाग 2

  5. 2008 R2 बग फिक्स जो RCSI को तोड़ता है