PostgreSQL kommer med inte mindre än 6 olika typer av index, där B-Treeindex är det vanligaste. Läs vidare för att ta reda på mer om B-Treeindexes i PostgreSQL.
Typer av index
Ett index i PostgreSQL, som de som skapats för PRIMÄRA KEYS och UNIQUEs i en CREATE TABLE-sats eller skapade uttryckligen med en CREATE INDEX-sats, är av en viss "typ" (även om vi tekniskt sett borde kalla dem "indexaccess-metoder").
PostgreSQL kommer med dessa inbyggda indextyper:
- B-träd
- Hash
- GIN – Generalized Inverted Index
- BRIN – Block Range Index (endast i v9.5 och högre)
- GiST – Generalized Inverted Search Tree
- SP-GiST – Space Partitioned GiST
B-Tree är standard och den mest använda indextypen. Att specificera en primär nyckel eller en unik i en CREATE TABLE-sats gör att PostgreSQL skapar B-Tree-index. SKAPA INDEX-satser utan USING-satsen kommer också att skapa B-Tree-index:
-- 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);
Beställning
B-Tree index är naturligt ordnade. PostgreSQL kan använda denna ordning i stället för att sortera på det indexerade uttrycket. Att till exempel få titlarna på alla 80-talsfilmer sorterade efter titel skulle kräva en sortering:
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)
Men om du sorterar dem efter den indexerade kolumnen (år), krävs ingen ytterligare sortering.
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)
Fillfaktor
För tabeller som inte kommer att uppdateras kan du öka "fyllningsfaktorn" från standardvärdet 90, vilket borde ge dig något mindre och snabbare index. Omvänt, om det finns frekventa uppdateringar av tabellen som involverar den indexerade parametern, kan du minska utfyllnadsfaktorn till ett mindre antal – detta kommer att möjliggöra snabbare infogningar och uppdateringar, till bekostnad av något större index.
CREATE INDEX ix_smd ON silent_movies (director) WITH (fillfactor = 100);
Indexering på text
B-Tree-index kan hjälpa till med prefixmatchning av text. Låt oss ta en fråga för att lista alla filmer som börjar med bokstaven '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)
Denna plan kräver en fullständig sekventiell genomsökning av tabellen. Vad händer om vi lägger till ett B-Tree-index på 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)
Nåväl, det hjälpte inte alls. Men det finns en form av magiskt nävdamm som vi kan strö för att få Postgres att göra vad vi vill:
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)
Planen använder nu ett index, och kostnaden har minskat. Magin här är "text_pattern_ops" som gör att B-Tree index över ett "text" uttryck kan användas för mönsteroperatorer (LIKE och reguljära uttryck). "text_pattern_ops" kallas en OperatorClass.
Observera att detta bara fungerar för mönster med ett fast textprefix, så "%Angry%" eller "%Men" kommer inte att fungera. Använd PostgreSQL:s fulltextsökning för avancerade textfrågor.
Täckande index
Täckande index lades till PostgreSQL i v11. Om du täcker index kan du inkludera värdet av ett eller flera uttryck tillsammans med det indexerade uttrycket i indexet.
Låt oss försöka söka efter alla filmtitlar, sorterade efter släppår:
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)
Detta innebär en fullständig sekventiell genomsökning av tabellen, följt av en sorts projicerade kolumner. Låt oss först lägga till ett vanligt index på 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)
Nu bestämmer sig Postgres för att använda indexet för att dra ut posterna direkt från tabellen i önskad ordning. Tabellen måste slås upp eftersom indexet endast innehåller värdet för 'år' och referensen till tupeln i tabellen.
Om vi inkluderar värdet på "titel" också i indexet, kan tabelluppslagningen undvikas helt. Låt oss använda den nya syntaxen för att skapa ett sådant index:
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 använder nu en Index OnlyScan, vilket innebär att tabellsökningen helt undviks. Observera att vi var tvungna att ta bort det gamla indexet eftersom Postgres inte valde ix_year_cov framför ix_year för den här frågan.
Klustring
PostgreSQL stöder ökänt inte automatisk fysisk ordning av rader i en tabell, till skillnad från "klustrade index" i andra RDBMS. Om de flesta av dina frågor ska dra ut de flesta raderna i en mestadels statisk tabell i en fast ordning, skulle det vara en bra idé att layouta den fysiska tabelllagringen i den ordningen och använda sekventiella skanningar. För att ordna om en tabell fysiskt i den ordning som dikteras av ett index, använd:
CLUSTER VERBOSE movies USING ix_year;
Du skulle vanligtvis använda ett B-Tree-index för att samla ihop en tabell, eftersom det ger en fullständig ordning för alla rader i tabellen.
Indexstatistik
Hur mycket diskutrymme tar ditt index upp? Funktionen pg_relation_size kan svara på det:
idxdemo=# select * from pg_relation_size('ix_year');
pg_relation_size
------------------
663552
(1 row)
Detta returnerar det diskutrymme som används av indexet, i byte.
Mer information om indexet kan samlas in med hjälp av standard extensionpgstattuple. Innan du använder funktionerna nedan måste du göra en CREATE EXTENSION pgstattuple;
i den relevanta databasen som en superanvändare. Användning av dessa funktioner kräver också superanvändarprivilegier.
pgstattuple
funktion returnerar bland annat det oanvända (free_space
)och återanvändbar (dead_tuple_len
) diskutrymme i indexet. Detta kan vara till stor hjälp för att bestämma om en REINDEX
ska köras för att minska indexuppblåsthet.
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
funktionen returnerar B-Tree-specifik information, inklusive trädets nivå:
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
Detta kan användas för att bestämma om fyllningsfaktorn för indexet ska justeras.
Undersöka B-Tree Index innehåll
Även innehållet i B-trädet kan granskas direkt med hjälp av extensionpageinspect. Användningen av det här tillägget kräver superanvändarprivilegier.
Här är egenskaperna för en enskild sida (här, den 13:e sidan) i indexet:
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
Och här är det faktiska innehållet för varje objekt (begränsat till 5 här) på sidan:
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)
Och om du funderar på att skriva en fråga för att samla något över varje sida, behöver du också det totala antalet sidor i relationen, som kan skapas via pg_relpages
från pgstattuple
tillägg:
idxdemo=# select pg_relpages('ix_year');
pg_relpages
-------------
81
(1 row)
Andra indextyper
B-Tree-index är mångsidiga verktyg för att optimera frågor. Med lite experiment och planering kan den användas för att avsevärt förbättra svarstiderna för ansökningar och rapportera jobb.
De andra indextyperna av PostgreSQL är också användbara och kan vara mer effektiva och prestanda än B-Tree i specifika fall. Den här artikeln ger en snabböversikt över alla typer.
Har du ett tips på index som du vill dela med dig av? Lämna dem som en kommentar nedan!