Cách tốt nhất để xóa recordset rất lớn trong Oracle


18

Tôi quản lý một ứng dụng có dữ liệu rất lớn (gần 1TB dữ liệu với hơn 500 triệu hàng trong một bảng) cơ sở dữ liệu Oracle back end. Cơ sở dữ liệu không thực sự làm bất cứ điều gì (không SProcs, không kích hoạt hoặc bất cứ điều gì) nó chỉ là một kho lưu trữ dữ liệu.

Hàng tháng chúng tôi được yêu cầu thanh lọc hồ sơ từ hai trong số các bảng chính. Các tiêu chí cho thanh trừng khác nhau và là sự kết hợp giữa tuổi hàng và một vài trường trạng thái. Chúng tôi thường kết thúc thanh trừng từ 10 đến 50 triệu hàng mỗi tháng (chúng tôi thêm khoảng 3-5 triệu hàng mỗi tuần thông qua nhập khẩu).

Hiện tại chúng tôi phải thực hiện việc xóa này theo lô khoảng 50.000 hàng (nghĩa là xóa 50000, comit, xóa 50000, cam kết, lặp lại). Việc cố gắng xóa toàn bộ lô cùng một lúc khiến cơ sở dữ liệu không phản hồi trong khoảng một giờ (tùy thuộc vào # hàng). Việc xóa các hàng theo đợt như thế này là rất khó khăn trên hệ thống và chúng tôi thường phải thực hiện "khi thời gian cho phép" trong suốt một tuần; cho phép tập lệnh chạy liên tục có thể dẫn đến suy giảm hiệu suất không thể chấp nhận được đối với người dùng.

Tôi tin rằng loại xóa hàng loạt này cũng làm giảm hiệu suất của chỉ mục và có các tác động khác cuối cùng làm cho hiệu suất của cơ sở dữ liệu bị suy giảm. Có 34 chỉ mục trên một bảng và kích thước dữ liệu chỉ mục thực sự lớn hơn chính dữ liệu.

Đây là kịch bản mà một trong những người CNTT của chúng tôi sử dụng để thực hiện việc thanh lọc này:

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;

Cơ sở dữ liệu này phải tăng 99.99999% và chúng tôi chỉ có một cửa sổ bảo trì 2 ngày một lần mỗi năm.

Tôi đang tìm kiếm một phương pháp tốt hơn để xóa những hồ sơ này, nhưng tôi chưa tìm thấy. Bất kỳ đề xuất?


Cũng lưu ý rằng có hơn 30 chỉ mục đang chơi ở đây
jcolebrand

Câu trả lời:


17

Logic với 'A' và 'B' có thể bị "ẩn" đằng sau một cột ảo mà bạn có thể thực hiện phân vùng:

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;

Tôi có thể đã đơn giản hóa logic đằng sau cách xác định hồ sơ để thanh lọc, nhưng đây là một ý tưởng rất thú vị. Tuy nhiên, một điều phải được xem xét là hiệu suất hàng ngày. Purging là "vấn đề của chúng tôi", khách hàng sẽ không chấp nhận hiệu suất xuống cấp chỉ để giải quyết điều đó. Nghe có vẻ, từ một số ý kiến ​​và câu trả lời của Gary rằng đây có thể là một vấn đề với phân vùng?
Mã hóa Gorilla

Tôi không chắc đây có phải là câu trả lời mà chúng tôi đang tìm kiếm hay không, nhưng đây chắc chắn là một cách tiếp cận rất thú vị mà chúng tôi sẽ điều tra.
Mã hóa Gorilla

14

Giải pháp cổ điển cho vấn đề này là phân vùng các bảng của bạn, ví dụ theo tháng hoặc theo tuần. Nếu bạn chưa bắt gặp chúng trước đây, một bảng được phân đoạn giống như một số bảng có cấu trúc giống hệt nhau UNIONkhi ẩn và Oracle sẽ tự động lưu một hàng trong phân vùng thích hợp khi chèn nó dựa trên các tiêu chí phân vùng. Bạn đề cập đến các chỉ mục - cũng mỗi phân vùng cũng có các chỉ mục được phân vùng riêng. Đây là một hoạt động rất rẻ trong Oracle để loại bỏ một phân vùng (nó tương tự như mộtTRUNCATEvề mặt tải vì đó là những gì bạn đang thực sự làm - cắt hoặc bỏ một trong các bảng phụ vô hình này). Đây sẽ là một lượng đáng kể để xử lý để phân vùng "sau thực tế", nhưng không có cảm giác khóc vì sữa bị đổ - những lợi thế để làm cho đến nay vượt xa chi phí. Mỗi tháng bạn sẽ phân chia phân vùng trên cùng để tạo phân vùng mới cho dữ liệu của tháng tiếp theo (bạn có thể dễ dàng tự động hóa ths với a DBMS_JOB).

Và với các phân vùng, bạn cũng có thể khai thác truy vấn song songloại bỏ phân vùng , điều này sẽ khiến người dùng của bạn rất hài lòng ...


FWIW chúng tôi sử dụng kỹ thuật này tại trang web của tôi trên cơ sở dữ liệu 30Tb +
Gaius

Vấn đề với phân vùng là không có cách cắt rõ ràng để phân vùng dữ liệu. Trong một trong hai bảng (không phải bảng được hiển thị bên dưới), các tiêu chí được sử dụng để thực hiện thanh lọc dựa trên hai trường ngày khác nhau (và khác biệt) và trường trạng thái. Ví dụ: nếu trạng thái là Asau đó nếu DateAlớn hơn 3 năm, nó sẽ bị xóa. Nếu Tình trạng là BDateBcũ hơn 10 năm, nó sẽ bị thanh trừng. Nếu sự hiểu biết của tôi về phân vùng là chính xác, thì việc phân vùng sẽ không hữu ích trong tình huống như thế này (ít nhất là về việc thanh trừng có liên quan).
Mã hóa Gorilla

Bạn có thể phân vùng theo trạng thái và phân vùng theo phạm vi ngày. Nhưng nếu trạng thái (hoặc ngày) thay đổi, nó thực sự xóa một phân vùng phụ và chèn vào phân vùng khác. Nói tóm lại, bạn có thể nhận được một cú đánh vào các quy trình hàng ngày của mình để tiết kiệm thời gian cho việc thanh trừng.
Gary

6
Ngoài ra, bạn có thể tạo một cột ảo hiển thị DateA khi trạng thái là A và DateB khi trạng thái là B và sau đó phân vùng trên cột ảo. Việc di chuyển phân vùng tương tự sẽ xảy ra, nhưng nó sẽ giúp bạn thanh trừng. Có vẻ như điều này đã được đăng như một câu trả lời.
Leigh Riffel

4

Một khía cạnh cần xem xét là bao nhiêu kết quả thực hiện xóa từ các chỉ mục và bao nhiêu từ bảng thô. Mỗi bản ghi bị xóa khỏi bảng yêu cầu xóa cùng một hàng từ mọi chỉ mục btree. Nếu bạn đã có hơn 30 chỉ số btree, tôi nghi ngờ phần lớn thời gian của bạn dành cho việc bảo trì chỉ mục.

Điều này có tác động đến tính hữu ích của phân vùng. Giả sử bạn có một chỉ mục về tên. Một chỉ số Btree tiêu chuẩn, tất cả trong một phân đoạn, có thể phải thực hiện bốn lần nhảy để chuyển từ khối gốc sang khối lá và lần đọc thứ năm để có được hàng. Nếu chỉ mục đó được phân vùng thành 50 phân đoạn và bạn không có khóa phân vùng như một phần của truy vấn, thì mỗi phân đoạn trong số 50 phân đoạn đó sẽ cần được kiểm tra. Mỗi phân đoạn sẽ nhỏ hơn, do đó bạn có thể chỉ phải thực hiện 2 lần nhảy nhưng cuối cùng bạn vẫn có thể thực hiện 100 lần đọc thay vì 5 lần trước.

Nếu chúng là các chỉ mục bitmap, các phương trình là khác nhau. Bạn có thể không sử dụng chỉ mục để xác định các hàng riêng lẻ mà thay vào đó là các bộ. Vì vậy, thay vì truy vấn sử dụng 5 IO để trả về một bản ghi, nó đã sử dụng 10.000 IO. Vì vậy, chi phí phụ trong các phân vùng bổ sung cho chỉ mục sẽ không thành vấn đề.


2

xóa 50 triệu bản ghi mỗi tháng trong lô 50.000 chỉ là 1000 lần lặp. nếu bạn xóa 1 cứ sau 30 phút thì nó sẽ đáp ứng yêu cầu của bạn. một tác vụ theo lịch trình để chạy truy vấn bạn đã đăng nhưng xóa vòng lặp để nó chỉ thực hiện một lần nên không gây ra sự xuống cấp đáng chú ý cho người dùng. Chúng tôi làm về cùng một khối lượng hồ sơ trong nhà máy sản xuất của chúng tôi chạy khá nhiều 24/7 và nó đáp ứng nhu cầu của chúng tôi. Chúng tôi thực sự trải rộng ra hơn 10.000 bản ghi cứ sau 10 phút, thực thi trong khoảng 1 hoặc 2 giây chạy trên các máy chủ Oracle unix của chúng tôi.


Điều gì về 'hoàn tác' và 'làm lại' 'xóa' sẽ tạo ra? Nó cũng bóp nghẹt IO ... cách tiếp cận dựa trên 'xóa' chắc chắn là KHÔNG .. KHÔNG cho các bảng lớn.
pahariayogi

1

Nếu dung lượng ổ đĩa không ở mức cao, bạn có thể tạo bản sao "công việc" của bảng my_table_new, bằng cách sử dụng CTAS (Tạo bảng dưới dạng chọn) với các tiêu chí sẽ bỏ qua các bản ghi bị loại bỏ. Bạn có thể thực hiện song song câu lệnh tạo và với gợi ý chắp thêm để làm cho nó nhanh, và sau đó xây dựng tất cả các chỉ mục của bạn. Sau đó, khi nó kết thúc, (và đã kiểm tra), đổi tên bảng hiện có thành my_table_oldvà đổi tên bảng "công việc" thành my_table. Một khi bạn cảm thấy thoải mái với mọi thứ drop my_table_old purgeđể thoát khỏi cái bàn cũ. Nếu có một loạt các hạn chế khóa ngoại, hãy xem dbms_redefinition gói PL / SQL . Nó sẽ sao chép các chỉ mục, chống chỉ định của bạn, vv khi sử dụng các tùy chọn thích hợp. Đây là tổng kết một gợi ý của Tom Kyte của AskTomdanh tiếng. Sau lần chạy đầu tiên, bạn có thể tự động hóa mọi thứ và bảng tạo sẽ nhanh hơn rất nhiều và có thể được thực hiện trong khi hệ thống hoạt động và thời gian ngừng ứng dụng sẽ bị giới hạn trong vòng chưa đến một phút để thực hiện đổi tên bảng. Sử dụng CTAS sẽ nhanh hơn nhiều so với thực hiện xóa hàng loạt. Cách tiếp cận này có thể đặc biệt hữu ích nếu bạn không có phân vùng được cấp phép.

CTAS mẫu, giữ hàng với dữ liệu trong 365 ngày qua và 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
Điều này có thể được xem xét nếu (a) thanh trừng là một nhiệm vụ một lần. (b) nếu bạn giữ lại ít hàng hơn và hầu hết dữ liệu cần xóa ...
pahariayogi

0

Khi bỏ phân vùng, bạn để các chỉ mục toàn cầu không sử dụng được, cần phải xây dựng lại, việc xây dựng lại các chỉ mục toàn cầu sẽ là một vấn đề lớn, vì nếu bạn thực hiện trực tuyến, nó sẽ khá chậm, nếu không bạn cần thời gian chết. trong cả hai trường hợp, không thể phù hợp với yêu cầu.

"Chúng tôi thường kết thúc thanh trừng từ 10 đến 50 triệu hàng mỗi tháng"

Tôi khuyên bạn nên sử dụng xóa hàng loạt PL / SQL, vài giờ là ok tôi nghĩ.


1
Nếu bạn có khóa chính, thì việc bỏ phân vùng sẽ không làm cho bất kỳ chỉ mục chung nào không sử dụng được. Nhưng nếu OP có nhiều chỉ số toàn cầu thì sẽ có chi phí cao cho việc bỏ phân vùng. Trong trường hợp lý tưởng khi ai đó đang phân vùng bảng, phân vùng dựa trên khóa chính và họ không cần bất kỳ chỉ mục chung nào. Rằng mọi truy vấn đều có thể tận dụng việc cắt tỉa phân vùng.
Gandolf989

@ Gandolf989 bỏ phân vùng sẽ luôn làm cho chỉ số toàn cầu không thể sử dụng được
phép lạ173
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.