(a, b)
复合索引( 上的索引)和哈希函数上的索引之间的主要区别是:
另一方面,对于 on 的索引a::bigint << 32 + b
,即结合了 and 的值的 64 位单元素索引,a
只有同时具有andb
时才能使用它。上的索引也是如此。a
b
some_hash_function(a,b)
在值的哈希值上建立索引可能有很大的优势,因为它以降低选择性和需要重新检查条件为代价使索引更小,例如:
WHERE some_hash_function(a,b) = some_hash_function(42,3) AND (a = 42 AND b = 3)
但是,您忽略了一个重要的可能性:a
和上的两个单独的索引b
。PostgreSQL 可以将它们组合在位图索引扫描中,也可以单独使用它们,无论哪种方式更适合查询。对于两个松散相关且大部分不相关的值,这通常是最佳选择。
举个例子:
CREATE TABLE demoab(a integer, b integer);
INSERT INTO demoab(a, b)
SELECT a, b from generate_series(1,1000) a
CROSS JOIN generate_series(1,1000) b;
CREATE INDEX demoab_a ON demoab(a);
CREATE INDEX demoab_b ON demoab(b);
CREATE INDEX demoab_ab ON demoab(a,b);
CREATE INDEX demoab_ab_shifted ON demoab ((a::bigint << 32 + b));
ANALYZE demoab;
CREATE TABLE demob AS SELECT DISTINCT b FROM demoab ;
CREATE TABLE demoa AS SELECT DISTINCT a FROM demoab ;
ALTER TABLE demoa ADD PRIMARY KEY (a);
ALTER TABLE demob ADD PRIMARY KEY (b);
不同的查询方法:
regress=> explain analyze SELECT * FROM demoab WHERE a = 42 AND b = 3;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------
Index Scan using demoab_ab on demoab (cost=0.00..8.38 rows=1 width=8) (actual time=0.034..0.036 rows=1 loops=1)
Index Cond: ((a = 42) AND (b = 3))
Total runtime: 0.088 ms
(3 rows)
regress=> explain analyze SELECT * FROM demoab WHERE b = 3;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on demoab (cost=19.85..2358.66 rows=967 width=8) (actual time=1.089..4.636 rows=1000 loops=1)
Recheck Cond: (b = 3)
-> Bitmap Index Scan on demoab_b (cost=0.00..19.61 rows=967 width=0) (actual time=0.661..0.661 rows=1000 loops=1)
Index Cond: (b = 3)
Total runtime: 4.820 ms
(5 rows)
regress=> explain analyze SELECT * FROM demoab WHERE a = 42;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
Index Scan using demoab_a on demoab (cost=0.00..37.19 rows=962 width=8) (actual time=0.155..0.751 rows=1000 loops=1)
Index Cond: (a = 42)
Total runtime: 0.929 ms
(3 rows)
regress=> explain analyze SELECT * FROM demoab WHERE (a::bigint << 32 + b) = (42::bigint << 32 + 3);
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on demoab (cost=4.69..157.67 rows=41 width=8) (actual time=0.260..0.495 rows=94 loops=1)
Recheck Cond: (((a)::bigint << (32 + b)) = 1443109011456::bigint)
-> Bitmap Index Scan on demoab_ab_shifted (cost=0.00..4.67 rows=41 width=0) (actual time=0.232..0.232 rows=94 loops=1)
Index Cond: (((a)::bigint << (32 + b)) = 1443109011456::bigint)
Total runtime: 0.584 ms
(5 rows)
在这里,复合索引(a,b)
将是一个明显的胜利,因为它能够使用仅索引扫描来直接获取元组,但实际上不太可能从非索引列中获取值。因此,我参加SET enable_indexscan = off
了这些测试。
令人惊讶的是,索引大小是相同的:
regress=> SELECT
pg_relation_size('demoab_ab') AS shifted,
pg_relation_size('demoab_ab') AS ab,
pg_relation_size('demoab_a') AS a,
pg_relation_size('demoab_b') AS b;
shifted | ab | a | b
----------+----------+----------+----------
22487040 | 22487040 | 22487040 | 22487040
(1 row)
我预计单值索引需要的空间要少得多。对齐要求解释了其中一些,但这对我来说仍然是一个意想不到的结果。
在加入@wildplasser 的情况下询问:
regress=> EXPLAIN ANALYZE
SELECT demoa.a, demob.b
FROM demoab
INNER JOIN demoa ON (demoa.a = demoab.a)
INNER JOIN demob ON (demob.b = demoab.b)
WHERE demoa.a = 100 AND demob.b = 500;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------
Nested Loop (cost=0.00..24.94 rows=1 width=8) (actual time=0.121..0.126 rows=1 loops=1)
-> Nested Loop (cost=0.00..16.66 rows=1 width=8) (actual time=0.089..0.092 rows=1 loops=1)
-> Index Scan using demoab_ab on demoab (cost=0.00..8.38 rows=1 width=8) (actual time=0.021..0.021 rows=1 loops=1)
Index Cond: ((a = 100) AND (b = 500))
-> Index Scan using demoa_pkey on demoa (cost=0.00..8.27 rows=1 width=4) (actual time=0.062..0.062 rows=1 loops=1)
Index Cond: (a = 100)
-> Index Scan using demob_pkey on demob (cost=0.00..8.27 rows=1 width=4) (actual time=0.029..0.031 rows=1 loops=1)
Index Cond: (b = 500)
Total runtime: 0.203 ms
(9 rows)
表明在这种情况下 PostgreSQL 更喜欢 (a, b) 上的复合索引。如果您只是加入,则情况并非如此b
:
regress=> EXPLAIN ANALYZE
SELECT demoab.a, demoab.b
FROM demoab
INNER JOIN demob ON (demob.b = demoab.b)
WHERE demob.b = 500;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------
Nested Loop (cost=19.85..2376.59 rows=967 width=8) (actual time=0.935..3.653 rows=1000 loops=1)
-> Index Scan using demob_pkey on demob (cost=0.00..8.27 rows=1 width=4) (actual time=0.029..0.032 rows=1 loops=1)
Index Cond: (b = 500)
-> Bitmap Heap Scan on demoab (cost=19.85..2358.66 rows=967 width=8) (actual time=0.897..3.123 rows=1000 loops=1)
Recheck Cond: (b = 500)
-> Bitmap Index Scan on demoab_b (cost=0.00..19.61 rows=967 width=0) (actual time=0.436..0.436 rows=1000 loops=1)
Index Cond: (b = 500)
Total runtime: 3.834 ms
(8 rows)
您会注意到,任何功能性哈希索引在这里都没有用。因此,出于这个原因,我建议在需要时使用复合索引(a,b)
加上二级索引(b)
。
就独特性而言,您会发现查看pg_catalog.pg_stat
. 在那里,您会看到PostgreSQL 不维护单个索引的统计信息,只维护索引的堆列。在这种情况下:
regress=> select tablename, attname, n_distinct, correlation
from pg_stats where tablename like 'demo%';
tablename | attname | n_distinct | correlation
-------------------+---------+------------+-------------
demoab | a | 1000 | 1
demoab | b | 1000 | 0.0105023
demoab_ab_shifted | expr | 21593 | 0.0175595
demob | b | -1 | 0.021045
demoa | a | -1 | 0.021045
(5 rows)
看起来 Pg 不会看到散列/组合方法与两个离散的独立值之间的任何显着差异。