1

编辑

这不是关于空间的问题,而是关于索引唯一性的问题,它会影响查询计划。

就基数而言,哪个指标场景更高:

一个

Table:
(
  Col1 smallint,
  Col2 smallint
)

在哪里

Range Col1 : 0 - 1000
Range Col2 : 0 - 1000

和 上的复合索引(Col1, Col2),总是按顺序查询。

桌子:

(
  Col1_2 int
)

在哪里

Range Col1_2 : 0 - 1000^2

(Col1_2)以及结合了 Col1 和 Col2 组件的存储和查询的单个索引。

我基本上要问的是,将多个小数字组合在一起(散列)更好(如在索引使用中),还是没有区别?

4

2 回答 2

5

(a, b)复合索引( 上的索引)和哈希函数上的索引之间的主要区别是:

  • 使用复合索引,PostgreSQL 可以根据它为每个单独的列保留的统计信息做出决策;和

  • 在复合索引中,您可以有效地查询索引a。但是,您不能只查询它b

另一方面,对于 on 的索引a::bigint << 32 + b,即结合了 and 的值的 64 位单元素索引,a只有同时具有andb时才能使用它。上的索引也是如此。absome_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 不会看到散列/组合方法与两个离散的独立值之间的任何显着差异。

于 2013-07-17T08:19:02.670 回答
1

如果 Col1 和 Col2 是独立的字段,为什么要合并?它不会节省任何空间。只要坚持数据库的原子性原则。

于 2013-07-17T07:55:03.057 回答