PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Uwagi dotyczące indeksów PostgreSQL B-Tree

PostgreSQL zawiera co najmniej 6 różnych typów indeksów, z których B-Treeindex jest najczęściej używany. Czytaj dalej, aby dowiedzieć się więcej o B-Treeindexes w PostgreSQL.

Typy indeksów

Indeksy w PostgreSQL, takie jak te utworzone dla PRIMARY KEY i UNIQUE w instrukcji aCREATE TABLE lub utworzone jawnie za pomocą instrukcji CREATE INDEX, są określonego „typu” (chociaż technicznie powinniśmy nazywać je „metodami dostępu do indeksu”).

PostgreSQL zawiera następujące wbudowane typy indeksów:

  • B-drzewo
  • Hasz
  • GIN – uogólniony indeks odwrócony
  • BRIN – Indeks zakresu bloków (tylko w wersji 9.5 i nowszych)
  • GiST – uogólnione drzewo wyszukiwania odwróconego
  • SP-GiST – GiST z partycjami przestrzeni

B-Tree jest domyślnym i najczęściej używanym typem indeksu. Określenie klucza podstawowego lub unikatowego w instrukcji CREATE TABLE powoduje, że PostgreSQL tworzy indeksy B-Tree. Instrukcje CREATE INDEX bez klauzuli USING będą również tworzyć indeksy B-Tree:

-- the default index type is btree
CREATE INDEX ix_year ON movies (year);

-- equivalent, explicitly lists the index type
CREATE INDEX ix_year ON movies USING btree (year);

Zamawianie

Indeksy B-Tree są z natury uporządkowane. PostgreSQL może wykorzystać tę kolejność zamiast sortowania według zindeksowanego wyrażenia. Na przykład posortowanie tytułów wszystkich filmów z lat 80. według tytułu wymagałoby sortowania:

idxdemo=# explain select title from movies where year between 1980 and 1989 order by title asc;
                                    QUERY PLAN
----------------------------------------------------------------------------------
 Sort  (cost=240.79..245.93 rows=2056 width=17)
   Sort Key: title
   ->  Index Scan using ix_year on movies  (cost=0.29..127.65 rows=2056 width=17)
         Index Cond: ((year >= 1980) AND (year <= 1989))
(4 rows)

Ale jeśli sortujesz je według indeksowanej kolumny (roku), dodatkowe sortowanie nie jest wymagane.

idxdemo=# explain select title from movies where year between 1980 and 1989 order by year asc;
                                 QUERY PLAN
----------------------------------------------------------------------------
 Index Scan using ix_year on movies  (cost=0.29..127.65 rows=2056 width=21)
   Index Cond: ((year >= 1980) AND (year <= 1989))
(2 rows)

Współczynnik wypełnienia

W przypadku tabel, które nie będą aktualizowane, możesz zwiększyć „współczynnik wypełnienia” z domyślnego 90, co powinno dać ci nieco mniejsze i szybsze indeksy. I odwrotnie, jeśli zdarzają się częste aktualizacje tabeli zawierającej indeksowany parametr, możesz zmniejszyć współczynnik wypełnienia do mniejszej liczby – pozwoli to na szybsze wstawianie i aktualizowanie, kosztem nieco większych indeksów.

CREATE INDEX ix_smd ON silent_movies (director) WITH (fillfactor = 100);

Indeksowanie tekstu

Indeksy B-Tree mogą pomóc w dopasowaniu przedrostków tekstu. Zróbmy zapytanie, aby wyświetlić wszystkie filmy zaczynające się na literę „T”:

idxdemo=> explain select title from movies where title like 'T%';
                         QUERY PLAN
-------------------------------------------------------------
 Seq Scan on movies  (cost=0.00..1106.94 rows=8405 width=17)
   Filter: (title ~~ 'T%'::text)
(2 rows)

Ten plan wymaga pełnego sekwencyjnego skanowania tabeli. Co się stanie, jeśli dodamy indeks B-Tree do movies.title?

idxdemo=> create index ix_title on movies (title);
CREATE INDEX

idxdemo=> explain select title from movies where title like 'T%';
                         QUERY PLAN
-------------------------------------------------------------
 Seq Scan on movies  (cost=0.00..1106.94 rows=8405 width=17)
   Filter: (title ~~ 'T%'::text)
(2 rows)

Cóż, to wcale nie pomogło. Istnieje jednak forma magicznego pyłu pixie, który możemy posypać, aby Postgres zrobił to, czego chcemy:

idxdemo=> create index ix_title2 on movies (title text_pattern_ops);
CREATE INDEX

idxdemo=> explain select title from movies where title like 'T%';
                                 QUERY PLAN
-----------------------------------------------------------------------------
 Bitmap Heap Scan on movies  (cost=236.08..1085.19 rows=8405 width=17)
   Filter: (title ~~ 'T%'::text)
   ->  Bitmap Index Scan on ix_title2  (cost=0.00..233.98 rows=8169 width=0)
         Index Cond: ((title ~>=~ 'T'::text) AND (title ~<~ 'U'::text))
(4 rows)

Plan używa teraz indeksu, a koszt został zmniejszony. Magią jest tutaj „text_pattern_ops”, która pozwala na użycie indeksu B-Tree nad wyrażeniem „tekstowym” dla operatorów wzorców (LIKE i wyrażeń regularnych). „text_pattern_ops” nazywa się OperatorClass.

Pamiętaj, że będzie to działać tylko w przypadku wzorców ze stałym prefiksem tekstowym, więc „%Angry%” lub „%Men” nie będą działać. Skorzystaj z wyszukiwania pełnotekstowego PostgreSQL dla zaawansowanych zapytań tekstowych.

Indeksy pokrycia

Indeksy pokrywające zostały dodane do PostgreSQL w v11. Indeksy pokrywające umożliwiają uwzględnienie wartości jednego lub więcej wyrażeń wraz z indeksowanym wyrażeniem wewnątrz indeksu.

Spróbujmy wyszukać wszystkie tytuły filmów uporządkowane według roku wydania:

idxdemo=# explain select title from movies order by year asc;
                             QUERY PLAN
--------------------------------------------------------------------
 Sort  (cost=3167.73..3239.72 rows=28795 width=21)
   Sort Key: year
   ->  Seq Scan on movies  (cost=0.00..1034.95 rows=28795 width=21)
(3 rows)

Obejmuje to pełne sekwencyjne skanowanie tabeli, po którym następuje rodzaj rzutowanych kolumn. Najpierw dodajmy zwykły indeks dotyczący movies.year:

idxdemo=# create index ix_year on movies (year);
CREATE INDEX

idxdemo=# explain select title from movies order by year asc;
                                  QUERY PLAN
------------------------------------------------------------------------------
 Index Scan using ix_year on movies  (cost=0.29..1510.22 rows=28795 width=21)
(1 row)

Teraz Postgres postanawia użyć indeksu do wyciągania wpisów z tabeli bezpośrednio w żądanej kolejności. Należy sprawdzić tabelę, ponieważ indeks zawiera tylko wartość „rok” i odniesienie do krotki w tabeli.

Jeśli uwzględnimy wartość „title” również wewnątrz indeksu, można całkowicie uniknąć wyszukiwania tabeli. Użyjmy nowej składni do stworzenia takiego indeksu:

idxdemo=# create index ix_year_cov on movies (year) include (title);
CREATE INDEX
Time: 92.618 ms

idxdemo=# drop index ix_year;
DROP INDEX

idxdemo=# explain select title from movies order by year asc;
                                      QUERY PLAN
---------------------------------------------------------------------------------------
 Index Only Scan using ix_year_cov on movies  (cost=0.29..2751.59 rows=28795 width=21)
(1 row)

Postgres używa teraz funkcji Index OnlyScan, co oznacza, że ​​całkowicie unika się przeglądania tabel. Pamiętaj, że musieliśmy usunąć stary indeks, ponieważ Postgres nie wybrał dla tego zapytania ix_year_cov zamiast ix_year.

Klastrowanie

PostgreSQL niesławnie nie obsługuje automatycznego fizycznego porządkowania wierszy w tabeli, w przeciwieństwie do „indeksów klastrowych” w innych RDBMS. Jeśli większość zapytań ma pobierać większość wierszy w większości statycznej tabeli w ustalonej kolejności, dobrym pomysłem byłoby ułożenie fizycznej pamięci tabeli w tej kolejności i użycie sekwencyjnego skanowania. Aby fizycznie zmienić kolejność tabeli w kolejności określonej przez indeks, użyj:

CLUSTER VERBOSE movies USING ix_year;

Zazwyczaj do ponownego klastrowania tabeli używa się indeksu B-Tree, ponieważ zapewnia on kompletną kolejność wszystkich wierszy w tabeli.

Statystyki indeksu

Ile miejsca na dysku zajmuje Twój indeks? Funkcja pg_relation_size może odpowiedzieć, że:

idxdemo=# select * from pg_relation_size('ix_year');
 pg_relation_size
------------------
           663552
(1 row)

Zwraca miejsce na dysku używane przez indeks w bajtach.

Więcej informacji o indeksie można uzyskać za pomocą standardowego rozszerzenia pgstattuple. Zanim użyjesz poniższych funkcji, musisz wykonać CREATE EXTENSION pgstattuple; w odpowiedniej bazie danych jako superużytkownik. Korzystanie z tych funkcji wymaga również uprawnień superużytkownika.

pgstattuple funkcja zwraca m.in. nieużywane (free_space ) i wielokrotnego użytku (dead_tuple_len ) miejsce na dysku w indeksie. Może to być bardzo pomocne przy podejmowaniu decyzji, czy uruchomić REINDEX aby zmniejszyć rozdęcie indeksu.

idxdemo=# select * from pgstattuple('ix_year'::regclass);
-[ RECORD 1 ]------+-------
table_len          | 663552
tuple_count        | 28795
tuple_len          | 460720
tuple_percent      | 69.43
dead_tuple_count   | 0
dead_tuple_len     | 0
dead_tuple_percent | 0
free_space         | 66232
free_percent       | 9.98

pgstattuple funkcja zwraca informacje specyficzne dla B-drzewa, w tym poziom drzewa:

idxdemo=# select * from pgstatindex('ix_year'::regclass);
-[ RECORD 1 ]------+-------
version            | 2
tree_level         | 1
index_size         | 663552
root_block_no      | 3
internal_pages     | 1
leaf_pages         | 79
empty_pages        | 0
deleted_pages      | 0
avg_leaf_density   | 89.72
leaf_fragmentation | 0

Można to wykorzystać do podjęcia decyzji, czy dostosować współczynnik wypełnienia indeksu.

Badanie zawartości indeksu B-Tree

Nawet zawartość B-Tree można sprawdzić bezpośrednio, używając extensionpageinspect. Korzystanie z tego rozszerzenia wymaga uprawnień administratora.

Oto właściwości pojedynczej strony (tutaj, 13. strona) indeksu:

idxdemo=# select * from bt_page_stats('ix_year', 13);
-[ RECORD 1 ]-+-----
blkno         | 13
type          | l
live_items    | 367
dead_items    | 0
avg_item_size | 16
page_size     | 8192
free_size     | 808
btpo_prev     | 12
btpo_next     | 14
btpo          | 0
btpo_flags    | 1

A oto rzeczywista zawartość każdego elementu (ograniczona do 5 tutaj) na stronie:

idxdemo=# select * from bt_page_items('ix_year', 13) limit 5;
 itemoffset |   ctid   | itemlen | nulls | vars |          data
------------+----------+---------+-------+------+-------------------------
          1 | (104,40) |      16 | f     | f    | 86 07 00 00 00 00 00 00
          2 | (95,38)  |      16 | f     | f    | 86 07 00 00 00 00 00 00
          3 | (95,39)  |      16 | f     | f    | 86 07 00 00 00 00 00 00
          4 | (95,40)  |      16 | f     | f    | 86 07 00 00 00 00 00 00
          5 | (96,1)   |      16 | f     | f    | 86 07 00 00 00 00 00 00
(5 rows)

A jeśli myślisz o napisaniu zapytania, aby zagregować coś na każdej stronie, będziesz potrzebować również całkowitej liczby stron w relacji, którą można utworzyć za pomocą pg_relpages z pgstattuple rozszerzenie:

idxdemo=# select pg_relpages('ix_year');
 pg_relpages
-------------
          81
(1 row)

Inne typy indeksów

Indeksy B-Tree to wszechstronne narzędzia do optymalizacji zapytań. Przy odrobinie eksperymentów i planowania można go wykorzystać do znacznej poprawy czasów odpowiedzi aplikacji i zadań raportowania.

Inne typy indeksów PostgreSQL są również przydatne i mogą być bardziej wydajne i wydajne niż B-Tree w określonych przypadkach. Ten artykuł zawiera szybki przegląd wszystkich typów.

Masz wskazówkę dotyczącą indeksów, które chcesz udostępnić? Zostaw je jako komentarz poniżej!


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Konsola Rails znajduje użytkowników według tablicy identyfikatorów

  2. funkcje tablicy biginteger

  3. Jak zaokrąglić do najbliższych X minut w PL/pgSQL?

  4. PostgreSQL usuń całą zawartość

  5. Przechowywanie json, jsonb, hstore, xml, enum, ipaddr itp. kończy się niepowodzeniem, ponieważ kolumna x jest typu json, ale wyrażenie ma charakter zmienny