ओरेकल में बहुत बड़े रिकॉर्डसेट को हटाने का सबसे अच्छा तरीका


18

मैं एक ऐसे एप्लिकेशन का प्रबंधन करता हूं जिसमें एक बहुत बड़ी (लगभग 1TB डेटा एक तालिका में 500 मिलियन से अधिक पंक्तियों के साथ) Oracle डेटाबेस बैक एंड है। डेटाबेस वास्तव में कुछ भी नहीं करता है (कोई SProcs, कोई ट्रिगर या कुछ भी नहीं) यह सिर्फ एक डेटा स्टोर है।

हर महीने हमें मुख्य टेबल के दो से रिकॉर्ड को शुद्ध करने की आवश्यकता होती है। पर्स के मानदंड अलग-अलग होते हैं और यह पंक्ति की आयु और स्थिति क्षेत्रों के एक जोड़े का संयोजन होता है। हम आम तौर पर प्रति माह 10 से 50 मिलियन पंक्तियों के बीच की समाप्ति करते हैं (हम आयात के माध्यम से एक सप्ताह में लगभग 3-5 मिलियन पंक्तियों को जोड़ते हैं)।

वर्तमान में हमें यह डिलीट लगभग 50,000 पंक्तियों के बैचों में करना है (जैसे 50000 हटाएं, कॉमिट, 50000 डिलीट करें, कमिट करें, रिपीट करें)। एक समय में पूरे बैच को हटाने का प्रयास डेटाबेस को लगभग एक घंटे (पंक्तियों के # के आधार पर) के लिए गैर-जिम्मेदार बनाता है। बैचों में पंक्तियों को इस तरह से हटाना प्रणाली पर बहुत ही कठिन है और हमें आमतौर पर एक सप्ताह के दौरान इसे "समय परमिट" के रूप में करना पड़ता है; स्क्रिप्ट को लगातार चलाने की अनुमति देने से उपयोगकर्ता के लिए अस्वीकार्य प्रदर्शन में गिरावट आ सकती है।

मेरा मानना ​​है कि इस तरह का बैच डिलीट करना इंडेक्स परफॉर्मेंस को भी खराब कर देता है और दूसरे इफेक्ट्स हैं जो आखिरकार डेटाबेस के प्रदर्शन को नीचा दिखाने का कारण बनते हैं। सिर्फ एक टेबल पर 34 इंडेक्स हैं, और इंडेक्स डेटा का आकार वास्तव में डेटा से बड़ा है।

यहाँ वह स्क्रिप्ट है जो हमारे एक आईटी लोग इस पर्स को करने के लिए उपयोग करते हैं:

BEGIN
LOOP

delete FROM tbl_raw 
  where dist_event_date < to_date('[date]','mm/dd/yyyy') and rownum < 50000;

  exit when SQL%rowcount < 49999;

  commit;

END LOOP;

commit;

END;

यह डेटाबेस 99.99999% होना चाहिए और हमें केवल 2 बार एक वर्ष में एक बार रखरखाव खिड़की मिल गई है।

मैं इन रिकॉर्ड्स को हटाने के लिए एक बेहतर विधि की तलाश कर रहा हूं, लेकिन मुझे अभी तक कोई भी नहीं मिला है। कोई सुझाव?


यह भी ध्यान रखें वहाँ खेलने में 30+ अनुक्रमित यहाँ हैं
jcolebrand

जवाबों:


17

'ए' और 'बी' वाला तर्क एक वर्चुअल कॉलम के पीछे "छिपा" हो सकता है, जिस पर आप विभाजन कर सकते हैं:

alter session set nls_date_format = 'yyyy-mm-dd';
drop   table tq84_partitioned_table;

create table tq84_partitioned_table (
  status varchar2(1)          not null check (status in ('A', 'B')),
  date_a          date        not null,
  date_b          date        not null,
  date_too_old    date as
                       (  case status
                                 when 'A' then add_months(date_a, -7*12)
                                 when 'B' then            date_b
                                 end
                        ) virtual,
  data            varchar2(100) 
)
partition   by range  (date_too_old) 
( 
  partition p_before_2000_10 values less than (date '2000-10-01'),
  partition p_before_2000_11 values less than (date '2000-11-01'),
  partition p_before_2000_12 values less than (date '2000-12-01'),
  --
  partition p_before_2001_01 values less than (date '2001-01-01'),
  partition p_before_2001_02 values less than (date '2001-02-01'),
  partition p_before_2001_03 values less than (date '2001-03-01'),
  partition p_before_2001_04 values less than (date '2001-04-01'),
  partition p_before_2001_05 values less than (date '2001-05-01'),
  partition p_before_2001_06 values less than (date '2001-06-01'),
  -- and so on and so forth..
  partition p_ values less than (maxvalue)
);

insert into tq84_partitioned_table (status, date_a, date_b, data) values 
('B', date '2008-04-14', date '2000-05-17', 
 'B and 2000-05-17 is older than 10 yrs, must be deleted');


insert into tq84_partitioned_table (status, date_a, date_b, data) values 
('B', date '1999-09-19', date '2004-02-12', 
 'B and 2004-02-12 is younger than 10 yrs, must be kept');


insert into tq84_partitioned_table (status, date_a, date_b, data) values 
('A', date '2000-06-16', date '2010-01-01', 
 'A and 2000-06-16 is older than 3 yrs, must be deleted');


insert into tq84_partitioned_table (status, date_a, date_b, data) values 
('A', date '2009-06-09', date '1999-08-28', 
 'A and 2009-06-09 is younger than 3 yrs, must be kept');

select * from tq84_partitioned_table order by date_too_old;

-- drop partitions older than 10 or 3 years, respectively:

alter table tq84_partitioned_table drop partition p_before_2000_10;
alter table tq84_partitioned_table drop partition p_before_2000_11;
alter table tq84_partitioned_table drop partition p2000_12;

select * from tq84_partitioned_table order by date_too_old;

मैंने तर्क को सरल बनाया है कि कैसे शुद्ध करने के लिए रिकॉर्ड निर्धारित किए जाते हैं, लेकिन यह एक बहुत ही दिलचस्प विचार है। हालांकि, एक बात जिस पर विचार किया जाना चाहिए, वह है दिन प्रदर्शन। Purging "हमारी समस्या" है, ग्राहक केवल हल करने के लिए अपमानित प्रदर्शन को स्वीकार नहीं करेगा। यह कुछ टिप्पणियों और गैरी के जवाब से लगता है कि यह विभाजन के साथ एक मुद्दा हो सकता है?
गोरिल्ला

मुझे यकीन नहीं है कि अगर यह जवाब है जिसकी हम तलाश कर रहे हैं, लेकिन यह निश्चित रूप से एक बहुत ही दिलचस्प दृष्टिकोण है जिसकी हम जांच करेंगे।
गोरिल्ला

14

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

और विभाजन के साथ आप समानांतर क्वेरी और विभाजन उन्मूलन का भी फायदा उठा सकते हैं , जिससे आपके उपयोगकर्ताओं को बहुत खुश होना चाहिए ...


एफडब्ल्यूआईडब्ल्यू हम इस तकनीक का उपयोग मेरी साइट पर 30 टीबी + डेटाबेस
गयुस

विभाजन के साथ समस्या यह है कि डेटा को विभाजित करने का कोई स्पष्ट तरीका नहीं है। दो तालिकाओं में से एक में (नीचे नहीं दिखाया गया है) पर्स करने के लिए उपयोग किए जाने वाले मापदंड दो अलग-अलग (और अलग) दिनांक फ़ील्ड और एक स्थिति फ़ील्ड पर आधारित होते हैं। उदाहरण के लिए, स्थिति है अगर Aहै, तो उसके बाद DateAसे अधिक उम्र के 3 साल है, यह साफ़ कर दिया जाता है। यदि स्थिति है Bऔर DateBबड़े से अधिक 10 साल है, यह साफ़ कर दिया जाता है। यदि विभाजन के बारे में मेरी समझ सही है, तो विभाजन ऐसी स्थिति में उपयोगी नहीं होगा (कम से कम जहाँ तक पर्सिंग का संबंध है)।
कोडिंग गोरिल्ला

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

6
वैकल्पिक रूप से आप एक वर्चुअल कॉलम बना सकते हैं जो DateA दिखाता है जब स्टेटस A और DateB होता है जब स्टेटस B होता है और फिर वर्चुअल कॉलम पर विभाजन होता है। समान विभाजन प्रवासन होगा, लेकिन यह आपके शुद्धिकरण में मदद करेगा। ऐसा लगता है कि यह पहले से ही एक उत्तर के रूप में पोस्ट किया गया था।
लेह रिफ़ेल

4

विचार करने का एक पहलू यह है कि इंडेक्स से डिलीट परफॉर्मेंस का परिणाम कितना है और रॉ टेबल से कितना है। तालिका से हटाए गए प्रत्येक रिकॉर्ड को प्रत्येक btree सूचकांक से पंक्ति को हटाने की आवश्यकता होती है। यदि आपको 30+ btree इंडेक्स मिला है, तो मुझे संदेह है कि आपका अधिकांश समय इंडेक्स मेंटेनेंस में बीतता है।

इससे विभाजन की उपयोगिता पर प्रभाव पड़ता है। कहते हैं कि आपके पास नाम पर एक सूचकांक है। एक मानक बीट्री इंडेक्स, सभी एक सेगमेंट में, रूट ब्लॉक से लीफ ब्लॉक तक जाने के लिए चार जंप करने पड़ सकते हैं और पंक्ति को पढ़ने के लिए पांचवां रीड किया जा सकता है। यदि उस सूचकांक को 50 खंडों में विभाजित किया गया है और आपके पास क्वेरी के भाग के रूप में विभाजन कुंजी नहीं है, तो उन 50 खंडों में से प्रत्येक को जांचना होगा। प्रत्येक सेगमेंट छोटा होगा, इसलिए आपको केवल 2 जंप करने पड़ सकते हैं, लेकिन आप पिछले 5 के बजाय 100 रीड कर सकते हैं।

यदि वे बिटमैप इंडेक्स हैं, तो समीकरण अलग-अलग हैं। आप शायद अलग-अलग पंक्तियों की पहचान करने के लिए अनुक्रमित का उपयोग नहीं कर रहे हैं, बल्कि उनके सेट करते हैं। इसलिए एक एकल रिकॉर्ड को वापस करने के लिए 5 IOs का उपयोग करने के लिए क्वेरी के बजाय, यह 10,000 IOs का उपयोग कर रहा था। जैसे कि इंडेक्स के लिए अतिरिक्त विभाजन में अतिरिक्त ओवरहेड मायने नहीं रखेगा।


2

50,000 के बैच में प्रति माह 50 मिलियन रिकॉर्ड को हटाना केवल 1000 पुनरावृत्तियों है। यदि आप हर 30 मिनट में 1 हटाते हैं तो यह आपकी आवश्यकता को पूरा करना चाहिए। आपके द्वारा पोस्ट की गई क्वेरी को चलाने के लिए एक निर्धारित कार्य, लेकिन लूप को हटा दें ताकि यह केवल एक बार निष्पादित हो जाए, जिससे उपयोगकर्ताओं को ध्यान देने योग्य विकृतीकरण न हो। हम अपने विनिर्माण संयंत्र में रिकॉर्ड्स की समान मात्रा के बारे में करते हैं जो बहुत अधिक 24/7 चलता है और यह हमारी जरूरतों को पूरा करता है। हमने वास्तव में इसे हर 10 मिनट में थोड़ा और 10,000 रिकॉर्ड फैला दिया, जो कि हमारे ओरेकल यूनिक्स सर्वर पर चलने वाले लगभग 1 या 2 सेकंड में निष्पादित होता है।


बड़े पैमाने पर 'पूर्ववत करें' और 'हटाएं' 'हटाने' के बारे में क्या होगा? यह आईओ को भी चोक कर देता है ... 'डिलीट' आधारित दृष्टिकोण निश्चित रूप से बड़ी तालिकाओं के लिए NO .. NO होना चाहिए।
पहारयोगी

1

यदि डिस्क स्थान एक प्रीमियम पर नहीं है, तो आप तालिका की "कार्य" प्रतिलिपि बनाने में सक्षम हो सकते हैं, कहते हैं my_table_new मापदंड के साथ CTAS (Create Table As Select) का उपयोग करके , जो रिकॉर्ड गिराए जाने के लिए छोड़ देगा। आप बना स्टेटमेंट समानांतर में कर सकते हैं, और इसे तेजी से बनाने के लिए परिशिष्ट संकेत के साथ, और फिर अपने सभी इंडेक्स का निर्माण कर सकते हैं। फिर, एक बार जब यह समाप्त हो गया, (और परीक्षण किया गया), मौजूदा तालिका का my_table_oldनाम बदलें और "काम" तालिका का नाम बदलें my_table। एक बार जब आप drop my_table_old purgeपुरानी मेज से छुटकारा पाने के लिए सब कुछ के साथ सहज होते हैं । यदि विदेशी कुंजी प्रतिबंधों का एक गुच्छा है, तो dbms_redefinition PL / SQL पैकेज पर एक नज़र डालें । उपयुक्त विकल्पों का उपयोग करते समय यह आपके अनुक्रमित, विरोधाभासों आदि को क्लोन करेगा। यह टॉम Kyte के एक सुझाव का एक सारांश है AskTom के कायटे सारांश हैप्रसिद्धि। पहले रन के बाद, आप सब कुछ स्वचालित कर सकते हैं, और बनाने की मेज को बहुत जल्दी जाना चाहिए, और सिस्टम के ऊपर रहने के दौरान किया जा सकता है, और एप्लिकेशन डाउनटाइम तालिकाओं का नाम बदलने के लिए एक मिनट से भी कम तक सीमित होगा। CTAS का उपयोग कई बैच डिलीट करने की तुलना में अधिक तेज़ होगा। यदि आपके पास लाइसेंसिंग विभाजन नहीं है, तो यह दृष्टिकोण विशेष रूप से उपयोगी हो सकता है।

नमूना CTAS, पिछले 365 दिनों के आंकड़ों के साथ पंक्तियाँ रखना और flag_inactive = 'N':

create /*+ append */ table my_table_new 
   tablespace data as
   select /*+ parallel */ * from my_table 
       where some_date >= sysdate -365 
       and flag_inactive = 'N';

-- test out my_table_new. then if all is well:

alter table my_table rename to my_table_old;
alter table my_table_new rename to my_table;
-- test some more
drop table my_table_old purge;

1
इस पर विचार किया जा सकता है यदि (ए) शुद्धिकरण एक एकल कार्य है। (बी) यदि आप कम पंक्तियों को बनाए रखने के लिए और अधिकांश डेटा को हटाने के लिए ...
पहरयोगी

0

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

"हम आम तौर पर प्रति माह 10 और 50 मिलियन पंक्तियों के बीच शुद्धिकरण करते हैं"

मुझे लगता है कि PL / SQL बैच हटाने का उपयोग करने की सिफारिश की जाएगी, कई घंटे ठीक है मुझे लगता है।


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

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