Recently an interesting question came up on an Oracle French Forum about a performance problem during an insert/select operation via a dblink. The poster was suffering from the very long time (several hours without any response) the insert was taking. This is why he came to that forum and posted a question which looks like “what do you think if I commit every 1000 rows? Will my insert speed up”? That’s not the right way to follow in the process of troubleshooting an Oracle performance problem. I always remember Cary Millsap mantra “Why guess when you can know”. Fortunately Oracle gives us a lot fo tools and trace events to instrument what going on inside the Oracle database. Here below are the steps of discussion we’ve had (the original poster, others and I) in that forum.
I first have asked the poster to trace his insert select using events 10046
alter session set events '10046 trace name context forever, level 12'; INSERT /*+ append */ INTO my_table ( champs_1, champs_2, champs_3, champs_4, champs_5, champs_6, champs_7 ) SELECT * FROM la_table_cible@mon_dblink ; commit ; alter session set events '10046 trace name context off';
I am not going to stress that I have asked the poster to ensure that his direct path load will not be silently ignored by Oracle in case the table “my_table” has trigger or foreign key integrity constraints implemented on it. I have also asked if there will be subsequent deletes (after the insert) on this table making future direct path load not reusing any freed space made by that delete.
Up to this step, we persuaded the poster that his initial guess of committing every 1000 records was not the ultimate solution; but instead he should focus on reducing the volume of data transferred from the distant data base to the local one. The poster sends us the explain plan and Statistics of the select part when this one is executed in the distant data base
Statistics ---------------------------------------------- 212 recursive calls 3 db block gets 678993 consistent gets 483744 physical reads 592 redo size 124692665 bytes sent via SQL*Net TO client 1391305 bytes received via SQL*Net FROM client 126437 SQL*Net roundtrips TO/FROM client 17 sorts (memory) 0 sorts (disk) 1896535 rows processed
He is selecting 1,896,535 records via 126,437 SQL*Net roundtrips meaning that he is using the default arraysize which is 15 (1,896,535/126,437). For this, he did 0.358 (678,993/1,896,535) logical read for each selected row which seems to be quite acceptable. But, he is sending a very large amount of data 124MB over a data base link and making a hundred of thousands SQL*Net roundtrips. Note, that he is doing also a fairly large amount of physical read (483,744) when compared to the total number of logical reads (678,993).
I’ve then asked the poster to stop selecting all the rows from the distant data base and to start selecting only necessary rows. I asked also to post the explain plan of this query in attempt to help him tuning the select part of his problematic insert. When he said that he can’t intervene into the distant data base, I have then asked him to manage his insert with a PL/SQL stored procedure as shown below:
CREATE type distant_typ AS object (champs_1 number,champs_2 number,champs_3 number, ... champs_7 date); 2 / CREATE type distant_typ_tab AS TABLE of distant_typ; 2 / DECLARE CURSOR get_data_from_db IS SELECT champs_1, champs_2, champs_3, champs_4, champs_5, champs_6, champs_7 FROM la_table_cible@mon_dblink ; BEGIN OPEN get_data_from_db; LOOP FETCH get_data_from_db BULK collect INTO distant_typ_tab LIMIT 100; FOR i in 1..distant_typ_tab.COUNT LOOP INSERT INTO INSERT /*+ append */ INTO ma_table (champs_1, champs_2, champs_3, champs_4, champs_5, champs_6, champs_7 ) SELECT champs_1, champs_2, champs_3, champs_4, champs_5, champs_6, champs_7 FROM table (distant_typ_tab); EXIXT when get_data_from_db%notfound; END LOOP; END LOOP; COMMIT; END;
And magically the poster comes out and said “fantastic my insert/select now is completing in a very acceptable time”.