बड़े पैमाने पर INSERTs ब्लॉकिंग का चयन करते हैं


14

मुझे भारी मात्रा में INSERT की समस्या है जो मेरे SELECT संचालन को रोक रही हैं।

योजना

मेरे पास इस तरह की एक तालिका है:

CREATE TABLE [InverterData](
    [InverterID] [bigint] NOT NULL,
    [TimeStamp] [datetime] NOT NULL,    
    [ValueA] [decimal](18, 2) NULL,
    [ValueB] [decimal](18, 2) NULL
    CONSTRAINT [PrimaryKey_e149e28f-5754-4229-be01-65fafeebce16] PRIMARY KEY CLUSTERED 
    (
        [TimeStamp] DESC,
        [InverterID] ASC
    ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF
    , IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON
    , ALLOW_PAGE_LOCKS = ON)
)

मेरे पास यह छोटी सहायक प्रक्रिया भी है, जो मुझे MERGE कमांड के साथ सम्मिलित करने या अपडेट (संघर्ष पर अद्यतन) करने की अनुमति देती है:

CREATE PROCEDURE [InsertOrUpdateInverterData]
    @InverterID bigint, @TimeStamp datetime
    , @ValueA decimal(18,2), @ValueB decimal(18,2)
AS
BEGIN
    MERGE [InverterData] AS TARGET
        USING (VALUES (@InverterID, @TimeStamp, @ValueA, @ValueB))
        AS SOURCE ([InverterID], [TimeStamp], [ValueA], [ValueB])
        ON TARGET.[InverterID] = @InverterID AND TARGET.[TimeStamp] = @TimeStamp
    WHEN MATCHED THEN
        UPDATE
        SET [ValueA] = SOURCE.[ValueA], [ValueB] = SOURCE.[ValueB]              
    WHEN NOT MATCHED THEN
        INSERT ([InverterID], [TimeStamp], [ValueA], [ValueB]) 
        VALUES (SOURCE.[InverterID], SOURCE.[TimeStamp], SOURCE.[ValueA], SOURCE.[ValueB]);
END

प्रयोग

अब मैंने कई सर्वरों पर सेवा इंस्टेंसेस चलाए हैं जो [InsertOrUpdateInverterData]प्रक्रिया को तेजी से कॉल करके बड़े पैमाने पर अपडेट करते हैं ।

एक वेबसाइट भी है जो [InverterData]टेबल पर सेलेक्ट क्वेश्चन करती है।

मुसीबत

यदि मैं उस [InverterData]टेबल पर सेलेक्ट क्वेश्चन करता हूं, जिसे वे अलग-अलग टाइमपासों में आगे बढ़ाते हैं, तो मेरे सेवा इंस्टेंसेस के INSERT उपयोग पर निर्भर करता है। अगर मैं सभी सर्विस इंस्टेंस को रोक देता हूं, तो सेलेक्ट लाइटनिंग-फास्ट है, अगर इंस्टेंस तेजी से इंसर्ट करता है तो सेलेक्ट्स वास्तव में स्लो हो जाते हैं या टाइमआउट कैंसिल भी हो जाते हैं।

प्रयास

मैं [sys.dm_tran_locks]लॉकिंग प्रक्रियाओं को खोजने के लिए मेज पर कुछ चयन कर रहा हूं , जैसे

SELECT
tl.request_session_id,
wt.blocking_session_id,
OBJECT_NAME(p.OBJECT_ID) BlockedObjectName,
h1.TEXT AS RequestingText,
h2.TEXT AS BlockingText,
tl.request_mode

FROM sys.dm_tran_locks AS tl

INNER JOIN sys.dm_os_waiting_tasks AS wt ON tl.lock_owner_address = wt.resource_address
INNER JOIN sys.partitions AS p ON p.hobt_id = tl.resource_associated_entity_id
INNER JOIN sys.dm_exec_connections ec1 ON ec1.session_id = tl.request_session_id
INNER JOIN sys.dm_exec_connections ec2 ON ec2.session_id = wt.blocking_session_id
CROSS APPLY sys.dm_exec_sql_text(ec1.most_recent_sql_handle) AS h1
CROSS APPLY sys.dm_exec_sql_text(ec2.most_recent_sql_handle) AS h2

यह परिणाम है:

यहाँ छवि विवरण दर्ज करें

एस = साझा किया गया। होल्डिंग सत्र को संसाधन तक साझा पहुंच प्रदान की जाती है।

सवाल

चयन [InsertOrUpdateInverterData]प्रक्रिया केवल MERGE कमांड का उपयोग करने वाली प्रक्रिया से अवरुद्ध क्यों हैं ?

क्या मुझे परिभाषित आइसोलेशन मोड के साथ किसी प्रकार के लेनदेन का उपयोग करना है [InsertOrUpdateInverterData]?

अपडेट 1 (@Paul से संबंधित प्रश्न)

[InsertOrUpdateInverterData]निम्नलिखित आंकड़ों के बारे में MS-SQL सर्वर आंतरिक रिपोर्टिंग के आधार पर :

  • औसत CPU-समय: 0.12ms
  • औसत पढ़ने की प्रक्रिया: 5.76 प्रति / से
  • औसत लिखने की प्रक्रिया: 0.4 प्रति / एस

इसके आधार पर ऐसा लगता है कि MERGE कमांड ज्यादातर पढ़ने के संचालन में व्यस्त है जो तालिका को बंद कर देगा? (?)

अपडेट 2 (@Paul से संबंधित प्रश्न)

[InverterData]तालिका के रूप में भंडारण आँकड़े निम्नलिखित है:

  • डेटा स्पेस: 26,901.86 एमबी
  • पंक्ति गणना: 131,827,749
  • विभाजन: सच
  • विभाजन की संख्या: 62

यहाँ (सभी) पूर्ण sp_WhoIsActive परिणाम सेट है:

SELECT आदेश

  • dd hh: mm: ss.mss: 00 00: 01: 01.930
  • session_id: 73
  • Wait_info: (12629ms) LCK_M_S
  • सीपीयू: 198
  • block_session_id: 146
  • पढ़ता है: 99,368
  • लिखते हैं: 0
  • स्थिति: निलंबित
  • open_tran_count: 0

ब्लॉकिंग [InsertOrUpdateInverterData]कमांड

  • dd hh: mm: ss.mss: 00 00: 00: 00.330
  • session_id: 146
  • Wait_info: NULL
  • सीपीयू: 3,972
  • block_session_id: NULL
  • पढ़ता है: 376,95
  • लिखते हैं: 126
  • स्थिति: नींद
  • open_tran_count: 1

([TimeStamp] DESC, [InverterID] ASC)दिखता संकुल अनुक्रमणिका के लिए एक अजीब विकल्प की तरह। मेरा मतलब DESCहिस्सा है।
ypercube y

मुझे आपकी बात समझ में आती है: क्लस्टर इंडेक्स DESC डेटा डालने से टेबल रिड्यूस करने की बजाए अंत में अपील करने पर मजबूर होगी ... परफॉर्मेंस डॉग; पुनर्निर्माण होने पर मेज पर ताला लगाया जाएगा ... हाँ। जोव द्वारा, आपके पास है। संरचना ताले से अधिक लॉक करने का कारण है।
एलोकाइट

जवाबों:


12

सबसे पहले, हालांकि मुख्य प्रश्न से थोड़ा असंबंधित, आपका MERGEबयान संभावित रूप से एक दौड़ की स्थिति के कारण त्रुटियों का खतरा है । संक्षेप में, समस्या यह है कि कई समवर्ती धागे के लिए यह निष्कर्ष निकालना संभव है कि लक्ष्य पंक्ति मौजूद नहीं है, जिसके परिणामस्वरूप सम्मिलित करने के प्रयास टकराते हैं। मूल कारण यह है कि एक पंक्ति पर एक साझा या अपडेट लॉक लेना संभव नहीं है जो मौजूद नहीं है। समाधान एक संकेत जोड़ना है:

MERGE [dbo].[InverterData] WITH (SERIALIZABLE) AS [TARGET]

Serializable अलगाव स्तर संकेत कुंजी रेंज जहां पंक्ति जाना होगा लॉक किया गया है सुनिश्चित करता है। आपके पास रेंज लॉकिंग का समर्थन करने के लिए एक अद्वितीय सूचकांक है, इसलिए इस संकेत का लॉकिंग पर प्रतिकूल प्रभाव नहीं पड़ेगा, आप बस इस संभावित दौड़ की स्थिति के खिलाफ सुरक्षा प्राप्त करेंगे।

मुख्य प्रश्न

क्यों SELECTs[InsertOrUpdateInverterData] प्रक्रिया द्वारा अवरुद्ध किया जाता है जो केवल MERGEकमांड का उपयोग कर रहा है ?

डिफॉल्ट लॉकिंग रीड कमिटेड आइसोलेशन लेवल के तहत , डेटा को पढ़ते समय साझा (S) लॉक लिया जाता है, और आम तौर पर (हालांकि हमेशा नहीं) रीड पूरा होने के तुरंत बाद जारी किया जाता है। कुछ साझा ताले बयान के अंत तक आयोजित किए जाते हैं।

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

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

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

open_tran_count: 1InsertOrUpdateInverterData आदेश पर जांच कर रही लायक है। हालाँकि यह आदेश बहुत लंबे समय से नहीं चल रहा था, आपको यह देखना चाहिए कि आपके पास कोई लेन-देन (एप्लिकेशन या उच्च-स्तरीय संग्रहीत कार्यविधि में) नहीं है जो अनावश्यक रूप से लंबा है। सबसे अच्छा अभ्यास लेनदेन को यथासंभव कम रखना है। यह कुछ भी नहीं हो सकता है, लेकिन आपको निश्चित रूप से जांच करनी चाहिए।

संभावित समाधान

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

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

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

नोट: आरसीएसआई के विपरीत (जो एक बार पढ़ने में सक्षम सभी लेनदेन पर लागू होता है), एसआई को स्पष्ट रूप से उपयोग करने का अनुरोध करना होगा SET TRANSACTION ISOLATION SNAPSHOT;

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

अंत में, आपको यह सुनिश्चित करना चाहिए कि आपका उदाहरण SQL Server 2008 सर्विस पैक 4 में पैच किया गया है।


0

विनम्रतापूर्वक, मैं मर्ज का उपयोग नहीं करूंगा। मैं IF एक्जिस्ट (UPDATE) ELSE (INSERT) के साथ जाऊंगा - आपके पास दो कॉलम के साथ एक क्लस्टर की हुई कुंजी है जिसका उपयोग आप पंक्तियों की पहचान करने के लिए कर रहे हैं ताकि यह एक आसान परीक्षा हो।

आप MASSIVE आवेषण का उल्लेख करते हैं और फिर भी 1 से 1 करते हैं ... एक स्टेजिंग टेबल में डेटा को बैचने और एक बार में 1 से अधिक अपडेट / सम्मिलित करने के लिए POWER OVERWHELMING SQL डेटा सेट पावर का उपयोग करने के बारे में सोचा? जैसे स्टेजिंग टेबल में कंटेंट के लिए एक रूटीन टेस्टिंग होती है, और एक बार में 1 के बजाय एक बार में शीर्ष 10000 को हथियाना है ...

मैं अपने अपडेट में ऐसा कुछ करूंगा

DECLARE @Set TABLE (StagingKey, ID,DATE)
INSERT INTO @Set
UPDATE Staging 
SET InProgress = 1
OUTPUT StagingKey, Staging.ID, Staging.Date
WHERE InProgress = 0
AND StagingID IN (SELECT TOP (100000) StagingKey FROM Staging WHERE inProgress = 0 ORDER BY StagingKey ASC ) --FIFO

DECLARE @Temp 
INSERT INTO @TEMP 
UPDATE [DEST] SET Value = Staging.Value [whatever]
OUTPUT INSERTED.ID, DATE [row identifiers]
FROM [DEST] 
JOIN [STAGING]
JOIN [@SET]; 
INSERT INTO @TEMP 
INSERT [DEST] 
SELECT
OUTPUT INSERT.ID, DATE [row identifiers] 
FROM [STAGING] 
JOIN [@SET] 
LEFT JOIN [DEST]

UPDATE Staging
SET inProgress = NULL
FROM Staging 
JOIN @set
ON @Set.Key = Staging.Key
JOIN @temp
ON @temp.id = @set.ID
AND @temp.date = @set.Date

आप शायद अपडेट बैचों को पॉप करने वाले कई काम चला सकते हैं, और आपको एक अलग नौकरी की आवश्यकता होगी जो एक ट्रिकल डिलीट हो

while exists (inProgress is null) 
delete top (100) from staging where inProgress is null 

मंचन तालिका को साफ करने के लिए।

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