我们正在与同事的复合键进行讨论。事实上,Mysql需要在where子句中顺序使用索引列而没有间隙供计划者使用。我想展示Postgres如何在复合键中使用第二列进行扫描。我失败了!它使用的是第一列,但不是第二列!完全混淆我玩了一些,发现当索引比表格小6.5倍时,它开始使用第二列:
create table so2 (a int not null,b int not null, c text, d int not null);
with l as (select generate_series(999,999+76,1) r)
insert into so2
select l.r,l.r+1,concat('l',lpad('o',l.r,'o'),'ng'),1 from l;
;
alter table so2 ADD CONSTRAINT so2pk PRIMARY KEY (a,b);
analyze so2;
t=# explain analyze select 42 from so2 where a=1004;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------
Index Only Scan using so2pk on so2 (cost=0.14..8.16 rows=1 width=0) (actual time=0.013..0.013 rows=1 loops=1)
Index Cond: (a = 1004)
Heap Fetches: 1
Planning time: 0.090 ms
Execution time: 0.026 ms
(5 rows)
t=# explain analyze select 42 from so2 where b=1004;
QUERY PLAN
----------------------------------------------------------------------------------------------
Seq Scan on so2 (cost=0.00..11.96 rows=1 width=0) (actual time=0.006..0.028 rows=1 loops=1)
Filter: (b = 1004)
Rows Removed by Filter: 76
Planning time: 0.045 ms
Execution time: 0.036 ms
(5 rows)
然后我放弃so2并重新运行使用999+77
准备部分,而不是999+76
并计划b列更改:
t=# explain analyze select 42 from so2 where b=1004;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
Index Only Scan using so2pk on so2 (cost=0.14..12.74 rows=1 width=0) (actual time=0.004..0.004 rows=1 loops=1)
Index Cond: (b = 1004)
Heap Fetches: 1
Planning time: 0.038 ms
Execution time: 0.013 ms
(5 rows)
我注意到的唯一区别是关系需要的页数:
令人困惑的计划'大小
t=# \dt+ so2
List of relations
Schema | Name | Type | Owner | Size | Description
--------+------+-------+-------+--------+-------------
public | so2 | table | vao | 120 kB |
(1 row)
预期的大小:
t=# \dt+ so2
List of relations
Schema | Name | Type | Owner | Size | Description
--------+------+-------+-------+--------+-------------
public | so2 | table | vao | 128 kB |
(1 row)
两种情况下的指数相同:
t=# \di+ so2pk
List of relations
Schema | Name | Type | Owner | Table | Size | Description
--------+-------+-------+-------+-------+-------+-------------
public | so2pk | index | vao | so2 | 16 kB |
(1 row)
select name,setting
from pg_settings
where source != 'default' and name in (
'enable_bitmapscan',
'enable_hashagg',
'enable_hashjoin',
'enable_indexscan',
'enable_indexonlyscan',
'enable_material',
'enable_mergejoin',
'enable_nestloop',
'enable_seqscan',
'enable_sort',
'enable_tidscan',
'seq_page_cost',
'random_page_cost',
'cpu_tuple_cost',
'cpu_index_tuple_cost',
'cpu_operator_cost',
'effective_cache_size',
'geqo',
'geqo_threshold',
'geqo_effort',
'geqo_pool_size',
'geqo_generations',
'geqo_selection_bias',
'geqo_seed',
'join_collapse_limit',
'from_collapse_limit',
'cursor_tuple_fraction',
'constraint_exclusion',
'default_statistics_target'
) order by name
;
name | setting
------+---------
(0 rows)
试用了几个版本:9.3.10,9.5.4具有相同的行为
更新以反映e4c5肯定言论
此外: 有一秒钟,我认为可能是因为文本列保留在扩展的句子中,所以表本身占用的页面数量与索引相同(所有列只有文本一行),因此我将其更改为main和plain - 没有效果......
答案 0 :(得分:1)
我认为这里的关键区别在于mysql每个表只能使用一个索引,因为postgresql没有这个限制。可以使用多个索引,这可能是they say
的原因应谨慎使用多列索引。在大多数情况下, 单列上的索引就足够了,节省了空间和时间。 除非有以下情况,否则超过三列的索引不太可能有用 表的用法非常风格化。
其他一些指示
1)您的数据太少,无法得出任何结论。是的,查询计划在很大程度上取决于大小 - 表中的行数。第一个存储的函数只创建了62行,为此您不需要索引。
2)您搜索a = 4的值不在表中。
3)b的值始终为1,因此该列的索引将无用。即使是综合指数也不会给它非常高的基数。即(a,b)上的复合索引与
上的索引完全相同更新
在行数的沙子阈值中没有行,或者以KB为单位的索引生效。查询计划程序根据https://www.postgresql.org/docs/9.2/static/runtime-config-query.html
中描述的几个配置因素决定是否使用索引和要使用的索引