PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Бележки относно индексите на PostgreSQL B-Tree

PostgreSQL се предлага с не по-малко от 6 различни типа индекси, като B-Treeindex е най-често използваният. Прочетете, за да научите повече за B-Treeindexes в PostgreSQL.

Типове индекси

Индекс в PostgreSQL, като тези, създадени за PRIMARY KEY и UNIQUE в оператор aCREATE TABLE или създаден изрично с оператор CREATE INDEX, са от определен „тип“ (въпреки че технически трябва да ги наричаме „методи за достъп до индекс“).

PostgreSQL се предлага с тези вградени типове индекси:

  • B-дърво
  • Хеш
  • GIN – Обобщен обърнат индекс
  • BRIN – Block Range Index (само във v9.5 и по-нова версия)
  • GiST – Обобщено обърнато дърво за търсене
  • SP-GiST – GiST, разделен на пространство

B-Tree е по подразбиране и най-често използваният тип индекс. Указването на първичен ключ или уникален в израза CREATE TABLE кара PostgreSQL да създава индекси на B-Tree. Изявленията CREATE INDEX без клаузата USING също ще създават индекси на 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);

Поръчка

Индексите на B-Tree са по своята същност подредени. PostgreSQL може да използва този ред, вместо да сортира индексирания израз. Например, сортирането на заглавията на всички филми от 80-те по заглавие ще изисква сортиране:

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)

Но ако ги сортирате по индексираната колона (година), допълнително сортиране не се изисква.

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)

Коефициент на запълване

За таблици, които няма да се актуализират, можете да увеличите „коефициента на запълване“ от 90 по подразбиране, което трябва да ви даде малко по-малки и по-бързи индекси. Обратно, ако има чести актуализации на таблицата, включващи индексирания параметър, можете да намалите фактора на запълване до по-малък брой – това ще позволи по-бързи вмъквания и актуализации с цената на малко по-големи индекси.

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

Индексиране на текст

Индексите на B-Tree могат да помогнат при съвпадението на префиксите на текста. Нека направим запитване за списък на всички филми, започващи с буквата „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)

Този план изисква пълно последователно сканиране на таблицата. Какво се случва, ако добавим индекс на B-Tree към 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)

Е, това изобщо не помогна. Въпреки това, има форма на магически прах от пикси, който можем да поръсим, за да накараме Postgres да прави това, което искаме:

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)

Планът вече използва индекс и цената е намалена. Магията тук е „text_pattern_ops“, която позволява индексът на B-Tree върху израз „text“ да се използва за оператори на шаблон (LIKE и регулярни изрази). „text_pattern_ops“ се нарича OperatorClass.

Обърнете внимание, че това ще работи само за модели с фиксиран текстов префикс, така че „%Angry%“ или „%Men“ няма да работи. Използвайте пълнотекстово търсене на PostgreSQL за разширени текстови заявки.

Покриващи индекси

Покриващите индекси бяха добавени към PostgreSQL във v11. Покриващите индекси ви позволяват да включите стойността на един или повече изрази заедно с индексирания израз вътре в индекса.

Нека опитаме да направим заявка за всички филмови заглавия, подредени по година на издаване:

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)

Това включва пълно последователно сканиране на таблицата, последвано от един вид на проектираните колони. Нека първо добавим редовен индекс към 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)

Сега Postgres решава да използва индекса, за да извади записите от таблицата директно в желания ред. Таблицата трябва да се потърси, защото индексът съдържа само стойността на „година“ и препратката към кортежа в таблицата.

Ако включим стойността на ‘title’ също в индекса, търсенето в таблицата може да бъде избегнато напълно. Нека използваме новия синтаксис, за да създадем такъв индекс:

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 вече използва Index OnlyScan, което означава, че търсенето на таблица е напълно избегнато. Обърнете внимание, че трябваше да премахнем стария индекс, тъй като Postgres не избра ix_year_cov пред ix_year за тази заявка.

Групиране

PostgreSQL позорно не поддържа автоматично физическо подреждане на редове в таблица, за разлика от „клъстерираните индекси“ в други RDBMS. Ако повечето от вашите заявки ще извадят повечето от редовете на предимно статична таблица във фиксиран ред, би било добра идея да подредите хранилището на физическата таблица в този ред и да използвате последователни сканирания. За да пренаредите физически таблица в реда, продиктуван от индекс, използвайте:

CLUSTER VERBOSE movies USING ix_year;

Обикновено използвате индекс B-Tree за повторно групиране на таблица, тъй като той осигурява пълен ред за всички редове в таблицата.

Индексна статистика

Колко дисково пространство заема вашият индекс? Функцията pg_relation_size може да отговори на това:

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

Това връща дисковото пространство, използвано от индекса, в байтове.

Повече информация за индекса може да бъде събрана с помощта на стандартното разширение pgstattuple. Преди да използвате функциите по-долу, трябва да направите CREATE EXTENSION pgstattuple; в съответната база данни като суперпотребител. Използването на тези функции също изисква привилегии на суперпотребител.

pgstattuple функцията връща, наред с други неща, неизползваното (free_space ) и за многократна употреба (dead_tuple_len). ) дисково пространство в индекса. Това може да бъде много полезно при вземането на решение дали да стартирате REINDEX за намаляване на подуването на индекса.

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 функцията връща специфична информация за B-дървото, включително нивото на дървото:

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

Това може да се използва, за да се реши дали да се коригира коефициентът на запълване на индекса.

Разглеждане на съдържанието на B-Tree Index

Дори съдържанието на B-дървото може да бъде разгледано директно, като се използва extensionpageinspect. Използването на това разширение изисква привилегии на суперпотребител.

Ето свойствата на една страница (тук, 13-та страница) на индекса:

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

И ето действителното съдържание на всеки елемент (ограничено до 5 тук) на страницата:

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)

И ако мислите да напишете заявка за обобщаване на нещо върху всяка страница, ще ви е необходим и общият брой страници във връзката, който може да бъде създаден чрез pg_relpages от pgstattuple разширение:

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

Други типове индекси

Индексите на B-Tree са универсални инструменти за оптимизиране на заявки. С малко експериментиране и планиране, той може да се използва за значително подобряване на времето за реакция на приложенията и отчитане на задачи.

Другите типове индекси на PostgreSQL също са полезни и могат да бъдат по-ефективни и по-ефективни от B-Tree в конкретни случаи. Тази статия дава кратък преглед на всички видове.

Имате ли съвет за индекси, които бихте искали да споделите? Оставете ги като коментар по-долу!


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Как да разположите Teamcity с PostgreSQL за висока наличност

  2. Linux файлови системи и контролни точки на PostgreSQL

  3. Грешка в SQLAlchemy без предоставена парола

  4. Какво означава грешка при импортиране:Символът не е намерен:_PQencryptPasswordConn и как да го поправя?

  5. Проверка дали postgresql таблица съществува под python (и вероятно Psycopg2)