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

लॉग बफर फ्लश को समझना

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

लॉग बफर फ्लश

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

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

SQL सर्वर के लॉगिंग आर्किटेक्चर में मुश्किल हिस्सा यह है कि लॉग राइट्स अनुक्रमिक होते हैं। यदि SQL सर्वर ने डिस्क पर लॉग राइट को कम करने के लिए किसी प्रकार के लॉग बफर का उपयोग नहीं किया होता, तो राइट-इंटेंसिव सिस्टम-विशेष रूप से जिनमें बहुत सारे छोटे लेनदेन शामिल होते हैं-जल्दी से भयानक लॉग-राइट-संबंधित प्रदर्शन बाधाओं में चले जाते हैं।

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

  • SQL सर्वर को [पूरी तरह से टिकाऊ] लेन-देन का एक प्रतिबद्ध अनुरोध मिलता है जो डेटा को बदलता है [tempdb के अलावा किसी अन्य डेटाबेस में]
  • लॉग बफर भर जाता है, इसकी 60 केबी क्षमता तक पहुंच जाता है
  • SQL सर्वर को गंदे डेटा पृष्ठों को सख्त करने की आवश्यकता होती है, उदाहरण के लिए, एक चेकपॉइंट प्रक्रिया के दौरान, और उन पृष्ठों में परिवर्तनों का प्रतिनिधित्व करने वाले लॉग रिकॉर्ड अभी तक कठोर नहीं हुए थे (आगे लॉगिंग लिखें , या संक्षेप में वाल)
  • आप मैन्युअल रूप से sys.sp_flush_log प्रक्रिया निष्पादित करके लॉग बफर फ्लश का अनुरोध करते हैं
  • SQL सर्वर एक नया अनुक्रम-कैश-संबंधित पुनर्प्राप्ति मान लिखता है [tempdb के अलावा किसी अन्य डेटाबेस में]

यदि आप अभी के लिए वर्ग कोष्ठक में दी गई जानकारी को अनदेखा करते हैं, तो पहली चार शर्तें बहुत स्पष्ट होनी चाहिए। आखिरी वाला शायद अभी तक स्पष्ट नहीं है, लेकिन मैं इसे बाद में लेख में विस्तार से बताऊंगा।

SQL सर्वर एक लॉग बफर फ्लश को पूरा करने के लिए I/O ऑपरेशन के लिए प्रतीक्षा करता है, जो WRITELOG प्रतीक्षा प्रकार द्वारा परिलक्षित होता है।

तो, यह जानकारी इतनी दिलचस्प क्यों है, और हम इसके साथ क्या करते हैं? लॉग बफर फ्लश को ट्रिगर करने वाली स्थितियों को समझने से आपको यह पता लगाने में मदद मिल सकती है कि कुछ कार्यभार संबंधित बाधाओं का अनुभव क्यों करते हैं। साथ ही, कुछ मामलों में ऐसी बाधाओं को कम करने या समाप्त करने के लिए आप कुछ कदम उठा सकते हैं। मैं एक बड़े लेनदेन बनाम कई छोटे लेनदेन, पूरी तरह से टिकाऊ बनाम विलंबित टिकाऊ लेनदेन, उपयोगकर्ता डेटाबेस बनाम tempdb और अनुक्रम ऑब्जेक्ट कैशिंग जैसे कई उदाहरणों को कवर करूंगा।

एक बड़ा लेन-देन बनाम कई छोटे लेन-देन

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

हालांकि अक्सर ऐसा नहीं होता है, अगर आपके पास एक सत्र में बहुत सारे छोटे बदलाव सबमिट होते हैं, तो काम को अनुकूलित करने का एक सरल और प्रभावी तरीका है कि कई छोटे लेन-देन के बजाय एक ही बड़े लेनदेन में परिवर्तन लागू करें।

निम्नलिखित सरलीकृत उदाहरण पर विचार करें (यहां PerformanceV3 डाउनलोड करें):

SET NOCOUNT ON;
 
USE PerformanceV3;
 
ALTER DATABASE PerformanceV3 SET DELAYED_DURABILITY = Disabled; -- default
 
DROP TABLE IF EXISTS dbo.T1;
 
CREATE TABLE dbo.T1(col1 INT NOT NULL);
 
DECLARE @i AS INT = 1;
 
WHILE @i <= 1000000
BEGIN
 
  BEGIN TRAN
    INSERT INTO dbo.T1(col1) VALUES(@i);
  COMMIT TRAN;
 
  SET @i += 1;
END;

यह कोड 1,000,000 छोटे लेनदेन निष्पादित करता है जो उपयोगकर्ता डेटाबेस में डेटा बदलते हैं। यह कार्य कम से कम 1,000,000 लॉग बफर फ्लश को ट्रिगर करेगा। लॉग बफर भरने के कारण आपको कुछ अतिरिक्त मिल सकते हैं। लॉग बफर फ्लश की संख्या की गणना करने और कार्य को पूरा करने में लगने वाले समय को मापने के लिए आप निम्न परीक्षण टेम्पलेट का उपयोग कर सकते हैं:

-- Test template
 
-- ... Preparation goes here ...
 
-- Count log flushes and measure time
DECLARE @logflushes AS INT, @starttime AS DATETIME2, @duration AS INT;
 
-- Stats before
SET @logflushes = ( SELECT cntr_value FROM sys.dm_os_performance_counters
                    WHERE counter_name = 'Log Flushes/sec'
                      AND instance_name = @db );
 
SET @starttime = SYSDATETIME();
 
-- ... Actual work goes here ...
 
-- Stats after
SET @duration = DATEDIFF(second, @starttime, SYSDATETIME());
SET @logflushes = ( SELECT cntr_value FROM sys.dm_os_performance_counters
                    WHERE counter_name = 'Log Flushes/sec'
                      AND instance_name = @db ) - @logflushes;
 
SELECT 
  @duration AS durationinseconds,
  @logflushes AS logflushes;

भले ही प्रदर्शन काउंटर का नाम लॉग फ्लश/सेकंड है, यह वास्तव में अब तक लॉग बफर फ्लश की संख्या जमा करता रहता है। इसलिए, कोड कार्य द्वारा उत्पन्न लॉग फ्लश की संख्या का पता लगाने के लिए काम के बाद की गणना से पहले की संख्या को घटा देता है। यह कोड कार्य को पूरा करने में लगने वाले समय को सेकंडों में भी मापता है। भले ही मैं यहां ऐसा नहीं करता हूं, यदि आप चाहते हैं, तो आप fn_dblog की पूर्व-कार्य और कार्य-बाद की स्थितियों को क्वेरी करके लॉग रिकॉर्ड की संख्या और कार्य द्वारा लॉग पर लिखे गए आकार का भी पता लगा सकते हैं। समारोह।

ऊपर दिए गए हमारे उदाहरण के लिए, निम्नलिखित वह भाग है जिसे आपको परीक्षण टेम्पलेट के तैयारी अनुभाग में रखने की आवश्यकता है:

-- Preparation
SET NOCOUNT ON;
USE PerformanceV3;
 
ALTER DATABASE PerformanceV3 SET DELAYED_DURABILITY = Disabled;
 
DROP TABLE IF EXISTS dbo.T1;
 
CREATE TABLE dbo.T1(col1 INT NOT NULL);
 
DECLARE @db AS sysname = N'PerformanceV3';
 
DECLARE @logflushes AS INT, @starttime AS DATETIME2, @duration AS INT;

और निम्नलिखित वह हिस्सा है जिसे आपको वास्तविक कार्य अनुभाग में रखने की आवश्यकता है:

-- Actual work
DECLARE @i AS INT = 1;
 
WHILE @i <= 1000000
BEGIN
 
  BEGIN TRAN
    INSERT INTO dbo.T1(col1) VALUES(@i);
  COMMIT TRAN;
 
  SET @i += 1;
END;

कुल मिलाकर, आपको निम्न कोड मिलता है:

-- Example test with many small fully durable transactions in user database
-- ... Preparation goes here ...
 
-- Preparation
SET NOCOUNT ON;
USE PerformanceV3;
 
ALTER DATABASE PerformanceV3 SET DELAYED_DURABILITY = Disabled;
 
DROP TABLE IF EXISTS dbo.T1;
 
CREATE TABLE dbo.T1(col1 INT NOT NULL);
 
DECLARE @db AS sysname = N'PerformanceV3';
 
DECLARE @logflushes AS INT, @starttime AS DATETIME2, @duration AS INT;
 
-- Stats before
SET @logflushes = ( SELECT cntr_value FROM sys.dm_os_performance_counters
 
                    WHERE counter_name = 'Log Flushes/sec'
 
                      AND instance_name = @db );
 
SET @starttime = SYSDATETIME();
 
-- ... Actual work goes here ...
 
-- Actual work
DECLARE @i AS INT = 1;
 
WHILE @i <= 1000000
BEGIN
 
  BEGIN TRAN
    INSERT INTO dbo.T1(col1) VALUES(@i);
  COMMIT TRAN;
 
  SET @i += 1;
END;
 
-- Stats after
SET @duration = DATEDIFF(second, @starttime, SYSDATETIME());
 
SET @logflushes = ( SELECT cntr_value FROM sys.dm_os_performance_counters
                    WHERE counter_name = 'Log Flushes/sec'
                      AND instance_name = @db ) - @logflushes;
 
SELECT 
  @duration AS durationinseconds,
  @logflushes AS logflushes;

इस कोड को मेरे सिस्टम पर पूरा होने में 193 सेकंड का समय लगा, और 1,000,036 लॉग बफर फ्लश को ट्रिगर किया। यह बहुत धीमा है, लेकिन बड़ी संख्या में लॉग फ्लश के कारण इसे समझाया जा सकता है।

सामान्य ओएलटीपी वर्कलोड में अलग-अलग सत्र अलग-अलग छोटे लेनदेन में एक साथ छोटे बदलाव प्रस्तुत करते हैं, इसलिए ऐसा नहीं है कि आपके पास वास्तव में एक बड़े लेनदेन में बहुत सारे छोटे बदलावों को समाहित करने का विकल्प है। हालाँकि, यदि आपकी स्थिति यह है कि सभी छोटे परिवर्तन एक ही सत्र से सबमिट किए जाते हैं, तो कार्य को अनुकूलित करने का एक सरल तरीका यह है कि इसे एक ही लेन-देन में समाहित किया जाए। इससे आपको दो मुख्य फायदे होंगे। एक यह है कि आपका काम कम लॉग रिकॉर्ड लिखेगा। 1,000,000 छोटे लेनदेन के साथ, प्रत्येक लेनदेन वास्तव में तीन लॉग रिकॉर्ड लिखता है:एक लेनदेन शुरू करने के लिए, एक परिवर्तन के लिए, और एक लेनदेन करने के लिए। इसलिए, जब आप एक बड़े लेन-देन के रूप में निष्पादित होते हैं, तो आप लगभग 3,000,0000 लेनदेन लॉग रिकॉर्ड बनाम 1,000,000 से थोड़ा अधिक देख रहे होते हैं। लेकिन इससे भी महत्वपूर्ण बात यह है कि एक बड़े लेन-देन के साथ अधिकांश लॉग फ्लश केवल तभी ट्रिगर होते हैं जब लॉग बफर भर जाता है, साथ ही लेन-देन के अंत में एक और लॉग फ्लश होता है जब यह होता है। प्रदर्शन अंतर काफी महत्वपूर्ण हो सकता है। एक बड़े लेन-देन में कार्य का परीक्षण करने के लिए, परीक्षण टेम्पलेट के वास्तविक कार्य भाग में निम्न कोड का उपयोग करें:

-- Actual work
BEGIN TRAN;
 
DECLARE @i AS INT = 1;
 
WHILE @i <= 1000000
BEGIN
 
  INSERT INTO dbo.T1(col1) VALUES(@i);
  SET @i += 1;
 
END;
 
COMMIT TRAN;

मेरे सिस्टम पर, यह कार्य 7 सेकंड में पूरा हुआ, और 1,758 लॉग फ्लश चालू हो गया। यहां दो विकल्पों के बीच तुलना की गई है:

#transactions  log flushes  duration in seconds
-------------- ------------ --------------------
1000000        1000036      193
1              1758         7

लेकिन फिर से, सामान्य OLTP कार्यभार में, आपके पास वास्तव में विभिन्न सत्रों से सबमिट किए गए कई छोटे लेन-देन को एक ही सत्र से सबमिट किए गए एक बड़े लेन-देन से बदलने का विकल्प नहीं होता है।

पूरी तरह टिकाऊ बनाम विलंबित टिकाऊ लेनदेन

SQL सर्वर 2014 से शुरू करके, आप विलंबित स्थायित्व नामक एक सुविधा का उपयोग कर सकते हैं जो आपको सामान्य पूर्ण स्थायित्व गारंटी का त्याग करके, कई छोटे लेनदेन के साथ कार्यभार के प्रदर्शन में सुधार करने की अनुमति देता है, भले ही विभिन्न सत्रों द्वारा प्रस्तुत किया गया हो। विलंबित टिकाऊ लेन-देन करते समय, SQL सर्वर लॉग बफ़र को ट्रिगर किए बिना, लॉग बफ़र को कमिट लॉग रिकॉर्ड लिखे जाने के तुरंत बाद कमिट को स्वीकार करता है। लॉग बफर किसी भी अन्य उपरोक्त शर्तों के कारण फ्लश हो जाता है जैसे कि जब यह भरता है, लेकिन तब नहीं जब एक विलंबित टिकाऊ लेनदेन होता है।

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

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

विलंबित टिकाऊ लेनदेन के साथ काम करने के लिए, आपको DELAYED_DURABILITY नामक एक डेटाबेस विकल्प सेट करना होगा। यह विकल्प तीन मानों में से एक पर सेट किया जा सकता है:

  • अक्षम (डिफ़ॉल्ट):डेटाबेस में सभी लेनदेन पूरी तरह से टिकाऊ होते हैं, और इसलिए प्रत्येक प्रतिबद्ध लॉग बफर फ्लश को ट्रिगर करता है
  • जबरन :डेटाबेस में सभी लेनदेन टिकाऊ होते हैं, और इसलिए लॉग बफर फ्लश को ट्रिगर नहीं करते हैं
  • अनुमति है :जब तक अन्यथा उल्लेख नहीं किया जाता है, लेनदेन पूरी तरह से टिकाऊ होते हैं और उन्हें करने से लॉग बफर फ्लश शुरू हो जाता है; हालांकि, यदि आप COMMIT TRAN स्टेटमेंट या परमाणु ब्लॉक (मूल रूप से संकलित प्रो) में विकल्प DELAYED_DURABILITY =ON का उपयोग करते हैं, तो वह विशेष लेनदेन टिकाऊ होता है और इसलिए इसे करने से लॉग बफर फ्लश ट्रिगर नहीं होता है

एक परीक्षण के रूप में, हमारे परीक्षण टेम्पलेट के तैयारी अनुभाग में निम्नलिखित कोड का उपयोग करें (ध्यान दें कि डेटाबेस विकल्प जबरन पर सेट है):

-- Preparation
SET NOCOUNT ON;
USE PerformanceV3; -- http://tsql.solidq.com/SampleDatabases/PerformanceV3.zip
 
ALTER DATABASE PerformanceV3 SET DELAYED_DURABILITY = Forced;
 
DROP TABLE IF EXISTS dbo.T1;
 
CREATE TABLE dbo.T1(col1 INT NOT NULL);
 
DECLARE @db AS sysname = N'PerformanceV3';

और वास्तविक कार्य अनुभाग में निम्नलिखित कोड का उपयोग करें (नोटिस, 1,000,000 छोटे लेनदेन):

-- Actual work
DECLARE @i AS INT = 1;
 
WHILE @i <= 1000000
BEGIN
 
  BEGIN TRAN
    INSERT INTO dbo.T1(col1) VALUES(@i);
  COMMIT TRAN;
 
  SET @i += 1;
END;

वैकल्पिक रूप से, आप डेटाबेस स्तर पर अनुमत मोड का उपयोग कर सकते हैं, और फिर COMMIT TRAN कमांड में, (DELAYED_DURABILITY =ON) के साथ जोड़ें।

मेरे सिस्टम पर, काम को पूरा होने में 22 सेकंड लगे और 95,407 लॉग फ्लश चालू हो गए। यह कार्य को एक बड़े लेन-देन (7 सेकंड) के रूप में चलाने से अधिक लंबा है क्योंकि अधिक लॉग रिकॉर्ड उत्पन्न होते हैं (याद रखें, प्रति लेनदेन, लेन-देन शुरू करने के लिए एक, परिवर्तन के लिए एक, और लेनदेन करने के लिए एक); हालाँकि, यह 193 सेकंड की तुलना में बहुत तेज़ है कि इसने 1,000,000 पूरी तरह से टिकाऊ लेनदेन का उपयोग करके काम पूरा किया क्योंकि लॉग फ्लश की संख्या 1,000,000 से घटकर 100,000 से कम हो गई। साथ ही, विलंबित स्थायित्व के साथ, आपको प्रदर्शन लाभ मिलेगा, भले ही लेनदेन विभिन्न सत्रों से सबमिट किए गए हों, जहां यह एक बड़े लेनदेन का उपयोग करने का विकल्प नहीं है।

यह प्रदर्शित करने के लिए कि बड़े लेन-देन के रूप में कार्य करते समय विलंबित स्थायित्व का उपयोग करने से कोई लाभ नहीं है, अंतिम परीक्षण के तैयारी भाग में समान कोड रखें, और वास्तविक कार्य भाग में निम्न कोड का उपयोग करें:

-- Actual work
BEGIN TRAN;
 
DECLARE @i AS INT = 1;
 
WHILE @i <= 1000000
BEGIN
  INSERT INTO dbo.T1(col1) VALUES(@i);
 
  SET @i += 1;
END;
 
COMMIT TRAN;

मुझे 8 सेकंड का रन टाइम (एक बड़े पूरी तरह से टिकाऊ लेनदेन के लिए 7 की तुलना में) और 1,759 लॉग फ्लश (1,758 की तुलना में) मिला। संख्याएं अनिवार्य रूप से समान हैं, लेकिन विलंबित टिकाऊ लेन-देन से आपको डेटा हानि का जोखिम होता है।

यहां सभी चार परीक्षणों के लिए प्रदर्शन संख्याओं का सारांश दिया गया है:

durability          #transactions  log flushes  duration in seconds
------------------- -------------- ------------ --------------------
full                1000000        1000036      193
full                1              1758         7
delayed             1000000        95407        22
delayed             1              1759         8

स्टोरेज क्लास मेमोरी

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

आप इसे SQL Server 2016 SP1 या बाद में स्टोरेज क्लास मेमोरी, उर्फ ​​NVDIMM-N नॉनवोलेटाइल स्टोरेज का उपयोग करके प्राप्त कर सकते हैं। यह हार्डवेयर अनिवार्य रूप से एक मेमोरी मॉड्यूल है जो आपको मेमोरी-ग्रेड प्रदर्शन देता है, लेकिन वहां की जानकारी बनी रहती है और इसलिए बिजली जाने पर खो नहीं जाती है। SQL सर्वर 2016 SP1 में जोड़ आपको लॉग बफ़र को ऐसे हार्डवेयर पर एक स्थायी के रूप में कॉन्फ़िगर करने देता है। ऐसा करने के लिए, आप एससीएम को विंडोज़ में वॉल्यूम के रूप में सेट करते हैं और इसे डायरेक्ट एक्सेस मोड (डीएएक्स) वॉल्यूम के रूप में प्रारूपित करते हैं। फिर आप सामान्य ALTER DATABASE ADD LOG FILE कमांड का उपयोग करके डेटाबेस में एक लॉग फ़ाइल जोड़ते हैं, जिसमें फ़ाइल पथ DAX वॉल्यूम पर रहता है, और आकार को 20 एमबी पर सेट करता है। SQL सर्वर, बदले में, यह पहचानता है कि यह एक DAX वॉल्यूम है, और उस क्षण से लॉग बफर को उस वॉल्यूम पर एक स्थायी के रूप में मानता है। लेन-देन प्रतिबद्ध घटनाएं अब लॉग बफर फ्लश को ट्रिगर नहीं करती हैं, बल्कि एक बार लॉग बफर में प्रतिबद्ध दर्ज होने के बाद, SQL सर्वर जानता है कि यह वास्तव में कायम है, और इसलिए कॉलर को नियंत्रण लौटाता है। जब लॉग बफर भर जाता है, तो SQL सर्वर इसे पारंपरिक स्टोरेज पर ट्रांजेक्शन लॉग फाइल में फ्लश कर देता है।

प्रदर्शन संख्या सहित इस सुविधा के बारे में अधिक जानकारी के लिए, केविन फ़ार्ली द्वारा विंडोज सर्वर 2016/एसक्यूएल सर्वर 2016 एसपी1 में स्टोरेज क्लास मेमोरी का उपयोग करके ट्रांजेक्शन कमिट लेटेंसी एक्सेलेरेशन देखें।

उत्सुकता से, SQL सर्वर 2019 केवल स्थायी लॉग कैश परिदृश्य से परे स्टोरेज क्लास मेमोरी के लिए समर्थन बढ़ाता है। यह ऐसे हार्डवेयर पर डेटा फ़ाइलों, लॉग फ़ाइलों और इन-मेमोरी OLTP चेकपॉइंट फ़ाइलों को रखने का समर्थन करता है। आपको बस इसे OS स्तर पर वॉल्यूम के रूप में प्रदर्शित करना है और DAX ड्राइव के रूप में प्रारूपित करना है। SQL सर्वर 2019 स्वचालित रूप से इस तकनीक को पहचानता है, और एक प्रबुद्ध . में काम करता है मोड, सीधे डिवाइस तक पहुंच, ओएस के स्टोरेज स्टैक को छोड़कर। भविष्य में आपका स्वागत है!

उपयोगकर्ता डेटाबेस बनाम tempdb

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

इसे प्रदर्शित करने के लिए, आप वही परीक्षण चलाएंगे जो आपने पहले PerformanceV3 में चलाए थे, केवल इस बार tempdb में। जब डेटाबेस विकल्प DELAYED_DURABILITY को अक्षम (डिफ़ॉल्ट) पर सेट किया जाता है, तो हम कई छोटे लेनदेन के परीक्षण के साथ शुरू करेंगे। परीक्षण टेम्पलेट के तैयारी अनुभाग में निम्नलिखित कोड का प्रयोग करें:

-- Preparation
SET NOCOUNT ON;
USE tempdb;
 
ALTER DATABASE tempdb SET DELAYED_DURABILITY = Disabled;
 
DROP TABLE IF EXISTS dbo.T1;
 
CREATE TABLE dbo.T1(col1 INT NOT NULL);
 
DECLARE @db AS sysname = N'tempdb';

वास्तविक कार्य अनुभाग में निम्नलिखित कोड का प्रयोग करें:

-- Actual work
DECLARE @i AS INT = 1;
 
WHILE @i <= 1000000
BEGIN
 
  BEGIN TRAN
    INSERT INTO dbo.T1(col1) VALUES(@i);
  COMMIT TRAN;
 
  SET @i += 1;
END;

इस कार्य ने 5,095 लॉग फ्लश उत्पन्न किए, और इसे पूरा करने में 19 सेकंड का समय लगा। इसकी तुलना एक मिलियन से अधिक लॉग फ्लश और पूर्ण स्थायित्व वाले उपयोगकर्ता डेटाबेस में 193 सेकंड से की जाती है। यह लॉग रिकॉर्ड के कम आकार के कारण उपयोगकर्ता डेटाबेस (95,407 लॉग फ्लश और 22 सेकंड) में विलंबित स्थायित्व से भी बेहतर है।

एक बड़े लेन-देन का परीक्षण करने के लिए, तैयारी अनुभाग को अपरिवर्तित छोड़ दें, और वास्तविक कार्य अनुभाग में निम्नलिखित कोड का उपयोग करें:

-- Actual work
BEGIN TRAN;
 
DECLARE @i AS INT = 1;
 
WHILE @i <= 1000000
BEGIN
  INSERT INTO dbo.T1(col1) VALUES(@i);
 
  SET @i += 1;
END;
 
COMMIT TRAN;

मुझे 1,228 लॉग फ्लश और 9 सेकंड का रन टाइम मिला। इसकी तुलना उपयोगकर्ता डेटाबेस में 1,758 लॉग फ्लश और 7 सेकंड के रन टाइम से की जाती है। रन टाइम समान है, उपयोगकर्ता डेटाबेस में थोड़ा तेज भी है, लेकिन यह परीक्षणों के बीच छोटे बदलाव हो सकता है। tempdb में लॉग रिकॉर्ड के आकार कम हो जाते हैं, और इसलिए आपको उपयोगकर्ता डेटाबेस की तुलना में कम लॉग फ़्लश मिलते हैं।

आप जबरन पर सेट DELAYED_DURABILITY विकल्प के साथ परीक्षण चलाने का प्रयास कर सकते हैं, लेकिन इसका tempdb में कोई प्रभाव नहीं पड़ेगा, जैसा कि उल्लेख किया गया है, वैसे भी प्रतिबद्ध ईवेंट tempdb में लॉग फ्लश को ट्रिगर नहीं करते हैं।

उपयोगकर्ता डेटाबेस और tempdb दोनों में, सभी परीक्षणों के लिए प्रदर्शन उपाय यहां दिए गए हैं:

database       durability          #transactions  log flushes  duration in seconds
-------------- ------------------- -------------- ------------ --------------------
PerformanceV3  full                1000000        1000036      193
PerformanceV3  full                1              1758         7
PerformanceV3  delayed             1000000        95407        22
PerformanceV3  delayed             1              1759         8
tempdb         full                1000000        5095         19
tempdb         full                1              1228         9
tempdb         delayed             1000000        5091         18
tempdb         delayed             1              1226         9

ऑब्जेक्ट कैशिंग का क्रम

शायद एक आश्चर्यजनक मामला जो लॉग बफर फ्लश को ट्रिगर करता है वह अनुक्रम ऑब्जेक्ट कैश विकल्प से संबंधित है। एक उदाहरण के रूप में निम्नलिखित अनुक्रम परिभाषा पर विचार करें:

CREATE SEQUENCE dbo.Seq1 AS BIGINT MINVALUE 1 CACHE 50; -- the default cache size is 50;

हर बार जब आपको एक नए अनुक्रम मान की आवश्यकता होती है, तो आप फ़ंक्शन के लिए अगला मान का उपयोग करते हैं, जैसे:

SELECT NEXT VALUE FOR dbo.Seq1;

CACHE संपत्ति एक प्रदर्शन विशेषता है। इसके बिना, हर बार एक नए अनुक्रम मान का अनुरोध किया गया था, SQL सर्वर को पुनर्प्राप्ति उद्देश्यों के लिए डिस्क पर वर्तमान मान लिखना होगा। वास्तव में, NO CACHE मोड का उपयोग करते समय आपको यही व्यवहार मिलता है। इसके बजाय, जब विकल्प को शून्य से अधिक मान पर सेट किया जाता है, तो SQL सर्वर प्रत्येक कैश-आकार के अनुरोधों की संख्या में केवल एक बार डिस्क पर पुनर्प्राप्ति मान लिखता है। SQL सर्वर मेमोरी में दो सदस्यों को रखता है, अनुक्रम प्रकार के रूप में आकार, एक वर्तमान मान धारण करता है, और एक पुनर्प्राप्ति मान के अगले डिस्क लिखने से पहले शेष मानों की संख्या रखता है। बिजली की विफलता के मामले में, पुनरारंभ करने पर, SQL सर्वर वर्तमान अनुक्रम मान को पुनर्प्राप्ति मान पर सेट करता है।

इसे एक उदाहरण से समझाना शायद ज्यादा आसान है। CACHE विकल्प के साथ उपरोक्त अनुक्रम परिभाषा पर विचार करें जो 50 (डिफ़ॉल्ट) पर सेट है। आप उपरोक्त SELECT कथन को चलाकर पहली बार एक नए अनुक्रम मान का अनुरोध करते हैं। SQL सर्वर उपरोक्त सदस्यों को निम्न मानों पर सेट करता है:

On disk recovery value: 50, In-memory current value: 1, In-memory values left: 49, You get: 1

49 और अनुरोध डिस्क को छूने नहीं जा रहे हैं, बल्कि केवल मेमोरी सदस्यों को अपडेट करें। कुल 50 अनुरोधों के बाद, सदस्यों को निम्नलिखित मानों पर सेट किया जाता है:

On disk recovery value: 50, In-memory current value: 50, In-memory values left: 0, You get: 50

एक नए अनुक्रम मान के लिए एक और अनुरोध करें, और यह पुनर्प्राप्ति मान 100 का डिस्क लेखन ट्रिगर करता है। सदस्यों को तब निम्न मानों पर सेट किया जाता है:

On disk recovery value: 100, In-memory current value: 51, In-memory values left: 49, You get: 51

यदि इस बिंदु पर सिस्टम बिजली की विफलता का अनुभव करता है, तो पुनरारंभ करने के बाद, वर्तमान अनुक्रम मान 100 (डिस्क से पुनर्प्राप्त मान) पर सेट होता है। अनुक्रम मान के लिए अगला अनुरोध 101 उत्पन्न करता है (पुनर्प्राप्ति मान 150 डिस्क पर लिखना)। आपने 52 से 100 की सीमा में सभी मान खो दिए हैं। SQL सर्वर प्रक्रिया की अशुद्ध समाप्ति के कारण आप जितना अधिक खो सकते हैं, जैसे कि बिजली की विफलता के मामले में, कैश आकार के रूप में कई मान हैं। ट्रेडऑफ़ स्पष्ट है; कैश आकार जितना बड़ा होगा, डिस्क पुनर्प्राप्ति मान के बारे में उतनी ही कम लिखती है, और इसलिए बेहतर प्रदर्शन। साथ ही, बिजली की विफलता के मामले में दो अनुक्रम मानों के बीच जितना बड़ा अंतर उत्पन्न हो सकता है।

यह सब बहुत सीधा है, और शायद आप इससे अच्छी तरह परिचित हैं कि यह कैसे काम करता है। आश्चर्य की बात यह है कि हर बार SQL सर्वर डिस्क पर एक नया पुनर्प्राप्ति मान लिखता है (हमारे उदाहरण में प्रत्येक 50 अनुरोध), यह लॉग बफर को भी सख्त करता है। पहचान कॉलम संपत्ति के साथ ऐसा नहीं है, भले ही SQL सर्वर आंतरिक रूप से पहचान के लिए उसी कैशिंग सुविधा का उपयोग करता है जैसे यह अनुक्रम ऑब्जेक्ट के लिए करता है, यह आपको इसके आकार को नियंत्रित नहीं करने देता है। यह डिफ़ॉल्ट रूप से BIGINT और NUMERIC के लिए 10000, INT के लिए 1000, SMALLINT के लिए 100 और TINYINT के लिए 10 आकार के साथ डिफ़ॉल्ट रूप से चालू है। आप चाहें तो इसे ट्रेस फ्लैग 272 या IDENTITY_CACHE स्कोप्ड कॉन्फ़िगरेशन विकल्प (2017+) से बंद कर सकते हैं। डिस्क पर पहचान-कैश-संबंधित पुनर्प्राप्ति मान लिखते समय SQL सर्वर को लॉग बफर को फ्लश करने की आवश्यकता नहीं होती है, क्योंकि एक नया पहचान मान केवल तालिका में एक पंक्ति डालने पर ही बनाया जा सकता है। बिजली की विफलता के मामले में, एक लेन-देन द्वारा तालिका में डाली गई एक पंक्ति जो प्रतिबद्ध नहीं थी, सिस्टम के पुनरारंभ होने पर डेटाबेस पुनर्प्राप्ति प्रक्रिया के भाग के रूप में तालिका से बाहर खींची जाएगी। इसलिए, भले ही पुनरारंभ करने के बाद SQL सर्वर उसी पहचान मान को उत्पन्न करता है जैसे लेन-देन में बनाया गया था जो प्रतिबद्ध नहीं था, डुप्लिकेट के लिए कोई मौका नहीं है क्योंकि पंक्ति को तालिका से बाहर निकाला गया था। यदि लेन-देन किया जाता है, तो इससे लॉग फ्लश शुरू हो जाएगा, जो कैश से संबंधित पुनर्प्राप्ति मूल्य के लेखन को भी जारी रखेगा। इसलिए, Microsoft को हर बार पुनर्प्राप्ति मान की पहचान-कैश-संबंधित डिस्क लिखने के लिए लॉग बफ़र को फ़्लश करने के लिए बाध्य महसूस नहीं हुआ।

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

बहुत छोटे अनुक्रम कैश आकार का उपयोग करते समय बार-बार लॉग फ्लश का एक नकारात्मक प्रदर्शन प्रभाव विशेष रूप से ध्यान देने योग्य होता है, और एक लेनदेन में बहुत सारे अनुक्रम मान उत्पन्न होते हैं, उदाहरण के लिए, तालिका में बहुत सारी पंक्तियां डालने पर। अनुक्रम के बिना, इस तरह का लेन-देन ज्यादातर लॉग बफर को भरता है जब यह भर जाता है, साथ ही लेन-देन होने पर एक बार और। लेकिन अनुक्रम के साथ, आपको हर बार पुनर्प्राप्ति मान का डिस्क लिखने पर लॉग फ्लश मिलता है। इसलिए आप छोटे कैश आकार का उपयोग करने से बचना चाहते हैं - NO CACHE मोड की बात नहीं करना।

इसे प्रदर्शित करने के लिए, हमारे परीक्षण टेम्पलेट के तैयारी अनुभाग में निम्नलिखित कोड का उपयोग करें:

-- Preparation
SET NOCOUNT ON;
USE PerformanceV3; -- try PerformanceV3, tempdb
 
ALTER DATABASE PerformanceV3         -- try PerformanceV3, tempdb
  SET DELAYED_DURABILITY = Disabled; -- try Disabled, Forced
 
DROP TABLE IF EXISTS dbo.T1;
 
DROP SEQUENCE IF EXISTS dbo.Seq1;
 
CREATE SEQUENCE dbo.Seq1 AS BIGINT MINVALUE 1 CACHE 50; -- try NO CACHE, CACHE 50, CACHE 10000
 
DECLARE @db AS sysname = N'PerformanceV3'; -- try PerformanceV3, tempdb

और वास्तविक कार्य अनुभाग में निम्नलिखित कोड:

-- Actual work
SELECT
  -- n -- to test without seq
  NEXT VALUE FOR dbo.Seq1 AS n -- to test sequence
INTO dbo.T1
FROM PerformanceV3.dbo.GetNums(1, 1000000) AS N;

यह कोड SELECT INTO स्टेटमेंट का उपयोग करके तालिका में 1,000,000 पंक्तियों को लिखने के लिए एक लेनदेन का उपयोग करता है, जो सम्मिलित पंक्तियों की संख्या के रूप में कई अनुक्रम मान उत्पन्न करता है।

जैसा कि टिप्पणियों में निर्देश दिया गया है, प्रदर्शनV3 और tempdb दोनों में NO CACHE, CACHE 50 और CACHE 10000 के साथ परीक्षण चलाएं, और पूरी तरह से टिकाऊ लेनदेन और विलंबित टिकाऊ लेनदेन दोनों का प्रयास करें।

यहाँ प्रदर्शन संख्याएँ हैं जो मुझे अपने सिस्टम पर मिली हैं:

database       durability          cache     log flushes  duration in seconds
-------------- ------------------- --------- ------------ --------------------
PerformanceV3  full                NO CACHE  1000047      171
PerformanceV3  full                50        20008        4
PerformanceV3  full                10000     339          < 1
tempdb         full                NO CACHE  96           4
tempdb         full                50        74           1
tempdb         full                10000     8            < 1
PerformanceV3  delayed             NO CACHE  1000045      166
PerformanceV3  delayed             50        20011        4
PerformanceV3  delayed             10000     334          < 1
tempdb         delayed             NO CACHE  91           4
tempdb         delayed             50        74           1
tempdb         delayed             10000     8            < 1

ध्यान देने योग्य कुछ दिलचस्प बातें हैं।

NO CACHE के साथ आपको उत्पन्न प्रत्येक अनुक्रम मान के लिए लॉग फ्लश मिलता है। इसलिए, इससे बचने की पुरज़ोर अनुशंसा की जाती है।

एक छोटे अनुक्रम कैश आकार के साथ, आपको अभी भी बहुत सारे लॉग फ्लश मिलते हैं। शायद स्थिति NO CACHE जितनी खराब नहीं है, लेकिन ध्यान दें कि 10,000 के आकार के साथ एक सेकंड से भी कम समय की तुलना में 50 के डिफ़ॉल्ट कैश आकार के साथ कार्यभार को पूरा करने में 4 सेकंड का समय लगा। मैं व्यक्तिगत रूप से अपने पसंदीदा मूल्य के रूप में 10,000 का उपयोग करता हूं।

In tempdb you don’t get log flushes when a sequence cache-related recovery value is written to disk, but the recovery value is still written to disk every cache-sized number of requests. That’s perhaps surprising since such a value would never need to be recovered. Therefore, even when using a sequence object in tempdb, I’d still recommend using a large cache size.

Also notice that delayed durability doesn’t prevent the need for log flushes every time the sequence cache-related recovery value is written to disk.

निष्कर्ष

This article focused on log buffer flushes. Understanding this aspect of SQL Server’s logging architecture is important especially in order to be able to optimize OLTP-style workloads that require high frequency and low latency. Workloads using In-Memory OLTP included, of course. You have more options with newer features like delayed durability and persisted log buffer with storage class memory. Make sure you’re very careful with the former, though, since it does incur potential for data loss unlike the latter.

Be careful not to use the sequence object with a small cache size, not to speak of the NO CACHE mode. I find the default size 50 too small and prefer to use 10,000. I’ve heard people expressing concerns that with a cache size 10000, after multiple power failures they might lose all the values in the type. However, even with a four-byte INT type, using only the positive range, 10,000 fits 214,748 times. If your system experience that many power failures, you have a completely different problem to worry about. Therefore, I feel very comfortable with a cache size of 10,000.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. SQL में DECODE फंक्शन का क्या उपयोग है?

  2. #temp तालिका निर्माण ट्रैकिंग का ओवरहेड

  3. टेबल एक्सप्रेशन के फंडामेंटल, भाग 4 - व्युत्पन्न टेबल, अनुकूलन विचार, जारी रखा

  4. स्कीमा स्विच-ए-रू :भाग 2

  5. कंटेनर डेटाबेस (सीडीबी) में उपयोगकर्ता पासवर्ड बदलते समय त्रुटि ORA-65048