Я протестировал INSERT / DELETE / UPDATE 10000 строк в таблице (с индексами), чья средняя длина строки составляет 230 байтов в Oracle 12.2, и строка за строкой, и все строки одновременно. Я видел ожидаемые результаты для массового INSERT, генерирующего в 5-6 раз меньше повторов и касающегося в 6 раз меньшего количества блоков дБ, что привело к значительному улучшению производительности в 10-25 раз, но тест UPDATE / DELETE оставил меня озадаченным. Я не вижу никакой разницы там. Ни в количестве повторов, ни в скорости. У кого-нибудь есть объяснение, почему UPDATE / DELETE ведет себя так? Может случиться, что все 10000 строк находятся в 10000 блоках, однако обычно это не так. Кроме того, в этом тесте UPDATE работал только с несколькими блоками, поскольку он изменял только что вставленные строки. То же самое с массовой DELETE.
set serverout on
set timing on
spool redo_test.log
drop table redo_test;
create table redo_test(cn1 number primary key, cn2 number, cd3 date, cv4 varchar2(128));
-- ===================== INSERT ==================================
SELECT
sn.NAME,
mst.value
FROM
v$mystat mst,
V$STATNAME sn
WHERE
mst.STATISTIC# = sn.STATISTIC#
AND sn.name IN (
'db block changes',
'redo size',
'undo change vector size'
);
PROMPT ==== INSERT ROW BY ROW
-- slow by slow
BEGIN
FOR i IN 1..10000 LOOP
INSERT INTO redo_test VALUES (
i,
i,
TO_DATE('06-MAR-2020 06:00:00', 'DD-MON-YYYY HH24:MI:SS') + i / 24,
'abracadabraabracadabraabracadabraabracadabraabracadabra' || TO_CHAR(i)
);
END LOOP;
END;
/
SELECT
sn.NAME,
mst.value
FROM
v$mystat mst,
V$STATNAME sn
WHERE
mst.STATISTIC# = sn.STATISTIC#
AND sn.name IN (
'db block changes',
'redo size',
'undo change vector size'
);
ROLLBACK;
-- Interesting to see almost the same amount of redo.
-- Undo vector remains the same, which is understandable.
SELECT
sn.NAME,
mst.value
FROM
v$mystat mst,
V$STATNAME sn
WHERE
mst.STATISTIC# = sn.STATISTIC#
AND sn.name IN (
'db block changes',
'redo size',
'undo change vector size'
);
PROMPT ==== Batch Insert
-- Batch all together
DECLARE
lt_number sys.dbms_utility.number_array;
TYPE tt_date IS
TABLE OF DATE INDEX BY BINARY_INTEGER;
lt_date tt_date;
lt_varchar sys.dbms_utility.dblink_array;
BEGIN
FOR i IN 1..10000 LOOP
lt_number(i) := i;
lt_date(i) := TO_DATE('06-MAR-2020 06:00:00', 'DD-MON-YYYY HH24:MI:SS') + i / 24;
lt_varchar(i) := 'abracadabraabracadabraabracadabraabracadabraabracadabra' || TO_CHAR(i);
END LOOP;
FORALL i IN 1..10000
INSERT INTO redo_test VALUES (
lt_number(i),
lt_number(i),
lt_date(i),
lt_varchar(i)
);
END;
/
SELECT
sn.NAME,
mst.value
FROM
v$mystat mst,
V$STATNAME sn
WHERE
mst.STATISTIC# = sn.STATISTIC#
AND sn.name IN (
'db block changes',
'redo size',
'undo change vector size'
);
rollback;
SELECT
sn.NAME,
mst.value
FROM
v$mystat mst,
V$STATNAME sn
WHERE
mst.STATISTIC# = sn.STATISTIC#
AND sn.name IN (
'db block changes',
'redo size',
'undo change vector size'
);
PROMPT ==== Batch Insert Table of records
-- same batch insert, but with table of records this time
-- Curious to see if there is going to be any difference in redo/undo
DECLARE
TYPE tt_rec IS
TABLE OF redo_test%rowtype INDEX BY BINARY_INTEGER;
lt_rec tt_rec;
BEGIN
FOR i IN 1..10000
LOOP
lt_rec(i).cn1 := i;
lt_rec(i).cn2 := i;
lt_rec(i).cd3 := TO_DATE('06-MAR-2020 06:00:00', 'DD-MON-YYYY HH24:MI:SS') + i / 24;
lt_rec(i).cv4 := 'abracadabraabracadabraabracadabraabracadabraabracadabra' || TO_CHAR(i);
END LOOP;
FORALL i IN lt_rec.first..lt_rec.last
INSERT INTO redo_test VALUES lt_rec(i);
END;
/
SELECT
sn.NAME,
mst.value
FROM
v$mystat mst,
V$STATNAME sn
WHERE
mst.STATISTIC# = sn.STATISTIC#
AND sn.name IN (
'db block changes',
'redo size',
'undo change vector size'
);
COMMIT;
-- ===================== UPDATE ==================================
-- Yes, the test may be skewed a bit
-- because it deals with only available blocks
-- whereas in real life all 10000 rows just happen to be in 10000 different blocks
-- Yet, the ability to batch update exists.
PROMPT ==== UPDATE ROW BY ROW
-- Update Slow by Slow
BEGIN
FOR i IN 1..10000
LOOP
UPDATE BIDBOX.redo_test
SET
cn2 = i,
cd3 = TO_DATE('06-MAR-2020 06:00:00', 'DD-MON-YYYY HH24:MI:SS') + i / 24,
cv4 = cv4
WHERE
cn1 = i;
END LOOP;
END;
/
SELECT
sn.NAME,
mst.value
FROM
v$mystat mst,
V$STATNAME sn
WHERE
mst.STATISTIC# = sn.STATISTIC#
AND sn.name IN (
'db block changes',
'redo size',
'undo change vector size'
);
rollback;
SELECT
sn.NAME,
mst.value
FROM
v$mystat mst,
V$STATNAME sn
WHERE
mst.STATISTIC# = sn.STATISTIC#
AND sn.name IN (
'db block changes',
'redo size',
'undo change vector size'
);
PROMPT ==== BATCH UPDATE
-- Batch Update
DECLARE
TYPE tt_rec IS
TABLE OF redo_test%rowtype INDEX BY BINARY_INTEGER;
lt_rec tt_rec;
BEGIN
SELECT *
BULK COLLECT INTO lt_rec
FROM redo_test;
sys.dbms_output.put_line('lt_rec.count'||lt_rec.count);
FORALL i IN lt_rec.first..lt_rec.last
UPDATE redo_test SET ROW = lt_rec(i)
WHERE cn1 = lt_rec(i).cn1;
END;
/
SELECT
sn.NAME,
mst.value
FROM
v$mystat mst,
V$STATNAME sn
WHERE
mst.STATISTIC# = sn.STATISTIC#
AND sn.name IN (
'db block changes',
'redo size',
'undo change vector size'
);
rollback;
SELECT
sn.NAME,
mst.value
FROM
v$mystat mst,
V$STATNAME sn
WHERE
mst.STATISTIC# = sn.STATISTIC#
AND sn.name IN (
'db block changes',
'redo size',
'undo change vector size'
);
-- ===================== DELETE ==================================
PROMPT ==== DELETE Row by Row
-- DELETE Row by Row
BEGIN
FOR i IN 1..10000
LOOP
DELETE BIDBOX.redo_test
WHERE cn1 = i;
END LOOP;
END;
/
SELECT
sn.NAME,
mst.value
FROM
v$mystat mst,
V$STATNAME sn
WHERE
mst.STATISTIC# = sn.STATISTIC#
AND sn.name IN (
'db block changes',
'redo size',
'undo change vector size'
);
rollback;
SELECT
sn.NAME,
mst.value
FROM
v$mystat mst,
V$STATNAME sn
WHERE
mst.STATISTIC# = sn.STATISTIC#
AND sn.name IN (
'db block changes',
'redo size',
'undo change vector size'
);
PROMPT ==== Batch Delete
DECLARE
TYPE tt_rec IS
TABLE OF redo_test%rowtype INDEX BY BINARY_INTEGER;
lt_rec tt_rec;
BEGIN
SELECT *
BULK COLLECT INTO lt_rec
FROM redo_test;
sys.dbms_output.put_line('lt_rec.count'||lt_rec.count);
FORALL i IN lt_rec.first..lt_rec.last
DELETE redo_test
WHERE cn1 = lt_rec(i).cn1;
END;
/
SELECT
sn.NAME,
mst.value
FROM
v$mystat mst,
V$STATNAME sn
WHERE
mst.STATISTIC# = sn.STATISTIC#
AND sn.name IN (
'db block changes',
'redo size',
'undo change vector size'
);
rollback;
SELECT
sn.NAME,
mst.value
FROM
v$mystat mst,
V$STATNAME sn
WHERE
mst.STATISTIC# = sn.STATISTIC#
AND sn.name IN (
'db block changes',
'redo size',
'undo change vector size'
);
spool off
Спасибо