Ова страница је машински преведена. Прочитајте енглески оригинал. English

IBSurgeon библиотека

Негативни утицај индекса на перформансе INSERT, UPDATE и DELETE операција у Firebird SQL-у

Генерално, индекси су неопходни за било коју озбиљну базу података, јер су кључни за убрзавање упита са WHERE клаузулом (SELECT, UPDATE, MERGE, DELETE, итд.). Међутим, сваки индекс има цену, а цена је очигледна када упоредимо брзину INSERT/UPDATE/DELETE операција на индексираним и неиндексираним пољима.

У овом чланку ћемо демонстрирати утицај индекса са неколико јединствених кључева на брзину операција.

Тест база података

Хајде да креирамо једноставну Firebird базу података:

Code
CREATE DATABASE “E:\TESTFIREBIRDINDEX.FDB” USER “SYSDBA” PASSWORD “masterkey”;
CREATE TABLE TABLEIND1 (
    I1            INTEGER NOT NULL PRIMARY KEY,
    NAME          VARCHAR(250),
    MANORWOMAN  SMALLINT
);
CREATE GENERATOR G1;
SET TERM ^ ;

create or alter procedure INS1MLN
returns (
    INSERTED_CNT integer)
as
BEGIN
inserted_cnt = 0;
WHILE (inserted_cnt <1000000) DO
BEGIN
 Insert into tableind1(i1, name, manorwoman) values(gen_id(g1,1), 'TEST name', (:inserted_cnt - (:inserted_cnt/2)*2));
 inserted_cnt=inserted_cnt+1;
END
suspend;
END^
SET TERM ; ^
GRANT INSERT ON TABLEIND1 TO PROCEDURE INS1MLN;
GRANT EXECUTE ON PROCEDURE INS1MLN TO SYSDBA;
COMMIT;

Да бисмо демонстрирали проблем са лошим индексима, извршимо следеће операције:

  • Прво, убацујемо милион записа у табелу
  • Затим ажурирамо те записе
  • Након тога, бришемо све записе
  • И, на крају, покрећемо SELECT count(*) из табеле

Команде испод извршавају описане операције:

Code
set stat on;  /*enable display of statistics in isql*/
select * from ins1mln;
update tableind1 SET MANORWOMAN  = 3;
delete from tableind1;
select count(*) from tableind1;

Покренимо то и сачувајмо резултате за даљу анализу.

Након тога, хајде да креирамо још једну табелу са истом структуром - и додамо индекс за колону MANORWOMAN

Code
CREATE INDEX TABLEIND1_IDX1 ON TABLEIND1 (MANORWOMAN);

Као што видите горе у скрипти, у ову колону убацујемо само 0 или 1 целобројне вредности. Такав индекс је бескористан за SELECT упите, и у теорији, сви програмери база података би требало да избегавају такве лоше индексе (осим у веома посебним случајевима са неуравнотеженом расподелом вредности у табели), али у пракси постоји много индекса са 2 јединствене вредности или чак са 1 од њих.

Затим поновимо скрипту са овим индексом и упоредимо резултате - погледајте следећу табелу:

Без индекса за MANORWOMAN Са индексом за MANORWOMAN
SQL> set stat on; /*show statistics*/
SQL > select * from ins1mln;
INSERTED_CNT
============
1000000
Current memory = 10487216
Delta memory = 80560
Max memory = 12569996
Elapsed time= 13.33 sec
Buffers = 2048
Reads = 0
Writes 18756
Fetches = 7833503
SQL > update tableind1 SET MANORWOMAN = 3;
Current memory = 76551788
Delta memory = 66064572
Max memory = 111442520
Elapsed time= 15.04 sec
Buffers = 2048
Reads = 16166
Writes 15852
Fetches = 6032307
SQL> delete from tableind1;
Current memory = 76550240
Delta memory = -1548
Max memory = 111442520
Elapsed time= 3.27 sec
Buffers = 2048
Reads = 16147
Writes 16006
Fetches = 5032277
SQL> select count(*) from tableind1;
COUNT
============
0
Current memory = 76552064
Delta memory = 1824
Max memory = 111442520
Elapsed time= 1.35 sec
Buffers = 2048
Reads = 16021
Writes 1791
Fetches = 2032278
SQL> set stat on; /*show statistics*/
SQL> select * from ins1mln;
INSERTED_CNT
============
1000000
Current memory = 10484140
Delta memory = 75524
Max memory = 12569996
Elapsed time= 23.94 sec
Buffers = 2048
Reads = 1
Writes 23942
Fetches = 11459599
SQL> update tableind1 SET MANORWOMAN = 3;
Current memory = 76548712
Delta memory = 66064572
Max memory = 111439444
Elapsed time= 29.30 sec
Buffers = 2048
Reads = 16167
Writes 19492
Fetches = 10035948
SQL> delete from tableind1;
Current memory = 76547164
Delta memory = -1548
Max memory = 111439444
Elapsed time= 3.41 sec
Buffers = 2048
Reads = 16147
Writes 15967
Fetches = 5032277
SQL> select count(*) from tableind1;
COUNT
============
0
Current memory = 76548988
Delta memory = 1824
Max memory = 111439444
Elapsed time= 0.69 sec
Buffers = 2048
Reads = 16021
Writes 1901
Fetches = 2032278

Дакле, лош индекс смањује перформансе приближно 2 пута приликом уметања или ажурирања. Такође, можемо видети да неоптималан индекс значајно повећава број уписа и читања записа.

Хајде да добијемо статистику за ову пример базу података (са лошим индексом за MANORWOMAN) и покушамо да пронађемо неке детаље. Да прикупимо статистику, покрећемо следећу команду:

Code
gstat -r e:\testfirebirdindex.fdb > e:\teststat.txt

TABLEIND1 табела и статистика индекса изгледају интригантно, али какве корисне информације нам то даје?

Code
TABLEIND1 (128)
    Primary pointer page: 166, Index root page: 167
    Average record length: 0.00, total records: 1000000
    Average version length: 27.00, total versions: 1000000, max versions: 1
    Data pages: 16130, data page slots: 16130, average fill: 93%
    Fill distribution:
       0 - 19% = 1
      20 - 39% = 0
      40 - 59% = 0
      60 - 79% = 0
      80 - 99% = 16129

    Index RDB$PRIMARY1 (0)
      Depth: 3, leaf buckets: 1463, nodes: 1000000
      Average data length: 1.00, total dup: 0, max dup: 0
      Fill distribution:
           0 - 19% = 0
          20 - 39% = 0
          40 - 59% = 0
          60 - 79% = 1
          80 - 99% = 1462

    Index TABLEIND1_IDX1 (1)
      Depth: 3, leaf buckets: 2873, nodes: 2000000
      Average data length: 0.00, total dup: 1999997, max dup: 999999
      Fill distribution:
           0 - 19% = 0
          20 - 39% = 1
          40 - 59% = 1056
          60 - 79% = 0
          80 - 99% = 1816

Да бисмо разумели значење приказаних бројева и процентуалних вредности, можемо користити HQbird Database Analyst, који нуди визуелну интерпретацију статистике базе података:

Кликом на Reports/View recommendations можемо пронаћи одговарајуће објашњење за овај индекс:

Bad indices count: 1. By `bad` we name indices with many duplicate keys (90% of all keys) and big groups of equal keys (30% of all keys). Big groups of equal keys slowdown garbage collection - MaxEquals here is % of max groups of keys having equal values. Index search for such an index is not efficient. You can drop such indices (if they are not on FK constraints).

Index ( Relation) Duplicates MaxEquals TABLEIND1.IDX1 (TABLEIND1) : 100%, 50%

У производним базама података често можемо видети много лоших индекса, који могу значајно утицати на перформансе базе података. У овом примеру можемо видети табелу са 13 милиона записа која има 7 лоших индекса, који су (највероватније) бескорисни и значајно смањују Firebird перформансе:

Ако HQbird Database Analyst означи индексе као «Useless» (тј. имају само 1 вредност), такве индексе је препоручљиво одмах уклонити, ако је могуће.

Ако је индекс означен као Bad (неколико вредности), такође треба размотрити његово уклањање, али са опрезом: могуће је да се лош индекс користи у неком одређеном SQL упиту, који захтева специфичну комбинацију индекса (укључујући и лош) да би се брзо извршио.

Свакако, ови SQL упити треба да буду преправљени да користе оптималнији план извршења без лошег индекса. Да бисмо пронашли такве упите, треба урадити ревизију SQL планова: најлакши начин је покренути trace сесију са HQbird PerfMon и омогућити евидентирање SQL планова, а затим претражити SQL планове за специфичне лоше индексе.