我有一张我正在研究的表,它有大约300万个元组。它不会经常更改(每周更新或插入一些)并且可以阅读很多。 (请不要评论长度为1的varchar。我知道,我知道。)
Column | Type | Modifiers
-------------+-----------------------+------------------------------------------------------
id | integer | not null default nextval('mytable_id_seq'::regclass)
A | character varying(5) | not null
B | character varying(16) | not null
C | character varying(3) | not null
D | character varying(1) | not null
otherdata | character varying(99) | not null
Indexes:
"mytable_pkey" PRIMARY KEY, btree (id)
"mytable_unique_key" UNIQUE, btree (A, B, C, D)
"mytable_B_idx" btree (B)
Foreign-key constraints:
"$1" FOREIGN KEY (A, B) REFERENCES anothertable1(A, B)
"$2" FOREIGN KEY (C) REFERENCES anothertable2(C)
"$3" FOREIGN KEY (D) REFERENCES anothertable3(D)
Referenced by:
TABLE "anothertable4" CONSTRAINT "$1" FOREIGN KEY (id) REFERENCES mytable(id)
TABLE "anothertable5" CONSTRAINT "fkey_id" FOREIGN KEY (id) REFERENCES mytable(id) ON UPDATE CASCADE ON DELETE CASCADE
id
是我的主要关键。 A,B,C,D
是候选密钥。两者显然都是唯一识别元组的。
最常见的查询是:
SELECT * FROM mytable WHERE B='foo';
- 将返回多个元组
SELECT * FROM mytable WHERE A='foo' AND B='bar' AND C='baz' AND D='f';
- 将返回一个元组。
因此,为什么B
和A,B,C,D
上有索引。
现在,无论出于何种原因,我正在进行以下查询(更类似):
SELECT * FROM mytable WHERE ((A='foo' AND B='bar') OR (B='foo' AND C='bar'));
一个盒子正在运行PostgreSQL 8.4.4。如果我解析第一个查询,我得到以下查询计划:
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on mytable (cost=9.74..174.30 rows=1 width=14) (actual time=0.000..0.000 rows=5 loops=1)
Recheck Cond: ((((A)::text = 'foo'::text) AND ((B)::text = 'bar'::text)) OR ((B)::text = 'foo'::text))
Filter: ((((A)::text = 'foo'::text) AND ((B)::text = 'bar'::text)) OR (((B)::text = 'foo'::text) AND ((C)::text = 'bar'::text)))
-> BitmapOr (cost=9.74..9.74 rows=42 width=0) (actual time=0.000..0.000 rows=0 loops=1)
-> Bitmap Index Scan on mytable_unique_key(cost=0.00..4.80 rows=1 width=0) (actual time=0.000..0.000 rows=0 loops=1)
Index Cond: (((A)::text = 'foo'::text) AND ((B)::text = 'bar'::text))
-> Bitmap Index Scan on mytable_B_idx(cost=0.00..4.94 rows=42 width=0) (actual time=0.000..0.000 rows=316 loops=1)
Index Cond: ((B)::text = 'foo'::text)
Total runtime: 0.000 ms
(9 rows)
最低成本为9.74,几乎是即时回报(是的,它是缓存的)。现在,如果我在另一台类似的机器上运行PostgreSQL 8.1.5上的相同查询 - 表中的内容完全相同 - 我得到以下内容:
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on mytable (cost=110156.34..110168.36 rows=3 width=26) (actual time=147200.984..147221.480 rows=5 loops=1)
Recheck Cond: ((((A)::text = 'foo'::text) AND ((B)::text = 'bar'::text)) OR (((B)::text = 'foo'::text) AND ((C)::text = 'bar'::text)))
-> BitmapOr (cost=110156.34..110156.34 rows=3 width=0) (actual time=147185.513..147185.513 rows=0 loops=1)
-> Bitmap Index Scan on mytable_unique_key(cost=0.00..2.01 rows=1 width=0) (actual time=83.275..83.275 rows=0 loops=1)
Index Cond: (((A)::text = 'foo'::text) AND ((B)::text = 'bar'::text))
-> Bitmap Index Scan on mytable_unique_key(cost=0.00..110154.34 rows=2 width=0) (actual time=147102.230..147102.230 rows=5 loops=1)
Index Cond: (((B)::text = 'foo'::text) AND ((C)::text = 'bar'::text))
Total runtime: 147221.663 ms
(8 rows)
两张桌子都是VACUUM,两个盒子。因此,令人难以置信的差异是由于8.1.5和8.4.4之间引入的不同版本和性能提升。最开心的开发者!
好的,这个问题的关键不是要对不同版本的PostgreSQL进行基准测试,而是要问:我如何提高上述查询的性能?我有以下解决方案(或问题):
A,B
和B,C
或A
,B
和C
?前者将有助于上述查询。但是,我有其他类似的查询,可以过滤其他列。查询将在以下列集上完成:B
,B,C
,A,B
,A,B,C
,B,C,D
和A,B,C,D
。这是否意味着我需要每个列集的索引?还是最贵的?在上面的查询中,扫描B,C
是最贵的。提前谢谢。
答案 0 :(得分:2)
看起来mytable_unique_key-index在8.1-box上膨胀。首先尝试解决这个问题:
REINDEX TABLE tablename;
重新编制索引后,你能做一个新的EXPLAIN吗?
您也应该开始迁移到更新的版本,对8.1的支持将在今年结束。