कोई डेटा नहीं बदलता है, जहां प्रदर्शन बढ़ाएँ


31

यदि मेरे पास एक UPDATEबयान है जो वास्तव में कोई डेटा नहीं बदलता है (क्योंकि डेटा पहले से ही अद्यतन स्थिति में है)। क्या WHEREअपडेट रोकने के लिए क्लॉज़ में चेक लगाने में कोई प्रदर्शन लाभ है ?

उदाहरण के लिए UPDATE 1 और UPDATE 2 के बीच निष्पादन की गति में कोई अंतर होगा:

CREATE TABLE MyTable (ID int PRIMARY KEY, Value int);
INSERT INTO MyTable (ID, Value)
VALUES
    (1, 1),
    (2, 2),
    (3, 3);

-- UPDATE 1
UPDATE MyTable
SET
    Value = 2
WHERE
    ID = 2
    AND Value <> 2;
SELECT @@ROWCOUNT;

-- UPDATE 2
UPDATE MyTable
SET
    Value = 2
WHERE
    ID = 2;
SELECT @@ROWCOUNT;

DROP TABLE MyTable;

मेरे द्वारा पूछे जाने का कारण यह है कि अपरिवर्तित पंक्ति को शामिल करने के लिए मुझे पंक्ति गणना की आवश्यकता है, इसलिए मुझे पता है कि क्या आईडी डालने के लिए कोई प्रविष्टि नहीं है। जैसे मैंने UPDATE 2 फॉर्म का उपयोग किया। यदि UPDATE 1 फ़ॉर्म का उपयोग करने के लिए कोई प्रदर्शन लाभ है, तो क्या पंक्ति गणना प्राप्त करना संभव है जो मुझे किसी तरह की आवश्यकता है?


देखें sqlperformance.com/2012/10/t-sql-queries/conditional-updates (हालांकि मैंने उस मामले को प्रोफाइल नहीं किया था जहां कोई मान नहीं बदलता है)।
हारून बर्ट्रेंड

जवाबों:


24

अगर मेरे पास एक अद्यतन विवरण है जो वास्तव में कोई डेटा नहीं बदलता है (क्योंकि डेटा पहले से ही अद्यतन स्थिति में है), तो क्या चेक में कोई रोक लगाने में कोई प्रदर्शन लाभ है जहां अपडेट को रोकने के लिए है?

निश्चित रूप से वहाँ हो सकता है क्योंकि UPDATE 1 के कारण थोड़ा सा प्रदर्शन अंतर है :

  • वास्तव में किसी भी पंक्तियों को अपडेट नहीं करना (इसलिए डिस्क पर लिखने के लिए कुछ भी नहीं, न्यूनतम लॉग गतिविधि भी नहीं), और
  • वास्तविक अद्यतन करने के लिए जो आवश्यक हो उससे कम प्रतिबंधात्मक ताले को निकालना (इसलिए संगामिति के लिए बेहतर है) ( कृपया अंत तक अद्यतन अनुभाग देखें )

हालाँकि, आपके स्कीमा, और डेटा, और सिस्टम लोड के साथ आपके सिस्टम पर आपके द्वारा मापा जाने वाले अंतर में से कितना अंतर होगा। अपडेट न होने वाले UPDATE में कितने कारक हैं, जो कितना प्रभाव डालते हैं:

  • अद्यतन की जा रही तालिका पर विवाद की मात्रा
  • अपडेट की जा रही पंक्तियों की संख्या
  • यदि अद्यतन किए जा रहे टेबल पर अद्यतन ट्रिगर हैं (जैसा कि प्रश्न पर टिप्पणी में मार्क द्वारा नोट किया गया है)। यदि आप निष्पादित करते हैं UPDATE TableName SET Field1 = Field1, तो एक अपडेट ट्रिगर फायर करेगा और इंगित करेगा कि फ़ील्ड अपडेट की गई थी (यदि आप या तो अद्यतन करें) या ( COLUMNS_UPDATED फ़ंक्शंस का उपयोग करके ), और यह कि दोनों INSERTEDऔर DELETEDतालिकाओं में फ़ील्ड एक ही मान हैं।

इसके अलावा, पॉल व्हाइट के लेख, द इम्पैक्ट ऑफ़ नॉन-अपडेटिंग अपडेट्स में निम्नलिखित सारांश अनुभाग पाया गया है (जैसा कि उनके जवाब में एक टिप्पणी में @spaghettidba द्वारा उल्लेख किया गया है):

SQL सर्वर में अनावश्यक लॉगिंग या पृष्ठ फ्लशिंग से बचने के लिए कई अनुकूलन होते हैं, जब एक UPDATE ऑपरेशन को संसाधित करते हैं जो लगातार डेटाबेस में किसी भी परिवर्तन का परिणाम नहीं होगा।

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

कृपया ध्यान रखें (विशेषकर यदि आप पॉल के पूर्ण लेख को देखने के लिए लिंक का अनुसरण नहीं करते हैं), निम्नलिखित दो आइटम:

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

  2. जैसा कि मैंने ऊपर कहा है, आपको अपने सिस्टम पर परीक्षण करने की आवश्यकता है। पॉल जो उपयोग कर रहा है उन्हीं शोध प्रश्नों का उपयोग करें और देखें कि क्या आपको वही परिणाम मिलते हैं। मैं अपने सिस्टम पर थोड़ा अलग परिणाम देख रहा हूं जो लेख में दिखाया गया है। अभी भी कोई गंदा पृष्ठ नहीं लिखा जाना है, लेकिन थोड़ी और लॉग गतिविधि।


... मुझे अपरिवर्तित पंक्ति को शामिल करने के लिए पंक्ति गणना की आवश्यकता है, इसलिए मुझे पता है कि क्या आईडी सम्मिलित नहीं है या नहीं। ... क्या यह संभव है कि मुझे किसी तरह की पंक्ति की गिनती मिल जाए?

सरलीकृत रूप से, यदि आप केवल एक पंक्ति के साथ काम कर रहे हैं, तो आप निम्न कार्य कर सकते हैं:

UPDATE MyTable
SET    Value = 2
WHERE  ID = 2
AND Value <> 2;

IF (@@ROWCOUNT = 0)
BEGIN
  IF (NOT EXISTS(
                 SELECT *
                 FROM   MyTable
                 WHERE  ID = 2 -- or Value = 2 depending on the scenario
                )
     )
  BEGIN
     INSERT INTO MyTable (ID, Value) -- or leave out ID if it is an IDENTITY
     VALUES (2, 2);
  END;
END;

कई पंक्तियों के लिए, आप का उपयोग करके निर्णय लेने के लिए आवश्यक जानकारी प्राप्त कर सकते हैं OUTPUT क्लॉज । वास्तव में क्या पंक्तियों को अद्यतन करके कैप्चर किया गया था, फिर आप पंक्तियों को अपडेट न करने के बीच अंतर जानने के लिए आइटमों को संकीर्ण कर सकते हैं जो मौजूद पंक्तियों को अपडेट नहीं करने के विरोध में मौजूद नहीं हैं लेकिन अपडेट की आवश्यकता नहीं है।

मैं निम्नलिखित उत्तर में मूल कार्यान्वयन दिखाता हूं:

एक्सएमएल पैरामीटर का उपयोग करके कई डेटा को अपग्रेड करते समय मर्ज क्वेरी का उपयोग करने से कैसे बचें?

उस उत्तर में दिखाई गई विधि उन पंक्तियों को फ़िल्टर नहीं करती है जो अभी तक मौजूद हैं उन्हें अपडेट करने की आवश्यकता नहीं है। उस हिस्से को जोड़ा जा सकता है, लेकिन आपको सबसे पहले यह दिखाने की जरूरत होगी कि आप अपने डेटासेट को कहां से विलय कर रहे हैं MyTable। क्या वे एक अस्थायी तालिका से आ रहे हैं? एक टेबल-वैल्यू पैरामीटर (TVP)?


अद्यतन 1:

मैं अंत में कुछ परीक्षण करने में सक्षम था और लेन-देन लॉग और लॉकिंग के बारे में मुझे यही मिला। सबसे पहले, तालिका के लिए स्कीमा:

CREATE TABLE [dbo].[Test]
(
  [ID] [int] NOT NULL CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED,
  [StringField] [varchar](500) NULL
);

इसके बाद, परीक्षण क्षेत्र को उस मूल्य पर अद्यतन करता है जो पहले से है:

UPDATE rt
SET    rt.StringField = '04CF508B-B78E-4264-B9EE-E87DC4AD237A'
FROM   dbo.Test rt
WHERE  rt.ID = 4082117

परिणाम:

-- Transaction Log (2 entries):
Operation
----------------------------
LOP_BEGIN_XACT
LOP_COMMIT_XACT


-- SQL Profiler (3 Lock:Acquired events):
Mode            Type
--------------------------------------
8 - IX          5 - OBJECT
8 - IX          6 - PAGE
5 - X           7 - KEY

अंत में, परीक्षण जो मान को नहीं बदलने के कारण अद्यतन को फ़िल्टर करता है:

UPDATE rt
SET    rt.StringField = '04CF508B-B78E-4264-B9EE-E87DC4AD237A'
FROM   dbo.Test rt
WHERE  rt.ID = 4082117
AND    rt.StringField <> '04CF508B-B78E-4264-B9EE-E87DC4AD237A';

परिणाम:

-- Transaction Log (0 entries):
Operation
----------------------------


-- SQL Profiler (3 Lock:Acquired events):
Mode            Type
--------------------------------------
8 - IX          5 - OBJECT
7 - IU          6 - PAGE
4 - U           7 - KEY

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

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


अद्यतन 2:

यदि संचालन जिसमें नया मूल्य वर्तमान मूल्य (यानी कोई अद्यतन नहीं) के समान है, तो ऐसे संचालन की संख्या जिसमें नया मूल्य अलग है और अद्यतन आवश्यक है, तो निम्न पैटर्न और भी बेहतर साबित हो सकता है, खासकर यदि मेज पर बहुत विवाद है। SELECTवर्तमान मूल्य प्राप्त करने के लिए एक सरल पहले करने के लिए विचार है । यदि आपको कोई मूल्य नहीं मिलता है, तो आपके पास अपना जवाब है INSERT। यदि आपके पास एक मूल्य है, तो आप एक सरल कर सकते हैं IFऔर UPDATE केवल तभी जारी कर सकते हैं जब इसकी आवश्यकता हो।

DECLARE @CurrentValue VARCHAR(500) = NULL,
        @NewValue VARCHAR(500) = '04CF508B-B78E-4264-B9EE-E87DC4AD237A',
        @ID INT = 4082117;

SELECT @CurrentValue = rt.StringField
FROM   dbo.Test rt
WHERE  rt.ID = @ID;

IF (@CurrentValue IS NULL) -- if NULL is valid, use @@ROWCOUNT = 0
BEGIN
  -- row does not exist
  INSERT INTO dbo.Test (ID, StringField)
  VALUES (@ID, @NewValue);
END;
ELSE
BEGIN
  -- row exists, so check value to see if it is different
  IF (@CurrentValue <> @NewValue)
  BEGIN
    -- value is different, so do the update
    UPDATE rt
    SET    rt.StringField = @NewValue
    FROM   dbo.Test rt
    WHERE  rt.ID = @ID;
  END;
END;

परिणाम:

-- Transaction Log (0 entries):
Operation
----------------------------


-- SQL Profiler (2 Lock:Acquired events):
Mode            Type
--------------------------------------
6 - IS          5 - OBJECT
6 - IS          6 - PAGE

इसलिए 3 के बजाय केवल 2 ताले प्राप्त किए गए हैं, और ये दोनों ही लॉक इन्टेंट शेयर किए गए हैं, इंटेंट एक्सक्लूसिव या इंटेंट अपडेट ( लॉक कम्पेटिबिलिटी ) नहीं। यह ध्यान में रखते हुए कि प्रत्येक लॉक अधिग्रहीत भी जारी हो जाएगा, प्रत्येक लॉक वास्तव में 2 ऑपरेशन है, इसलिए यह नई विधि मूल रूप से प्रस्तावित विधि में 6 संचालन के बजाय कुल 4 ऑपरेशन है। इस ऑपरेशन को ध्यान में रखते हुए हर 15 एमएस (लगभग, जैसा कि ओपी द्वारा कहा गया है) चल रहा है, यानी लगभग 66 बार प्रति सेकंड। इसलिए मूल प्रस्ताव प्रति सेकंड 396 लॉक / अनलॉक संचालन की मात्रा है, जबकि यह नई विधि केवल 264 लॉक / अनलॉक ऑपरेशन प्रति सेकंड भी हल्के-वजन वाले लॉक की मात्रा है। यह कमाल के प्रदर्शन की गारंटी नहीं है, लेकिन निश्चित रूप से परीक्षण के लायक है :-)।


14

थोड़ा बाहर ज़ूम करें और बड़ी तस्वीर के बारे में सोचें। वास्तविक दुनिया में, क्या आपका अपडेट स्टेटमेंट वास्तव में ऐसा दिखने वाला है:

UPDATE MyTable
  SET Value = 2
WHERE
     ID = 2
     AND Value <> 2;

या यह इस तरह दिखाई देने वाला है:

UPDATE Customers
  SET AddressLine1 = '123 Main St',
      AddressLine2 = 'Apt 24',
      City = 'Chicago',
      State = 'IL',
      (and a couple dozen more fields)
WHERE
     ID = 2
     AND (AddressLine1 <> '123 Main St'
     OR AddressLine2 <> 'Apt 24'
     OR City <> 'Chicago'
     OR State <> 'IL'
      (and a couple dozen more fields))

क्योंकि वास्तविक दुनिया में, तालिकाओं में बहुत सारे कॉलम होते हैं। इसका मतलब है कि आप गतिशील तारों का निर्माण करने के लिए बहुत सारे जटिल डायनेमिक ऐप लॉजिक उत्पन्न करने वाले हैं, या आप हर क्षेत्र के पहले और बाद की सामग्री, हर बार निर्दिष्ट करने जा रहे हैं।

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

उन जटिलताओं के कारण, यह आमतौर पर इस तरह की पंक्ति-दर-पंक्ति, क्षेत्र-दर-क्षेत्र तुलना करने के लिए समझ में नहीं आता है क्योंकि आप अपडेट कर रहे हैं। इसके बजाय सेट-आधारित ऑपरेशन सोचें।


1
मेरी वास्तविक दुनिया उदाहरण के रूप में सरल है, लेकिन बहुत कुछ कहा जाता है। मेरा अनुमान एक बार हर 15 पी पर है। मैं सोच रहा था कि क्या SQL सर्वर क्लीवर है जब इसे लिखने की आवश्यकता नहीं है, तो डिस्क पर लिखना नहीं है।
मार्टिन ब्राउन

3

आप लंघन पंक्तियों में एक प्रदर्शन लाभ देख सकते हैं जिन्हें केवल तभी अपडेट करने की आवश्यकता नहीं है जब पंक्तियों की संख्या बड़ी हो (डिस्क में लिखने के लिए कम लॉगिंग, कम गंदे पृष्ठ)।

जब आपके मामले में एकल पंक्ति अपडेट के साथ काम करते हैं, तो प्रदर्शन अंतर पूरी तरह से नगण्य है। यदि सभी मामलों में पंक्तियों को अपडेट करना आपके लिए आसान है, तो इसे करें।

विषय पर अधिक जानकारी के लिए पॉल व्हाइट द्वारा नॉन अपडेटिंग अपडेट देखें


3

आप अपडेट को जोड़ सकते हैं और एक स्टेटमेंट में डाल सकते हैं। SQL सर्वर पर, आप अपडेट करने के लिए MERGE स्टेटमेंट का उपयोग कर सकते हैं और यदि नहीं मिला तो डालें। MySQL के लिए, आप INUPERT ON DUPLICATE KEY UPDATE का उपयोग कर सकते हैं ।


1

सभी क्षेत्रों के मूल्यों की जाँच करने के बजाय, आप जिस कॉलम में रुचि रखते हैं उसका उपयोग करके हैश मूल्य प्राप्त कर सकते हैं, तुलना करें कि तालिका में पंक्ति के खिलाफ संग्रहीत हैश के लिए?

IF EXISTS (Select 1 from Table where ID =@ID AND HashValue=Sha256(column1+column2))
GOTO EXIT
ELSE
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.