Tóm tắt
Tôi nghĩ rằng việc cập nhật để null là chậm hơn bởi vì Oracle (không chính xác) cố gắng tận dụng các cách thức mà nó lưu trữ null, khiến nó thường xuyên tổ chức lại các hàng trong khối ("khối đống nén "), tạo thêm nhiều UNDO và REDO.
Điều gì đặc biệt về null?
Từ Oracle Database Concepts:
"Nulls được lưu trữ trong cơ sở dữ liệu nếu họ rơi giữa cột với các giá trị dữ liệu Trong những trường hợp họ yêu cầu 1 byte để lưu trữ theo chiều dài của cột (zero)
..
Việc lưu giữ các giá trị null trong một hàng không yêu cầu lưu trữ vì một tiêu đề hàng mới báo hiệu rằng các cột còn lại trong hàng trước là null, ví dụ, nếu ba cột cuối cùng của bảng là rỗng, thì không có thông tin nào được lưu trữ cho các cột đó. với nhiều cột, các cột có nhiều khả năng chứa null hơn nên được xác định cuối cùng để tiết kiệm dung lượng đĩa. "
thử nghiệm
cập nhật Điểm chuẩn là rất khó khăn vì chi phí thực sự của một bản cập nhật không thể đo lường chỉ từ báo cáo cập nhật. Ví dụ: các công tắc nhật ký sẽ không xảy ra với mọi cập nhật và việc xóa khối bị trễ sẽ xảy ra sau đó. Để kiểm tra chính xác bản cập nhật, cần có nhiều lần chạy, các đối tượng sẽ được tạo lại cho mỗi lần chạy và các giá trị cao và thấp phải được loại bỏ.
Để đơn giản, tập lệnh bên dưới không đưa ra kết quả cao và thấp, đồng thời chỉ kiểm tra bảng có một cột. Nhưng vấn đề vẫn xảy ra bất kể số cột, dữ liệu của chúng và cột nào được cập nhật.
Tôi đã sử dụng tiện ích RunStats từ http://www.oracle-developer.net/utilities.php để so sánh mức tiêu thụ tài nguyên của việc cập nhật thành giá trị với cập nhật-thành-một-không.
create table test1(col1 number);
BEGIN
dbms_output.enable(1000000);
runstats_pkg.rs_start;
for i in 1 .. 10 loop
execute immediate 'drop table test1 purge';
execute immediate 'create table test1 (col1 number)';
execute immediate 'insert /*+ append */ into test1 select 1 col1
from dual connect by level <= 100000';
commit;
execute immediate 'update test1 set col1 = 1';
commit;
end loop;
runstats_pkg.rs_pause;
runstats_pkg.rs_resume;
for i in 1 .. 10 loop
execute immediate 'drop table test1 purge';
execute immediate 'create table test1 (col1 number)';
execute immediate 'insert /*+ append */ into test1 select 1 col1
from dual connect by level <= 100000';
commit;
execute immediate 'update test1 set col1 = null';
commit;
end loop;
runstats_pkg.rs_stop();
END;
/
quả
Có hàng tá các khác biệt, đây là những bốn tôi nghĩ là phù hợp nhất:
Type Name Run1 Run2 Diff
----- ---------------------------- ------------ ------------ ------------
TIMER elapsed time (hsecs) 1,269 4,738 3,469
STAT heap block compress 1 2,028 2,027
STAT undo change vector size 55,855,008 181,387,456 125,532,448
STAT redo size 133,260,596 581,641,084 448,380,488
Solutions?
Giải pháp duy nhất tôi có thể nghĩ là bật tính năng nén bảng. Thủ thuật lưu trữ dấu vết null không xảy ra đối với các bảng nén. Vì vậy, mặc dù số lượng "đống khối nén" thậm chí còn cao hơn cho Run2, từ 2028 đến 23208, tôi đoán nó không thực sự làm bất cứ điều gì. Thời gian làm lại, hoàn tác và thời gian trôi qua giữa hai lần chạy gần giống với tính năng nén bảng được bật.
Tuy nhiên, có rất nhiều nhược điểm tiềm ẩn đối với nén bảng. Việc cập nhật lên một null sẽ chạy nhanh hơn nhiều, nhưng mọi cập nhật khác sẽ chạy ít nhất một chút chậm hơn.
Bạn có thể đăng kế hoạch thực hiện/giải thích của bạn? – diagonalbatman
Có mệnh đề 'where' không? – Johan
Nếu tôi đi qua toàn bộ bảng cùng một lúc, không có mệnh đề where. Nếu tôi đi qua hàng của bảng theo hàng, thì có một mệnh đề where tham chiếu đến khóa chính của bảng. Kết quả vẫn giữ nguyên trong cả hai phiên bản. Đối với kế hoạch thực hiện, tôi sẽ chuẩn bị một và một ví dụ từng bước để tái tạo kết quả sau ngày hôm nay. –