it-swarm.asia

أفضل طريقة لحذف مجموعة سجلات كبيرة جدًا في Oracle

أدير تطبيقًا يحتوي على قاعدة بيانات Oracle كبيرة جدًا (ما يقرب من 1 تيرابايت من البيانات مع أكثر من 500 مليون صف في جدول واحد). قاعدة البيانات لا تفعل شيئًا حقًا (لا يوجد SProcs ولا مشغلات أو أي شيء) إنها مجرد متجر بيانات.

كل شهر ، نحن مطالبون بتنظيف السجلات من الجدولين الرئيسيين. تختلف معايير التطهير وهي مزيج من عمر الصف واثنين من حقول الحالة. عادةً ما ينتهي الأمر بالتطهير بين 10 و 50 مليون صف شهريًا (نضيف حوالي 3-5 ملايين صف أسبوعيًا عن طريق الاستيراد).

حاليًا ، يتعين علينا إجراء هذا الحذف على دفعات من حوالي 50000 صف (على سبيل المثال ، حذف 50000 ، comit ، حذف 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٪ ولدينا فقط نافذة صيانة لمدة يومين مرة واحدة في السنة.

أنا أبحث عن طريقة أفضل لإزالة هذه السجلات ، ولكن لم أجد أي منها. أي اقتراحات؟

19
Coding Gorilla

قد يكون المنطق الذي يحتوي على "أ" و "ب" "مخفيًا" خلف عمود ظاهري يمكنك إجراء التقسيم عليه:

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;
18
René Nyffenegger

الحل الكلاسيكي لهذا هو قسم جداولك ، على سبيل المثال حسب الشهر أو الأسبوع. إذا لم تصادفهم من قبل ، فإن الجدول المقسم يشبه العديد من الجداول ذات الهيكل المتطابق مع UNION الضمني عند التحديد ، وستقوم Oracle تلقائيًا بتخزين صف في القسم المناسب عند إدراجه بناءً على معايير التقسيم. تذكر الفهارس - حسنًا كل قسم يحصل على الفهارس المقسمة الخاصة به أيضًا. إنها عملية رخيصة جدًا في Oracle لإسقاط قسم (يشبه TRUNCATE من حيث التحميل لأن هذا هو ما تفعله حقًا - اقتطاع أو إسقاط أحد هذه الجداول الفرعية غير المرئية). سيكون هناك قدر كبير من المعالجة للتقسيم "بعد الحقيقة" ، ولكن لا يوجد أي شعور بالبكاء على اللبن المسكوب - مزايا القيام بذلك حتى الآن تفوق التكاليف. في كل شهر ، ستقوم بتقسيم القسم العلوي لإنشاء قسم جديد لبيانات الشهر التالي (يمكنك أتمتة هذا القسم بسهولة باستخدام DBMS_JOB).

ومع الأقسام ، يمكنك أيضًا استغلال الاستعلام الموازي و إزالة القسم ، الأمر الذي سيجعل المستخدمين سعداء للغاية ...

14
Gaius

أحد الجوانب التي يجب مراعاتها هو مقدار أداء الحذف الناتج من الفهارس وكم من الجدول الخام. يتطلب كل سجل محذوف من الجدول نفس حذف الصف من كل فهرس btree. إذا كان لديك أكثر من 30 فهرس btree ، أعتقد أن معظم وقتك يقضي في صيانة الفهرس.

هذا له تأثير على فائدة التقسيم. لنفترض أن لديك فهرسًا بالاسم. قد يتعين على مؤشر Btree القياسي ، كل ذلك في جزء واحد ، القيام بأربع قفزات للانتقال من كتلة الجذر إلى كتلة الأوراق وقراءة خامسة للحصول على الصف. إذا تم تقسيم هذا الفهرس إلى 50 مقطعًا ولم يكن لديك مفتاح القسم كجزء من الاستعلام ، فسيتعين عليك التحقق من كل جزء من تلك الأجزاء الـ 50. سيكون كل جزء أصغر حجمًا ، لذلك قد تضطر إلى القيام بقفزين فقط ولكن قد ينتهي بك الأمر في 100 قراءة بدلاً من 5 السابقة.

إذا كانت فهارس نقطية ، فإن المعادلات مختلفة. ربما لا تستخدم الفهارس لتحديد الصفوف الفردية ، بل مجموعات منها. لذا بدلاً من الاستعلام باستخدام 5 IOs لإرجاع سجل واحد ، كان يستخدم 10000 IOs. على هذا النحو لن يكون الحمل الإضافي في الأقسام الإضافية للمؤشر مهمًا.

4
Gary

حذف 50 مليون سجل شهريا على دفعات من 50،000 هو 1000 تكرار فقط. إذا قمت بإجراء حذف واحد كل 30 دقيقة ، فيجب أن يلبي متطلباتك. مهمة مجدولة لتشغيل الاستعلام الذي قمت بنشره ولكن إزالة الحلقة بحيث يتم تنفيذها مرة واحدة فقط لا يجب أن تتسبب في تدهور ملحوظ للمستخدمين. نقوم بنفس حجم السجلات تقريبًا في مصنع التصنيع لدينا والذي يعمل على مدار الساعة طوال أيام الأسبوع ويلبي احتياجاتنا. لقد قمنا في الواقع بنشره أكثر من 10000 سجل كل 10 دقائق ، والذي يتم تنفيذه في حوالي 1 أو 2 ثانية يتم تشغيله على خوادم Oracle unix.

2
Jason Jakob

إذا لم تكن مساحة القرص مرتفعة ، فقد تتمكن من إنشاء نسخة "عمل" من الجدول ، على سبيل المثال my_table_new ، باستخدام CTAS (إنشاء جدول حسب التحديد) مع معايير تحذف السجلات المراد إسقاطها. يمكنك القيام ببيان إنشاء بالتوازي ، ومع تلميح الإلحاق لجعله سريعًا ، ثم إنشاء كافة الفهارس الخاصة بك. ثم ، بمجرد الانتهاء ، (واختبار) ، أعد تسمية الجدول الحالي إلى my_table_old وأعد تسمية جدول "العمل" إلى my_table. بمجرد أن تشعر بالراحة مع كل شيء drop my_table_old purge للتخلص من المائدة القديمة. إذا كانت هناك مجموعة من قيود المفاتيح الخارجية ، ألق نظرة على dbms_redefinitionحزمة PL/SQL . ستقوم باستنساخ الفهارس ، والقيود ، وما إلى ذلك عند استخدام الخيارات المناسبة. هذا هو ملخص لاقتراح توم كيت من 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
Mark Stewart

عند إسقاط أحد الأقسام ، تترك الفهارس العالمية غير قابلة للاستخدام ، وتحتاج إلى إعادة البناء ، وستكون إعادة إنشاء الفهارس العالمية مشكلة كبيرة ، كما لو كنت تفعل ذلك عبر الإنترنت ، فستكون بطيئة جدًا ، وإلا فستحتاج إلى وقت تعطل. في كلتا الحالتين ، لا يمكن أن تتناسب مع المتطلبات.

"عادةً ما ينتهي الأمر بالتطهير بين 10 و 50 مليون صف شهريًا"

أود أن أوصي باستخدام حذف دفعة PL/SQL ، عدة ساعات على ما أعتقد.

0
iceburge5