วิธีที่ดีที่สุดในการลบชุดระเบียนที่มีขนาดใหญ่มากใน Oracle


18

ฉันจัดการแอปพลิเคชันที่มีข้อมูลขนาดใหญ่มาก (เกือบ 1TB ของข้อมูลมีมากกว่า 500 ล้านแถวในหนึ่งตาราง) ฐานข้อมูล Oracle ส่วนหลัง ฐานข้อมูลไม่ได้ทำอะไรเลย (ไม่มี SProcs, ไม่มีทริกเกอร์หรืออะไรเลย) เป็นเพียงแหล่งข้อมูล

ทุกเดือนเราจะต้องล้างเร็กคอร์ดจากตารางหลักสองตาราง เกณฑ์สำหรับการกำจัดแตกต่างกันไปและเป็นการรวมกันของอายุแถวและฟิลด์สถานะสองสาม โดยทั่วไปแล้วเราจะกวาดล้างระหว่าง 10 ถึง 50 ล้านแถวต่อเดือน (เราเพิ่มประมาณ 3-5 ล้านแถวต่อสัปดาห์ผ่านการนำเข้า)

ขณะนี้เราต้องทำการลบแบบกลุ่มประมาณ 50,000 แถว (เช่นลบ 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% และเรามีหน้าต่างการบำรุงรักษา 2 วันต่อปี

ฉันกำลังมองหาวิธีที่ดีกว่าสำหรับการลบระเบียนเหล่านี้ แต่ฉันยังไม่พบรายการใด ๆ ข้อเสนอแนะใด ๆ


โปรดทราบว่ามีดัชนีมากกว่า 30 รายการในการเล่นที่นี่
jcolebrand

คำตอบ:


17

ตรรกะที่มี 'A' และ 'B' อาจเป็น "ซ่อนอยู่" หลังคอลัมน์เสมือนที่คุณสามารถทำการแบ่งพาร์ติชันได้:

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;

ฉันอาจใช้ตรรกะที่เรียบง่ายไปกว่าวิธีการที่กำหนดไว้เพื่อล้างข้อมูล แต่นี่เป็นแนวคิดที่น่าสนใจมาก อย่างไรก็ตามสิ่งหนึ่งที่ต้องพิจารณาคือการแสดงวันต่อวัน การล้างคือ "ปัญหาของเรา" ลูกค้าจะไม่ยอมรับประสิทธิภาพที่ลดลงเพียงเพื่อแก้ปัญหานั้น ฟังดูจากความคิดเห็นบางส่วนและคำตอบของ Gary ว่านี่อาจเป็นปัญหาเกี่ยวกับการแบ่งพาร์ติชันหรือไม่
เข้ารหัส Gorilla

ฉันไม่แน่ใจว่านี่เป็นคำตอบที่เรากำลังมองหาหรือไม่ แต่นี่เป็นแนวทางที่น่าสนใจอย่างยิ่งที่เราจะทำการตรวจสอบ
เข้ารหัส Gorilla

14

โซลูชันแบบคลาสสิกสำหรับสิ่งนี้คือการแบ่งพาร์ติชันตารางของคุณเช่นตามเดือนหรือรายสัปดาห์ หากคุณยังไม่เคยพบเจอมาก่อนตารางที่มีการแบ่งพาร์ติชั่นจะเหมือนกับตารางที่มีโครงสร้างเหมือนกันหลายตัวที่มีความหมายUNIONเมื่อเลือกและ Oracle จะจัดเก็บแถวในพาร์ติชันที่เหมาะสมโดยอัตโนมัติเมื่อทำการแทรกตามเกณฑ์การแบ่งพาร์ติชัน คุณพูดถึงดัชนี - แต่ละพาร์ติชั่นจะได้รับดัชนีแบ่งพาร์ติชันของตัวเองเช่นกัน เป็นการดำเนินการที่ถูกมากใน Oracle เพื่อวางพาร์ติชัน (คล้ายกับ aTRUNCATEในแง่ของการโหลดเพราะนั่นคือสิ่งที่คุณกำลังทำจริง ๆ - ตัดทอนหรือวางหนึ่งในตารางย่อยที่มองไม่เห็นเหล่านี้) มันจะเป็นการประมวลผลจำนวนมากเพื่อแบ่งพาร์ติชัน "หลังจากข้อเท็จจริง" แต่ไม่มีความรู้สึกร้องไห้นมที่หก - ข้อดีของการทำเช่นนี้เกินดุลค่าใช้จ่าย ทุกเดือนคุณจะแบ่งพาร์ติชันด้านบนเพื่อสร้างพาร์ติชันใหม่สำหรับข้อมูลของเดือนถัดไป (คุณสามารถทำให้เป็นอัตโนมัติด้วย a DBMS_JOB)

และด้วยพาร์ติชันคุณสามารถใช้ประโยชน์จากการสอบถามแบบขนานและการกำจัดพาร์ติชันซึ่งจะทำให้ผู้ใช้ของคุณมีความสุขมาก ...


FWIW เราใช้ใช้เทคนิคนี้ในเว็บไซต์ของฉันในฐานข้อมูล 30Tb +
ออกุสตุ

ปัญหาเกี่ยวกับการแบ่งพาร์ติชันคือไม่มีวิธีที่ชัดเจนในการแบ่งพาร์ติชันข้อมูล ในหนึ่งในสองตาราง (ไม่ใช่ที่แสดงด้านล่าง) เกณฑ์ที่ใช้ในการล้างจะขึ้นอยู่กับเขตข้อมูลวันที่สอง (และแตกต่างกัน) และเขตข้อมูลสถานะ ตัวอย่างเช่นหากสถานะAนั้นหากDateAมีอายุมากกว่า 3 ปีสถานะจะถูกลบทิ้ง หากสถานะเป็นBและDateBมีอายุมากกว่า 10 ปีสถานะจะถูกลบทิ้ง หากความเข้าใจในการแบ่งพาร์ติชั่นของฉันถูกต้องการแบ่งพาร์ติชันจะไม่เป็นประโยชน์ในสถานการณ์เช่นนี้ (อย่างน้อยที่สุดเท่าที่การกวาดล้างจะเกี่ยวข้อง)
เข้ารหัส Gorilla

คุณสามารถแบ่งพาร์ติชันตามสถานะและส่วนย่อยตามช่วงวันที่ แต่ถ้าสถานะ (หรือวันที่) เปลี่ยนแปลงจะเป็นการลบอย่างมีประสิทธิภาพจากพาร์ติชันย่อยหนึ่งและการแทรกเข้าไปในอีก ในระยะสั้นคุณสามารถได้รับความนิยมในกระบวนการประจำวันของคุณเพื่อประหยัดเวลาในการกำจัดของคุณ
Gary

6
อีกทางหนึ่งคุณสามารถสร้างคอลัมน์เสมือนที่แสดง DateA เมื่อสถานะเป็น A และ DateB เมื่อสถานะเป็น B แล้วพาร์ติชันบนคอลัมน์เสมือน การโยกย้ายพาร์ติชันเดียวกันจะเกิดขึ้น แต่จะช่วยให้การล้าง ดูเหมือนว่านี่โพสต์แล้วเป็นคำตอบ
Leigh Riffel

4

แง่มุมหนึ่งที่ควรพิจารณาคือประสิทธิภาพการลบเป็นผลมาจากดัชนีและจำนวนเท่าไรจากตารางดิบ ทุกระเบียนที่ถูกลบจากตารางจะต้องมีการลบแถวเดียวกันจากดัชนี btree ทุกตัว หากคุณมีดัชนีมากกว่า 30 btree ฉันสงสัยว่าใช้เวลาส่วนใหญ่ในการบำรุงรักษาดัชนี

สิ่งนี้มีผลกระทบต่อประโยชน์ของการแบ่งพาร์ติชัน สมมติว่าคุณมีดัชนีชื่อ ดัชนี Btree มาตรฐานทั้งหมดในส่วนเดียวอาจต้องทำการข้ามสี่ครั้งเพื่อรับจากบล็อกรูทถึงบล็อกลีฟและการอ่านครั้งที่ห้าเพื่อให้ได้แถว หากดัชนีนั้นแบ่งเป็น 50 เซกเมนต์และคุณไม่มีคีย์พาร์ติชันเป็นส่วนหนึ่งของเคียวรีคุณจะต้องตรวจสอบ 50 เซกเมนต์เหล่านั้นแต่ละรายการ แต่ละส่วนจะมีขนาดเล็กลงดังนั้นคุณอาจต้องทำการกระโดด 2 ครั้งเท่านั้น แต่คุณอาจจบลงด้วยการอ่าน 100 ครั้งแทนที่จะอ่าน 5 ครั้งก่อนหน้า

หากเป็นดัชนีบิตแมปสมการจะต่างกัน คุณอาจไม่ได้ใช้ดัชนีเพื่อระบุแถวแต่ละแถว แต่แทนที่จะเป็นชุด ดังนั้นแทนที่จะใช้เคียวรีที่ใช้ 5 IOs เพื่อส่งกลับระเบียนเดียวมันใช้ 10,000 IOs เช่นค่าใช้จ่ายเพิ่มเติมในพาร์ทิชันพิเศษสำหรับดัชนีจะไม่สำคัญ


2

การลบ 50 ล้านเร็กคอร์ดต่อเดือนในแบตช์ 50,000 เป็นเพียง 1,000 ซ้ำ หากคุณลบ 1 ครั้งทุก ๆ 30 นาทีมันควรจะตอบสนองความต้องการของคุณ งานที่กำหนดเวลาไว้ให้เรียกใช้คิวรีที่คุณโพสต์ แต่ลบลูปเพื่อให้เรียกใช้งานได้เพียงครั้งเดียวไม่ควรทำให้ผู้ใช้เกิดการลดระดับลงอย่างเห็นได้ชัด เราทำบันทึกข้อมูลปริมาณเท่ากันในโรงงานผลิตของเราซึ่งทำงานตลอด 24 ชั่วโมงทุกวันและตรงตามความต้องการของเรา เราเผยแพร่ไปมากกว่า 10,000 เร็กคอร์ดเล็กน้อยทุก ๆ 10 นาทีซึ่งดำเนินการในประมาณ 1 หรือ 2 วินาทีที่ทำงานบนเซิร์ฟเวอร์ Oracle unix ของเรา


จะเกิดอะไรขึ้นถ้า 'เลิกทำ' และ 'ทำซ้ำ' 'ลบ' มาก? มันฉายา IO ด้วยเช่นกัน ... 'ลบ' วิธีการที่ใช้ควรเป็น NO .. NO สำหรับตารางขนาดใหญ่
pahariayogi

1

หากพื้นที่ว่างในดิสก์ไม่อยู่ในระดับพรีเมียมคุณสามารถสร้างสำเนา "งาน" ของตารางพูดmy_table_newโดยใช้ CTAS (สร้างตารางเป็นเลือก) ด้วยเกณฑ์ที่จะตัดเรคคอร์ดที่จะทิ้ง คุณสามารถสร้างคำสั่งแบบขนานและใช้คำใบ้ต่อท้ายเพื่อทำให้มันเร็วและสร้างดัชนีทั้งหมดของคุณ จากนั้นเมื่อมันเสร็จแล้ว (และทดสอบ) เปลี่ยนชื่อตารางที่มีอยู่เพื่อmy_table_oldและเปลี่ยนชื่อ "งาน" my_tableตาราง เมื่อคุณพอใจกับทุกอย่างdrop my_table_old purgeเพื่อกำจัดโต๊ะเก่า หากมีพวงของหมอนรองที่สำคัญต่างประเทศให้ดูที่การแพคเกจdbms_redefinition PL / SQL มันจะโคลนดัชนีดัชนีของคุณ ฯลฯ เมื่อใช้ตัวเลือกที่เหมาะสม นี่คือผลรวมของข้อเสนอแนะโดย Tom 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
สิ่งนี้ถือได้ว่า (a) การกวาดล้างเป็นงานที่ต้องทำครั้งเดียว (b) ถ้าคุณเก็บแถวไว้น้อยลงและข้อมูลส่วนใหญ่ที่จะลบ ...
pahariayogi

0

เมื่อวางพาร์ติชันคุณปล่อยให้ดัชนีส่วนกลางไม่สามารถใช้งานได้ซึ่งจำเป็นต้องสร้างใหม่การสร้างดัชนีทั่วโลกจะเป็นปัญหาใหญ่ราวกับว่าคุณทำออนไลน์มันจะช้ามากมิฉะนั้นคุณต้องหยุดทำงาน ในทั้งสองกรณีไม่เหมาะกับความต้องการ

"โดยทั่วไปแล้วเราจะกวาดล้างระหว่าง 10 ถึง 50 ล้านแถวต่อเดือน"

ฉันอยากจะแนะนำให้ใช้การลบชุด PL / SQL หลายชั่วโมงก็โอเคฉันคิดว่า


1
หากคุณมีคีย์หลักการวางพาร์ติชันไม่ควรทำให้ดัชนีส่วนกลางไม่สามารถใช้งานได้ แต่ถ้า OP มีดัชนีทั่วโลกจำนวนมากจะมีค่าใช้จ่ายสูงสำหรับการลดพาร์ติชัน ในกรณีที่เหมาะสมที่สุดเมื่อมีคนกำลังแบ่งพาร์ติชันตารางการแบ่งพาร์ติชั่นจะขึ้นอยู่กับคีย์หลักและพวกเขาไม่ต้องการดัชนีทั่วโลก ที่ทุกแบบสอบถามสามารถใช้ประโยชน์จากการตัดพาร์ติชัน
Gandolf989

@ Gandolf989 การลบพาร์ติชั่นจะทำให้ดัชนีทั่วโลกไม่สามารถใช้งานได้
miracle173
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.