2011-04-06 18 views
8

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

-- Do not block if records are locked. 
SET LOCK_TIMEOUT 0 
-- This process should be chosen as a deadlock victim in the case of a deadlock. 
SET DEADLOCK_PRIORITY LOW 
SET NOCOUNT ON 

DECLARE @Count 
SET @Count = 1 
WHILE @Count > 0 
BEGIN TRY 
    BEGIN TRANSACTION -- added per comment below 

    DELETE TOP (1000) FROM MyTable WITH (ROWLOCK, READPAST) WHERE MyField = SomeValue 
    SET @Count == @@ROWCOUNT 

    COMMIT 
END TRY 
BEGIN CATCH 
    exec sp_lock -- added to display the open locks after the timeout 
    exec sp_who2 -- shows the active processes 

    IF @@TRANCOUNT > 0 
     ROLLBACK 
    RETURN -- ignoring this error for brevity 
END CATCH 

MyTable एक क्लस्टर टेबल है:

अधिक संख्या के कारण

, मैं एक पाश इस के समान में हटाना हड्डी टूट गई है। MyField क्लस्टर सूचकांक में पहले कॉलम में है। यह रिकॉर्ड के तार्किक समूह को इंगित करता है, इसलिए MyField = SomeValue अक्सर कई रिकॉर्ड चुनते हैं। मुझे कोई परवाह नहीं है कि वे किस क्रम में हटाए जाते हैं जब तक एक समूह को एक समय में संसाधित किया जाता है। इस टेबल पर कोई अन्य अनुक्रमणिका नहीं है।

मैंने उत्पादन में देखा गया लॉक एस्केलेशन से बचने के लिए ROWLOCK संकेत जोड़ा। मैंने अन्य प्रक्रियाओं द्वारा लॉक किए गए रिकॉर्ड्स को हटाने से बचने के लिए READPAST संकेत जोड़ा। ऐसा कभी नहीं होना चाहिए, लेकिन मैं सुरक्षित होने की कोशिश कर रहा हूं।

समस्या: कभी-कभी यह लूप लॉक टाइमआउट 1222 "लॉक अनुरोध समय अवधि समाप्त हो जाती है" जब यह एकमात्र चीज चलती है।

मैं सकारात्मक हूं कि इस प्रक्रिया का परीक्षण करने के दौरान इस प्रणाली पर कोई अन्य गतिविधि नहीं है, क्योंकि यह मेरा स्वयं का डेवलपर बॉक्स है, कोई और कनेक्ट नहीं है, इसमें कोई अन्य प्रक्रिया नहीं चल रही है, और प्रोफाइलर कोई नहीं दिखाता है गतिविधि।

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

मैंने 1222 त्रुटि को अनदेखा करने और हटाने का पुनः प्रयास करने के लिए BEGIN TRY/BEGIN CATCH को आजमाया है और हटाएं पुनः प्रयास करें, लेकिन यह उसी लॉक टाइमआउट त्रुटि के साथ तुरंत विफल हो जाता है। अगर मैं पुनः प्रयास करने से पहले एक छोटी देरी जोड़ता हूं तो यह फिर भी विफल हो जाता है।

मुझे लगता है कि लॉक टाइमआउट पृष्ठ विभाजन की तरह कुछ की वजह से हैं, लेकिन मुझे यकीन नहीं है कि यह वर्तमान पाश पुनरावृत्ति के साथ क्यों संघर्ष करेगा। पूर्व डिलीट स्टेटमेंट पहले ही पूरा हो चुका था, और मैंने सोचा कि इसका मतलब है कि कोई पेज स्प्लिट भी समाप्त हो गया था।

डेली लूप अपने खिलाफ लॉक टाइमआउट क्यों मार रहा है?

क्या इस तरह की प्रक्रिया इस लॉक टाइमआउट से बच सकती है या पता लगाना कि यह फिर से शुरू करना सुरक्षित है?

यह 2005

SQL सर्वर पर है - संपादित करें -

मैं ताला कहा: प्रोफाइलर को समय समाप्त घटना। इसे हटाने के दौरान एक PAGELOCK पर बाहर समय है:

Event Class: Lock:Timeout 
TextData: 1:15634 (one example of several) 
Mode:  7 - IU 
Type:  6 - PAGE 

बी सी सी पृष्ठ की रिपोर्ट इन पृष्ठों मास्टर डेटाबेस (आईडी 1) की सीमा के बाहर हैं।

- संपादित करें 2 -

मैं एक BEGIN TRY/BEGIN CATCH जोड़ा गया है और कैच ब्लॉक में एक exec sp_lock भाग गया।यहां मैंने देखा है:

spid dbid ObjId  IndId Type Resource Mode Status 
19 2 1401108082 1  PAG 1:52841 X GRANT (tempdb.dbo.MyTable) 
19 2 1401108082 0  TAB   IX GRANT (tempdb.dbo.MyTable) 
Me 2 1401108082 0  TAB   IX GRANT (tempdb.dbo.MyTable) 
Me 1 1115151018 0  TAB   IS GRANT (master..spt_values) (?) 

एसपीआईडी ​​1 एक एसक्यूएल सर्वर टास्क प्रबंधक है। इन कार्य प्रबंधकों में से एक MyTable पर ताले हासिल क्यों करेगा?

+0

आप एसक्यूएल ट्रेस में विभिन्न ताला घटनाओं का पता लगाने अगर आप फैलाना कर सकते हैं क्या हो रहा है देखने के लिए कोशिश की है? –

+0

बस किया, इसका उल्लेख करने के लिए धन्यवाद। मैंने ऊपर लॉक टाइमआउट जानकारी जोड़ा। निश्चित नहीं है कि वास्तव में क्या लॉक किया जा रहा है। –

+0

एक और संपादन: लॉक टाइमआउट के तुरंत बाद कुछ sp_lock जानकारी जोड़ा गया। –

उत्तर

6

मुझे जवाब मिला है: मेरा लूप डिलीट भूत क्लीनअप प्रो के साथ विरोधाभासी है।

निकोलस के सुझाव का उपयोग करते हुए, मैंने BEGIN TRANSACTION और COMMIT जोड़ा। मैंने डिलीट लूप को BEGIN TRY/BEGIN CATCH में लपेट लिया। BEGIN CATCH में, ROLLBACK से ठीक पहले, मैं sp_lock और sp_who2 चला गया। (मैं प्रश्न में कोड में परिवर्तन से ऊपर गयी।)

जब मेरे प्रक्रिया अवरुद्ध, मैं निम्नलिखित उत्पादन देखा:

spid dbid ObjId  IndId Type Resource       Mode  Status 
------ ------ ----------- ------ ---- -------------------------------- -------- ------ 
20  2  1401108082 0  TAB         IX  GRANT 
20  2  1401108082 1  PAG 1:102368       X  GRANT 

SPID Status  Login HostName BlkBy DBName Command  CPUTime DiskIO 
---- ---------- ----- -------- ----- ------ ------------- ------- ------ 
20 BACKGROUND sa .  .  tempdb GHOST CLEANUP 31  0 

भविष्य में संदर्भ के लिए, जब SQL सर्वर रिकॉर्ड हटाता है, यह उन पर एक सा सेट उन्हें "भूत रिकॉर्ड" के रूप में चिह्नित करने के लिए। प्रत्येक कुछ मिनट, भूत क्लीनअप नामक एक आंतरिक प्रक्रिया पूरी तरह से हटाए गए रिकॉर्ड के पृष्ठों को पुनः प्राप्त करने के लिए चलाती है (यानी सभी रिकॉर्ड भूत रिकॉर्ड हैं)।

The ghost cleanup process was discussed on ServerFault in this question.

Here is Paul S. Randal's explanation of the ghost cleanup process.

It is possible to disable the ghost cleanup process with a trace flag. लेकिन मैं इस मामले में ऐसा करने के लिए नहीं था।

मैंने 100 एमएस के लॉक प्रतीक्षा टाइमआउट को जोड़ दिया। यह भूत रिकॉर्ड क्लीनअप प्रक्रिया में कभी-कभी लॉक प्रतीक्षा टाइमआउट का कारण बनता है, लेकिन यह स्वीकार्य है। मैंने एक लूप भी जोड़ा जो 5 बार तक लॉक टाइमआउट को पुनः प्रयास करता है। इन दो परिवर्तनों के साथ, अब मेरी प्रक्रिया आमतौर पर पूरी होती है। अब यह केवल एक टाइमआउट प्राप्त करता है यदि बहुत सारी प्रक्रिया को धक्का दे रहा है जो उस डेटा पर टेबल या पेज लॉक प्राप्त करता है जिस पर मेरी प्रक्रिया को साफ करने की आवश्यकता है।

संपादित 2016-07-20

अंतिम कोड इस तरह दिखता है:

-- Do not block long if records are locked. 
SET LOCK_TIMEOUT 100 

-- This process volunteers to be a deadlock victim in the case of a deadlock. 
SET DEADLOCK_PRIORITY LOW 

DECLARE @Error BIT 
SET @Error = 0 

DECLARE @ErrMsg VARCHAR(1000) 
DECLARE @DeletedCount INT 
SELECT @DeletedCount = 0 

DECLARE @LockTimeoutCount INT 
SET @LockTimeoutCount = 0 

DECLARE @ContinueDeleting BIT, 
    @LastDeleteSuccessful BIT 

SET @ContinueDeleting = 1 
SET @LastDeleteSuccessful = 1 

WHILE @ContinueDeleting = 1 
BEGIN 
    DECLARE @RowCount INT 
    SET @RowCount = 0 

    BEGIN TRY 

     BEGIN TRANSACTION 

     -- The READPAST below attempts to skip over locked records. 
     -- However, it might still cause a lock wait error (1222) if a page or index is locked, because the delete has to modify indexes. 
     -- The threshold for row lock escalation to table locks is around 5,000 records, 
     -- so keep the deleted number smaller than this limit in case we are deleting a large chunk of data. 
     -- Table name, field, and value are all set dynamically in the actual script. 
     SET @SQL = N'DELETE TOP (1000) MyTable WITH(ROWLOCK, READPAST) WHERE MyField = SomeValue' 
     EXEC sp_executesql @SQL, N'@ProcGuid uniqueidentifier', @ProcGUID 

     SET @RowCount = @@ROWCOUNT 

     COMMIT 

     SET @LastDeleteSuccessful = 1 

     SET @DeletedCount = @DeletedCount + @RowCount 
     IF @RowCount = 0 
     BEGIN 
      SET @ContinueDeleting = 0 
     END 

    END TRY 
    BEGIN CATCH 

     IF @@TRANCOUNT > 0 
      ROLLBACK 

     IF Error_Number() = 1222 -- Lock timeout 
     BEGIN 

      IF @LastDeleteSuccessful = 1 
      BEGIN 
       -- If we hit a lock timeout, and we had already deleted something successfully, try again. 
       SET @LastDeleteSuccessful = 0 
      END 
      ELSE 
      BEGIN 
       -- The last delete failed, too. Give up for now. The job will run again shortly. 
       SET @ContinueDeleting = 0 
      END 
     END 
     ELSE -- On anything other than a lock timeout, report an error. 
     BEGIN  
      SET @ErrMsg = 'An error occurred cleaning up data. Table: MyTable Column: MyColumn Value: SomeValue. Message: ' + ERROR_MESSAGE() + ' Error Number: ' + CONVERT(VARCHAR(20), ERROR_NUMBER()) + ' Line: ' + CONVERT(VARCHAR(20), ERROR_LINE()) 
      PRINT @ErrMsg -- this error message will be included in the SQL Server job history 
      SET @Error = 1 
      SET @ContinueDeleting = 0 
     END 

    END CATCH 

END 

IF @Error <> 0 
    RAISERROR('Not all data could be cleaned up. See previous messages.', 16, 1) 
+0

क्या आप फिक्स के बाद अपना उत्पादन समाधान पोस्ट कर सकते हैं? –

+0

@RonnieOverby मैंने एक नमूना समाधान जोड़ा। हमारा वास्तविक उत्पादन कोड इससे अधिक जटिल है, क्योंकि यह गतिशील एसक्यूएल के माध्यम से कई अलग-अलग तालिकाओं को साफ़ करता है। उपरोक्त इस कोड में अतिरिक्त सामान शामिल नहीं है। –

+0

बहुत बढ़िया। ऐसा करने के लिए समय लेने के लिए धन्यवाद। –

4

आप या कनेक्शन का उपयोग करने वाले किसी अन्य व्यक्ति को डिफ़ॉल्ट के अलावा किसी अन्य चीज़ पर लॉक टाइमआउट सेट करना है। विवरण के लिए http://msdn.microsoft.com/en-US/library/ms189470(v=SQL.90).aspx देखें।

डिफ़ॉल्ट लॉक टाइम यह -1 मिलीसेकंड है, जिसका अर्थ है "हमेशा प्रतीक्षा करें"।

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

स्टार्टर्स के लिए, आप लॉक आकार को नियंत्रित नहीं कर सकते हैं: बकाया ताले की संख्या के आधार पर लॉक एस्केलेशन स्वचालित रूप से होता है। यह पंक्ति ताले के साथ शुरू होता है। यदि आप बहुत अधिक पंक्ति ताले जमा करते हैं, तो SQL सर्वर पृष्ठ लॉक पर बढ़ जाता है। बहुत सारे पेज लॉक प्राप्त करें और यह टेबल लॉक में बढ़ता है। ताला वृद्धि विवरण के लिए http://msdn.microsoft.com/en-us/library/ms184286(v=SQL.90).aspx देखें। आपके द्वारा सेट किए जा सकने वाले कुछ ट्रेस ध्वज हैं, हालांकि, यह लॉक एस्केलेशन को रोक देगा: हालांकि, यह SQL सर्वर के प्रदर्शन को घटा देगा।

एक और बात: आपको लेनदेन में DELETE कथन को विशेष रूप से संग्रहीत प्रक्रिया में लपेटना चाहिए।

DECLARE @Count INT 
SET @Count = 1 
WHILE @Count > 0 
    BEGIN 
    BEGIN TRANSACTION 
    DELETE TOP (1000) FROM MyTable WITH (ROWLOCK, READPAST) WHERE MyField = SomeValue 
    SET @Count = @@ROWCOUNT 
    COMMIT TRANSACTION 
    END 

यह आपके इरादे को स्पष्ट करता है और यह सुनिश्चित करता है कि ताले तब रिलीज़ हो जाएं जब उन्हें होना चाहिए।

+1

एसक्यूएल एक लॉक लॉक को एक पंक्ति लॉक में नहीं बढ़ाता है - यह सीधे एक टेबल लॉक में बढ़ता है। http://www.sqlskills.com/BLOGS/PAUL/post/A-SQL- सर्वर- डीबीए-myth-a-day-(2330)-lock-escalation.aspx –

+0

आप सही हैं कि कोड LOCK_TIMEOUT को 0 पर सेट करता है मैंने अभी ऊपर शामिल किया है; पहले इसका उल्लेख नहीं करने के लिए खेद है। –

+0

लेनदेन में इसे लपेटने से लॉक टाइमआउट के समय खुले ताले की पहचान करने में मदद मिली है। उपरोक्त संपादन देखें। –

संबंधित मुद्दे