Почему пакетная операция DELETE не уменьшает повторяющиеся / отменяемые UNLIKE INSERT / UPDATE в Oracle - PullRequest
1 голос
/ 06 марта 2020

Я протестировал 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

Спасибо

...