1. 程式人生 > 其它 >【INDEX】Postgresql索引介紹

【INDEX】Postgresql索引介紹

索引訪問方法介紹

支援的索引

  1. mydb=# select * from pg_am;
  2. oid | amname | amhandler | amtype
  3. ------+--------+----------------------+--------
  4. 2 | heap | heap_tableam_handler | t
  5. 403 | btree | bthandler | i
  6. 405 | hash | hashhandler | i
  7. 783 | gist | gisthandler | i
  8. 2742 | gin | ginhandler | i
  9. 4000 | spgist | spghandler | i
  10. 3580 | brin | brinhandler | i
  11. (7 rows)

索引屬性

  • 訪問方法屬性—pg_indexam_has_property
  • 特定索引的屬性—pg_index_has_property
  • 索引中各列的屬性—pg_index_column_has_property
訪問方法屬性中的四種類型
  • can_order: 訪問方法能夠在建立索引時指定值的排序順序(僅適用於“ btree”)
  • can_unique:支援唯一約束和主鍵,適用於btree
  • can_multi_col:可以在多列建立索引
  • can_exclude:支援排它約束
  1. select a.amname, p.name, pg_indexam_has_property(a.oid,p.name)
  2. from pg_am a,
  3. unnest(array['can_order','can_unique','can_multi_col','can_exclude']) p(name)
  4. where a.amname = 'btree'
  5. order by a.amname;
  6. amname | name | pg_indexam_has_property
  7. --------+---------------+-------------------------
  8. btree | can_order | t
  9. btree | can_unique | t
  10. btree | can_multi_col | t
  11. btree | can_exclude | t
  12. (4 rows)
特定索引屬性
  • clusterable:可以根據索引對行進行重新排序(對其進行cluster操作)
  • index_scan:支援索引掃描。儘管此屬性可能看起來很奇怪,但並非所有索引都能一次返回TID-有些返回結果一次全部返回,並且僅支援點陣圖掃描
  • bitmap_scan: 支援點陣圖掃描
  • backward_scan:可以按建立索引時指定的相反順序返回結果

clusterable:可以根據索引對錶的行重新排序,直接使用cluster命令,語法如下:

  1. mydb=# \h cluster
  2. Command: CLUSTER
  3. Description: cluster a table according to an index
  4. Syntax:
  5. CLUSTER [VERBOSE] table_name [ USING index_name ]
  6. CLUSTER [VERBOSE]
  7. URL: https://www.postgresql.org/docs/13/sql-cluster.html
列屬性
  • asc,desc,nulls_first,nulls_last,orderable:這些屬性與值的排序有關,只有btree索引支援排序
  • distance_orderable:可以按照操作確定的排序順序返回結果(到目前為止僅適用於GiST和RUM索引)
  • returnable:在不訪問表的情況下使用索引的可能性,即僅索引掃描的支援
  • search_array:支援使用表示式搜尋多個值。
  • search_nulls:通過IS NULL和IS NOT NULL條件進行搜尋的可能性。
  1. select p.name,
  2. pg_index_column_has_property('tbl_a_pkey'::regclass,1,p.name)
  3. from unnest(array[
  4. 'asc','desc','nulls_first','nulls_last','orderable','distance_orderable','returnable','search_array','search_nulls']) p(name);
  5. name | pg_index_column_has_property
  6. --------------------+------------------------------
  7. asc | t
  8. desc | f
  9. nulls_first | f
  10. nulls_last | t
  11. orderable | t
  12. distance_orderable | f
  13. returnable | t
  14. search_array | t
  15. search_nulls | t
  16. (9 rows)

操作符類和操作符族

除了需要知道各種操作方法相關的屬性之外,我們還需要知道各種不同的操作方法分別支援哪些資料型別和哪些運算子。pg中提供了操作符類和操作符族的概念。

操作符類包含用於索引的最小運算子集(可能還有輔助函式),以操作某種資料型別。

某些操作符族中包括一個操作符類別。此外,如果一個通用的運算子族具有相同的語義,則它們可以包含多個運算子類。例如,integer_ops系列包括型別bigint,integer和smallint的int8_ops,int4_ops和int2_ops類,它們的大小不同但含義相同

可操作的型別
  1. --如整型和日期為例
  2. select opfname, opcname, opcintype::regtype
  3. from pg_opclass opc, pg_opfamily opf
  4. where opf.opfname = 'integer_ops'
  5. and opc.opcfamily = opf.oid
  6. and opf.opfmethod = (
  7. select oid from pg_am where amname = 'btree'
  8. );
  9. opfname | opcname | opcintype
  10. -------------+----------+-----------
  11. integer_ops | int2_ops | smallint
  12. integer_ops | int4_ops | integer
  13. integer_ops | int8_ops | bigint
  14. (3 rows)
  15. --datetime_ops系列包含用於操作日期(有時間和無時間)的運算子類:
  16. select opfname, opcname, opcintype::regtype
  17. from pg_opclass opc, pg_opfamily opf
  18. where opf.opfname = 'datetime_ops'
  19. and opc.opcfamily = opf.oid
  20. and opf.opfmethod = (
  21. select oid from pg_am where amname = 'btree'
  22. );
  23. opfname | opcname | opcintype
  24. --------------+-----------------+-----------------------------
  25. datetime_ops | date_ops | date
  26. datetime_ops | timestamptz_ops | timestamp with time zone
  27. datetime_ops | timestamp_ops | timestamp without time zone
  28. (3 rows)

運算子族還可以包括其他運算子,以比較不同型別的值。分組到族中使計劃者可以將具有不同型別值的謂詞使用索引。一個族還可以包含其他輔助功能。
在大多數情況下,我們不需要了解操作員的族和類。通常,我們只建立索引,預設情況下使用某個運算子類。

但是,我們可以顯式指定運算子類。這是當需要顯式規範時的一個簡單示例:==在排序規則不同於C的資料庫中,常規索引不支援LIKE操作:==

  1. explain select * from t where b like 'A%';
  2. --這種情況我們就可以通過使用操作符類 text_pattern_ops建立索引來克服此限制:
  3. create index on t(b text_pattern_ops);

我們可以通過查詢這些系統目錄解決很多問題,例如:某種訪問方法可以操縱哪些資料型別:

btree方法可以操作以下資料型別

  1. select opcname, opcintype::regtype
  2. from pg_opclass
  3. where opcmethod = (select oid from pg_am where amname = 'btree')
  4. order by opcintype::regtype::text;
  5. opcname | opcintype
  6. ---------------------+-----------------------------
  7. array_ops | anyarray
  8. enum_ops | anyenum
  9. range_ops | anyrange
  10. int8_ops | bigint
  11. bit_ops | bit
  12. varbit_ops | bit varying
  13. bool_ops | boolean
  14. bytea_ops | bytea
  15. char_ops | "char"
  16. bpchar_pattern_ops | character
  17. bpchar_ops | character
  18. date_ops | date
  19. float8_ops | double precision
  20. cidr_ops | inet
  21. inet_ops | inet
  22. int4_ops | integer
  23. interval_ops | interval
  24. jsonb_ops | jsonb
  25. macaddr_ops | macaddr
  26. macaddr8_ops | macaddr8
  27. money_ops | money
  28. name_ops | name
  29. numeric_ops | numeric
  30. oid_ops | oid
  31. oidvector_ops | oidvector
  32. pg_lsn_ops | pg_lsn
  33. float4_ops | real
  34. record_image_ops | record
  35. record_ops | record
  36. int2_ops | smallint
  37. varchar_pattern_ops | text
  38. text_ops | text
  39. varchar_ops | text
  40. text_pattern_ops | text
  41. tid_ops | tid
  42. timestamp_ops | timestamp without time zone
  43. timestamptz_ops | timestamp with time zone
  44. time_ops | time without time zone
  45. timetz_ops | time with time zone
  46. tsquery_ops | tsquery
  47. tsvector_ops | tsvector
  48. uuid_ops | uuid
  49. xid8_ops | xid8
  50. (43 rows)

運算子類包含哪些運算子

  1. select amop.amopopr::regoperator
  2. from pg_opclass opc, pg_opfamily opf, pg_am am, pg_amop amop
  3. where opc.opcname = 'array_ops'
  4. and opf.oid = opc.opcfamily
  5. and am.oid = opf.opfmethod
  6. and amop.amopfamily = opc.opcfamily
  7. and am.amname = 'btree'
  8. and amop.amoplefttype = opc.opcintype;
  9. amopopr
  10. -----------------------
  11. <(anyarray,anyarray)
  12. <=(anyarray,anyarray)
  13. =(anyarray,anyarray)
  14. >=(anyarray,anyarray)
  15. >(anyarray,anyarray)
  16. (5 rows)

相關資料字典

資料字典描述
pg_proc procedure
pg_operator operator
pg_amop access method’s operator
pg_opfamily operator family
pg_amproc access method’s support procedure
pg_type datatype
pg_opclass operator class
pg_am access method

索引支援的新資料型別

  1. --建立一個新的組合型別
  2. create type complex as (re float, im float);
  3. --建立表
  4. create table t1(x complex);
  5. insert into t1 values ((0.0, 10.0)), ((1.0, 3.0)), ((1.0, 1.0));
  6. insert into t1 values((1.0,2.0)),((2.0,2.0));

預設情況,對於組合型別排序是分開的,先比較第一個欄位再比較第二個,也可以通過其他方式排序。

  1. --建立相關函式
  2. create function modulus(a complex) returns float as $$
  3. select sqrt(a.re*a.re + a.im*a.im);
  4. $$ immutable language sql;
  5. --定義5種操作符函式:
  6. create function complex_lt(a complex, b complex) returns boolean as $$
  7. select modulus(a) < modulus(b);
  8. $$ immutable language sql;
  9. create function complex_le(a complex, b complex) returns boolean as $$
  10. select modulus(a) <= modulus(b);
  11. $$ immutable language sql;
  12. create function complex_eq(a complex, b complex) returns boolean as $$
  13. select modulus(a) = modulus(b);
  14. $$ immutable language sql;
  15. create function complex_ge(a complex, b complex) returns boolean as $$
  16. select modulus(a) >= modulus(b);
  17. $$ immutable language sql;
  18. create function complex_gt(a complex, b complex) returns boolean as $$
  19. select modulus(a) > modulus(b);
  20. $$ immutable language sql;

建立對應的操作符

  1. create operator #<#(leftarg=complex, rightarg=complex, procedure=complex_lt);
  2. create operator #<=#(leftarg=complex, rightarg=complex, procedure=complex_le);
  3. create operator #=#(leftarg=complex, rightarg=complex, procedure=complex_eq);
  4. create operator #>=#(leftarg=complex, rightarg=complex, procedure=complex_ge);
  5. create operator #>#(leftarg=complex, rightarg=complex, procedure=complex_gt);
  6. --比較數字
  7. select (1.0,1.0)::complex #<# (1.0,3.0)::complex;

除了整個5個操作符,還需要定義函式:小於返回-1;等於返回0;大於返回1。其他訪問方法可能需要定義其他函式

  1. create function complex_cmp(a complex, b complex) returns integer as $$
  2. select case when modulus(a) < modulus(b) then -1
  3. when modulus(a) > modulus(b) then 1
  4. else 0
  5. end; $$ language sql;

建立一個操作符類

  1. create operator class complex_ops
  2. default for type complex
  3. using btree as
  4. operator 1 #<#,
  5. operator 2 #<=#,
  6. operator 3 #=#,
  7. operator 4 #>=#,
  8. operator 5 #>#,
  9. function 1 complex_cmp(complex,complex);
  10. --檢視排序結構
  11. mydb=# select * from t1 order by x;
  12. x
  13. --------
  14. (1,1)
  15. (1,3)
  16. (0,10)
  17. (3 rows)
  18. --檢視可以使用此查詢獲取支援的函式:
  19. select amp.amprocnum,
  20. amp.amproc,
  21. amp.amproclefttype::regtype,
  22. amp.amprocrighttype::regtype
  23. from pg_opfamily opf,
  24. pg_am am,
  25. pg_amproc amp
  26. where opf.opfname = 'complex_ops'
  27. and opf.opfmethod = am.oid
  28. and am.amname = 'btree'
  29. and amp.amprocfamily = opf.oid;
  30. amprocnum | amproc | amproclefttype | amprocrighttype
  31. -----------+-------------+----------------+-----------------
  32. 1 | complex_cmp | complex | complex
  33. (1 row)

管理操作符

  1. --檢視 操作符 和操作符類
  2. select * from pg_operator;
  3. select * from pg_opclass;
  4. --檢視操作符型別
  5. select o.oid,o.oprname,
  6. (select t.typname from pg_type t
  7. where o.oprleft=t.oid) as oprleft,
  8. (select t.typname from pg_type t
  9. where o.oprright=t.oid) as oprright
  10. from pg_operator o where o.oprname like '#%#';
  11. --刪除操作符
  12. drop operator #<# (complex,complex);
  13. --刪除操作類
  14. DROP OPERATOR CLASS complex_ops using btree;
  15. --刪除函式
  16. select * from pg_proc where proname like 'com%';
  17. drop function function_name;

參考資料

BTREE索引

重要特徵

  • B樹是平衡的
  • B樹是多分支的,即每個頁面(通常為8 KB)包含許多(數百個)ctid。因此,B樹的深度很小,對於非常大的表,實際上可以達到4–5的深度。
  • 索引中的資料按非遞減順序排序,(在頁面之間和每個頁面內部),並且同一級別的頁面通過雙向列表相互連線。因此,我們可以僅通過列表的一個方向或另一個方向獲得有序資料集,而不必每次都返回到根。

無論哪種掃描型別(index scan、index only scan、bitmap scan),使用btree索引都會返回有序的資料。所以,如果在排序列上存在索引,優化器會首先考慮是索引掃描,否則就是先順序掃描然後排序。

  1. create index idx_t1 on t1(c1 desc);
  2. --尤其組合索引 ,如果常用排序,可使用如下方法
  3. create index idx_t1 on t1(c1 desc,c2 asc);

空值

pg索引可以儲存空值,並支援按條件IS NULL和IS NOT NULL進行搜尋。

  1. explain select * from t1 where c1 is null;

在索引中null值儲存在索引的一端,取決於建立索引時指定nulls first還是nulls last。

如果查詢包括排序,則這一點很重要:如果SELECT命令在其ORDER BY子句中指定的NULL順序與構建索引指定的順序相同(NULLS FIRST或NULLS LAST),則可以使用索引,否則將無法使用索引。

在資料庫中null是無法和其它值進行比較的

內部結構

PostgreSQL的btree索引的演算法可以參考:src/backend/access/nbtree/README

其中PostgreSQL 的B-Tree索引頁分為幾種類別,我們可以通過btpo_flags的值去區分.

  1. meta page
  2. root page # btpo_flags=2
  3. branch page # btpo_flags=0
  4. leaf page # btpo_flags=1
  5. leaf&root page # btpo_flags=3
  6. --詳情見:src/include/access/nbtree.h
  7. /* Bits defined in btpo_flags */
  8. #define BTP_LEAF (1 << 0) /* leaf page, i.e. not internal page */
  9. #define BTP_ROOT (1 << 1) /* root page (has no parent) */
  10. #define BTP_DELETED (1 << 2) /* page has been deleted from tree */
  11. #define BTP_META (1 << 3) /* meta-page */
  12. #define BTP_HALF_DEAD (1 << 4) /* empty, but still in tree */
  13. #define BTP_SPLIT_END (1 << 5) /* rightmost page of split group */
  14. #define BTP_HAS_GARBAGE (1 << 6) /* page has LP_DEAD tuples */
  15. #define BTP_INCOMPLETE_SPLIT (1 << 7) /* right sibling's downlink is missing */

其中meta page和root page是必須有的,meta page需要一個頁來儲存,表示指向root page的page id。

隨著記錄數的增加,一個root page可能存不下所有的heap item,就會有leaf page,甚至branch page,甚至多層的branch page。

一共有幾層branch 和 leaf,就用btree page元資料的 level 來表示。

可以通過pageinspect外掛研究所以你內部結構

  • bt_metap(relname text):返回record,bt_metap用來返回關於一個B樹索引元頁的資訊。
  • bt_page_stats(relname text, blkno int):返回record,bt_page_stats返回有關
    B-樹索引單一頁面的總計資訊
  • bt_page_items(relname text, blkno int):返回record,bt_page_items返回一個
    B-樹索引頁面上項的所有細節資訊

參考文件

HASH索引

雜湊表是根據鍵(Key)而直接訪問在記憶體儲存位置的資料結構。它通過計算一個關於鍵值的函式,將所需查詢的資料對映到表中一個位置來訪問記錄,這加快了查詢速度。這個對映函式稱做雜湊函式,存放記錄的陣列稱做雜湊表

索引結構

當插入索引時,讓我們計算鍵的雜湊函式。PostgreSQL中的雜湊函式總是返回integer型別,其範圍為2的32次方≈40億個值。儲存桶的數量最初等於2,然後根據資料大小動態增加。bucket編號可以使用位演算法從雜湊碼中計算出來。這是我們將放置ctid的bucket。

當搜尋索引時,我們計算鍵的雜湊函式並獲取bucket編號。現在,仍然需要遍歷bucket的內容,並僅返回具有適當雜湊碼的匹配ctid。由於儲存的“hash code - ctid”對是有序的,因此可以高效地完成此操作。

雜湊索引包含4種頁:meta page, primary bucket page, overflow page, bitmap page

  • metapage(0號頁),包含了HASH索引的控制資訊,指導如何找到其他頁面(每個bucket的primary page),以及當前儲存概貌。其他索引的0號頁基本都是這一個套路。
  • primary bucket page,hash index將儲存劃分為多個bucket(邏輯概念),每個bucket中包含若干page(每個bucket的page數量不需要一致),當插入資料時,根據計算得到的雜湊,通過對映演算法,對映到某個bucket,也就是說資料首先知道應該插入哪個bucket中,然後插入bucket中的primary
    page,如果primary page空間不足時,會擴充套件overflow page,資料寫入overflow page。在page中,資料是有序儲存(TREE),page內支援二分查詢(binary search),而page與page之間是不保證順序的,所以hash index不支援order by。
  • overflow page,是bucket裡面的頁,當primary page沒有足夠空間時,擴充套件的塊稱為overflow page
  • bimap page,記錄primary , overflow page是否為空可以被重用

注意bucket, page都沒有提供收縮功能,即無法從OS中收縮空間,但是提供了reuse(通過bitmap page跟蹤),如果想要減小索引大小的唯一辦法就是使用REINDEX或VACUUM FULL命令從頭開始重建索引

總結

hash索引介紹:
src/backend/access/hash/README

使用場景:

  • 1、等值查詢;
  • 2、btree索引不支援的大欄位型別。

使用限制:

    • 1、不支援對null的處理;
    • 2、不能使用index only scan;
    • 3、不支援多列索引。