Kết nối băm rõ ràng là hoạt động tốt nhất khi mọi thứ có thể vừa với bộ nhớ. Nhưng điều đó không có nghĩa là chúng không phải là phương pháp kết nối tốt nhất khi bảng không thể vừa với bộ nhớ. Tôi nghĩ rằng phương pháp tham gia thực tế khác chỉ là một kết hợp sắp xếp hợp nhất.
Nếu bảng băm không thể vừa với bộ nhớ, việc sắp xếp bảng cho phép sắp xếp sắp xếp hợp nhất cũng không thể vừa với bộ nhớ. Và hợp nhất tham gia cần phải sắp xếp cả hai bảng. Theo kinh nghiệm của tôi, băm là luôn luôn nhanh hơn so với phân loại, để tham gia và cho nhóm.
Nhưng có một số ngoại lệ. Từ số Oracle® Database Performance Tuning Guide, The Query Optimizer:
Tham gia băm thường hoạt động tốt hơn kết hợp hợp nhất sắp xếp. Tuy nhiên, loại hợp nhất tham gia có thể thực hiện tốt hơn so với băm tham gia nếu cả hai điều kiện sau đây tồn tại:
The row sources are sorted already.
A sort operation does not have to be done.
thử nghiệm
Thay vì tạo ra hàng trăm triệu hàng, đó là dễ dàng hơn để buộc Oracle chỉ sử dụng một lượng bộ nhớ rất nhỏ.
Biểu đồ này cho thấy rằng băm tham gia làm tốt hơn kết hợp tham gia, ngay cả khi các bảng là quá lớn để phù hợp (giới hạn nhân tạo) bộ nhớ:
Ghi chú
Đối điều chỉnh hiệu suất thường tốt hơn khi sử dụng byte so với số hàng. Nhưng kích thước "thực" của bảng là một điều khó đo lường, đó là lý do tại sao biểu đồ hiển thị các hàng. Các kích thước này xấp xỉ từ 0.375 MB đến 14 MB. Để kiểm tra lại xem các truy vấn này có thực sự ghi vào đĩa hay không, bạn có thể chạy chúng bằng/* + gather_plan_statistics */và sau đó truy vấn v $ sql_plan_statistics_all.
Tôi chỉ thử nghiệm các phép nối băm và kết hợp sắp xếp hợp nhất. Tôi đã không kiểm tra đầy đủ các vòng lặp lồng nhau vì phương thức kết nối đó luôn cực kỳ chậm với số lượng lớn dữ liệu. Như một kiểm tra sanity, tôi đã so sánh nó một lần với kích thước dữ liệu cuối cùng, và phải mất ít nhất vài phút trước khi tôi giết nó.
Tôi cũng đã thử nghiệm với các dữ liệu _area_sizes khác nhau, thứ tự và dữ liệu không theo thứ tự, và sự khác biệt khác nhau của cột nối (nhiều kết quả khớp hơn CPU, ít khớp hơn IO) và có kết quả tương đối giống nhau.
Tuy nhiên, kết quả khác nhau khi lượng bộ nhớ nhỏ một cách lố bịch. Với chỉ 32K sắp xếp | hash_area_size, kết hợp sắp xếp sắp xếp nhanh hơn đáng kể. Nhưng nếu bạn có quá ít bộ nhớ, bạn có thể có nhiều vấn đề đáng lo ngại hơn.
Vẫn còn nhiều biến khác cần xem xét, chẳng hạn như song song, phần cứng, bộ lọc hoa, v.v. Mọi người có thể đã viết sách về chủ đề này, tôi chưa thử nghiệm một phần nhỏ các khả năng. Nhưng hy vọng điều này là đủ để xác nhận sự đồng thuận chung rằng việc kết hợp băm là tốt nhất cho dữ liệu lớn.
Mã
Dưới đây là những kịch bản tôi đã sử dụng:
--Drop objects if they already exist
drop table test_10k_rows purge;
drop table test1 purge;
drop table test2 purge;
--Create a small table to hold rows to be added.
--("connect by" would run out of memory later when _area_sizes are small.)
--VARIABLE: More or less distinct values can change results. Changing
--"level" to something like "mod(level,100)" will result in more joins, which
--seems to favor hash joins even more.
create table test_10k_rows(a number, b number, c number, d number, e number);
insert /*+ append */ into test_10k_rows
select level a, 12345 b, 12345 c, 12345 d, 12345 e
from dual connect by level <= 10000;
commit;
--Restrict memory size to simulate running out of memory.
alter session set workarea_size_policy=manual;
--1 MB for hashing and sorting
--VARIABLE: Changing this may change the results. Setting it very low,
--such as 32K, will make merge sort joins faster.
alter session set hash_area_size = 1048576;
alter session set sort_area_size = 1048576;
--Tables to be joined
create table test1(a number, b number, c number, d number, e number);
create table test2(a number, b number, c number, d number, e number);
--Type to hold results
create or replace type number_table is table of number;
set serveroutput on;
--
--Compare hash and merge joins for different data sizes.
--
declare
v_hash_seconds number_table := number_table();
v_average_hash_seconds number;
v_merge_seconds number_table := number_table();
v_average_merge_seconds number;
v_size_in_mb number;
v_rows number;
v_begin_time number;
v_throwaway number;
--Increase the size of the table this many times
c_number_of_steps number := 40;
--Join the tables this many times
c_number_of_tests number := 5;
begin
--Clear existing data
execute immediate 'truncate table test1';
execute immediate 'truncate table test2';
--Print headings. Use tabs for easy import into spreadsheet.
dbms_output.put_line('Rows'||chr(9)||'Size in MB'
||chr(9)||'Hash'||chr(9)||'Merge');
--Run the test for many different steps
for i in 1 .. c_number_of_steps loop
v_hash_seconds.delete;
v_merge_seconds.delete;
--Add about 0.375 MB of data (roughly - depends on lots of factors)
--The order by will store the data randomly.
insert /*+ append */ into test1
select * from test_10k_rows order by dbms_random.value;
insert /*+ append */ into test2
select * from test_10k_rows order by dbms_random.value;
commit;
--Get the new size
--(Sizes may not increment uniformly)
select bytes/1024/1024 into v_size_in_mb
from user_segments where segment_name = 'TEST1';
--Get the rows. (select from both tables so they are equally cached)
select count(*) into v_rows from test1;
select count(*) into v_rows from test2;
--Perform the joins several times
for i in 1 .. c_number_of_tests loop
--Hash join
v_begin_time := dbms_utility.get_time;
select /*+ use_hash(test1 test2) */ count(*) into v_throwaway
from test1 join test2 on test1.a = test2.a;
v_hash_seconds.extend;
v_hash_seconds(i) := (dbms_utility.get_time - v_begin_time)/100;
--Merge join
v_begin_time := dbms_utility.get_time;
select /*+ use_merge(test1 test2) */ count(*) into v_throwaway
from test1 join test2 on test1.a = test2.a;
v_merge_seconds.extend;
v_merge_seconds(i) := (dbms_utility.get_time - v_begin_time)/100;
end loop;
--Get average times. Throw out first and last result.
select (sum(column_value) - max(column_value) - min(column_value))
/(count(*) - 2)
into v_average_hash_seconds
from table(v_hash_seconds);
select (sum(column_value) - max(column_value) - min(column_value))
/(count(*) - 2)
into v_average_merge_seconds
from table(v_merge_seconds);
--Display size and times
dbms_output.put_line(v_rows||chr(9)||v_size_in_mb||chr(9)
||v_average_hash_seconds||chr(9)||v_average_merge_seconds);
end loop;
end;
/
Liệu kế hoạch giải thích hiển thị một chỉ số dao động quét?Bạn là chính xác, một bảng đầy đủ quét với hash tham gia sẽ tiêu thụ tải của bộ nhớ. Xin vui lòng gửi đầu ra kế hoạch giải thích đầy đủ –
là các số liệu thống kê cho bảng cập nhật? –