Tôi có một bảng được phân vùng để ghi nhật ký ứng dụng. Một vài năm trước, tôi phân vùng bảng bằng một phân vùng mỗi tháng. Khi chúng ta gần đến năm 2016, đã đến lúc tôi phải thêm các phân vùng cho năm mới. Bảng được phân vùng, là hai phân vùng cuối cùng của nó, phân vùng cho tháng 12 năm 2015 và một phân vùng sử dụng MAXVALUE. Tôi không bao giờ có kế hoạch có bất kỳ dữ liệu nào trong phân vùng MAXVALUE. Nó chỉ ở đó để làm cho các hoạt động của SPLIT PARTITION dễ dàng hơn.
Trước đây, tôi sẽ thêm các phân vùng bằng các lệnh tương tự như sau:
ALTER TABLE usage_tracking
SPLIT PARTITION usage_tracking_pmax AT (TO_DATE('02/01/2016 00:00:00','MM/DD/YYYY HH24:MI:SS))
INTO (PARTITION usage_tracking_p201601, PARTITION usage_tracking_pmax);
ALTER TABLE usage_tracking
SPLIT PARTITION usage_tracking_pmax AT (TO_DATE('03/01/2016 00:00:00','MM/DD/YYYY HH24:MI:SS'))
INTO (PARTITION usage_tracking_p201602, PARTITION usage_tracking_pmax);
Các câu lệnh SQL ở trên sẽ chia phân vùng MAXVALUE thành hai phân vùng. Có 12 lệnh như vậy, một lệnh cho mỗi tháng.
Năm nay, khi tôi cố gắng chạy kịch bản cho năm 2016 trong môi trường phi sản xuất, tôi đã rất ngạc nhiên khi thấy các lệnh này mất khoảng 30 phút để hoàn thành mỗi lệnh. Trong những năm trước, họ hoàn thành trong vài giây. Hãy nhớ rằng USAGE_TRACKING_PMAX trống nên không cần chuyển dữ liệu vào một phân vùng thích hợp.
Khi phân tích hoạt động trong phiên của tôi thực hiện SPLIT, tôi có thể thấy rõ ràng các sự kiện chờ tệp db được theo dõi đến bảng được phân vùng này. Rõ ràng là hoạt động SPLIT đang đọc phân vùng tối đa, ngay cả khi nó trống.
Những năm trước hoạt động tốt, nhưng cơ sở dữ liệu này gần đây đã được nâng cấp lên Oracle 12c. Tôi đã tìm thấy thông tin về cách thực hiện thao tác chia phân vùng nhanh trong MOS Note 1268714.1 cho biết điều này áp dụng cho Oracle 10.2.0.3 trở lên, nhưng tôi không gặp bất kỳ sự cố nào trong 11.2.0.4. Có lẽ đó chỉ là một sự may mắn ngu ngốc và tôi không có cơ sở dữ liệu 11g để kiểm tra điều này vì tất cả của tôi đã được nâng cấp. Do đó, thay vì tập trung vào những gì đã thay đổi, tôi sẽ chỉ giải quyết vấn đề và tiếp tục ngày của mình.
Theo lưu ý MOS, để thực hiện chia phân vùng nhanh trên phân vùng trống này, tôi cần đảm bảo rằng mình có số liệu thống kê trên phân vùng trống.
Tôi xác nhận rằng NUM_ROWS là 0 cho phân vùng trống này. Vì vậy, tôi không phải tính toán số liệu thống kê trên phân vùng. Thao tác SPLIT PARTITION đầu tiên của tôi rất nhanh, chỉ vài giây. Phân vùng trống và Oracle biết điều đó. Điều làm tôi ngạc nhiên là phân vùng mới, USAGE_TRACKING_P201601 và USAGE_TRACKING_PMAX đã đi đến giá trị NULL để thống kê. Điều này có nghĩa là việc thực hiện thao tác SPLIT PARTITION cho phân vùng mới thứ hai sẽ mất nhiều thời gian. Đây là một ví dụ về những gì tôi muốn nói. Đầu tiên, chúng ta có thể thấy 0 hàng trong phân vùng giá trị tối đa.
SQL> select num_rows from dba_tab_partitions
2 where partition_name='USAGE_TRACKING_PMAX';
NUM_ROWS
----------
0
Bây giờ tôi sẽ chia phân vùng đó.
SQL> ALTER TABLE usage_tracking
2 SPLIT PARTITION usage_tracking_pmax AT ( TO_DATE('02/01/2016 00:00:00','MM/DD/YYYY HH24:MI:SS') )
3 INTO (PARTITION usage_tracking_p201601, PARTITION usage_tracking_pmax);
Table altered.
Elapsed: 00:00:03.13
Bây giờ lưu ý rằng hai phân vùng cuối cùng không có thống kê.
SQL> select num_rows from dba_tab_partitions
2 where partition_name='USAGE_TRACKING_PMAX';
NUM_ROWS
----------
SQL> select num_rows from dba_tab_partitions
2 where partition_name='USAGE_TRACKING_P201601';
NUM_ROWS
----------
Không có số liệu thống kê, phân vùng tách tiếp theo để tạo phân vùng tháng 2 năm 2016 sẽ mất nhiều thời gian.
SQL> ALTER TABLE nau_system.usage_tracking
2 SPLIT PARTITION usage_tracking_pmax AT (TO_DATE('03/01/2016 00:00:00','MM/DD/YYYY HH24:MI:SS'))
3 INTO (PARTITION usage_tracking_p201602, PARTITION usage_tracking_pmax);
Table altered.
Elapsed: 00:27:41.09
Như ghi chú MOS đã nói, chúng ta cần số liệu thống kê trên phân vùng để thực hiện thao tác phân chia nhanh. Giải pháp là tính toán số liệu thống kê trên phân vùng, sau đó sử dụng một lệnh ALTER TABLE để tạo tất cả các phân vùng cùng một lúc.
BEGIN
DBMS_STATS.gather_table_stats (tabname=>'USAGE_TRACKING',
partname => 'USAGE_TRACKING_PMAX',
granularity => 'PARTITION');
END;
/
ALTER TABLE usage_tracking
SPLIT PARTITION usage_tracking_pmax INTO
(PARTITION usage_tracking_p201601 VALUES LESS THAN (TO_DATE('02/01/2016 00:00:00','MM/DD/YYYY HH24:MI:SS')),
PARTITION usage_tracking_p201602 VALUES LESS THAN (TO_DATE('03/01/2016 00:00:00','MM/DD/YYYY HH24:MI:SS')),
PARTITION usage_tracking_p201603 VALUES LESS THAN (TO_DATE('04/01/2016 00:00:00','MM/DD/YYYY HH24:MI:SS')),
PARTITION usage_tracking_p201604 VALUES LESS THAN (TO_DATE('05/01/2016 00:00:00','MM/DD/YYYY HH24:MI:SS')),
PARTITION usage_tracking_p201605 VALUES LESS THAN (TO_DATE('06/01/2016 00:00:00','MM/DD/YYYY HH24:MI:SS')),
PARTITION usage_tracking_p201606 VALUES LESS THAN (TO_DATE('07/01/2016 00:00:00','MM/DD/YYYY HH24:MI:SS')),
PARTITION usage_tracking_p201607 VALUES LESS THAN (TO_DATE('08/01/2016 00:00:00','MM/DD/YYYY HH24:MI:SS')),
PARTITION usage_tracking_p201608 VALUES LESS THAN (TO_DATE('09/01/2016 00:00:00','MM/DD/YYYY HH24:MI:SS')),
PARTITION usage_tracking_p201609 VALUES LESS THAN (TO_DATE('10/01/2016 00:00:00','MM/DD/YYYY HH24:MI:SS') ),
PARTITION usage_tracking_p201610 VALUES LESS THAN (TO_DATE('11/01/2016 00:00:00','MM/DD/YYYY HH24:MI:SS') ),
PARTITION usage_tracking_p201611 VALUES LESS THAN (TO_DATE('12/01/2016 00:00:00','MM/DD/YYYY HH24:MI:SS') ),
PARTITION usage_tracking_p201612 VALUES LESS THAN (TO_DATE('01/01/2017 00:00:00','MM/DD/YYYY HH24:MI:SS') ),
PARTITION usage_tracking_pmax);
Nếu tôi để tập lệnh thực hiện 12 hoạt động SPLIT PARTITION riêng lẻ, thì tôi sẽ cần phải tính toán lại số liệu thống kê trên phân vùng tối đa giữa mỗi cái. Sử dụng một lệnh hiệu quả hơn.