moe*_*eth 7 postgresql query-optimization
我有ad_item包含以下列的postgresql表。
id | name | remaining | created_at
Run Code Online (Sandbox Code Playgroud)
然后我的剩余指数小于或等于 300。
create index remaining_index on ad_item using btree(id) where remaining <= 300
Run Code Online (Sandbox Code Playgroud)
但是当我做explain analyze我的查询时。
explain analyze select id from ad_item where remaining <= 300
Run Code Online (Sandbox Code Playgroud)
它向我展示了以下内容。
Seq Scan on ad_item (cost=0.00..2.06 rows=1 width=4) (actual time=0.010..0.013 rows=1 loops=1)
Filter: (remaining <= '300'::numeric)
Rows Removed by Filter: 4
Planning time: 0.115 ms
Execution time: 0.026 ms
Run Code Online (Sandbox Code Playgroud)
为什么不使用我的remaining_index?那个索引是多余的吗?
谢谢。
当它开始使用你的索引时,你可以做一个非常 apx 的计算来得到这个想法。
t=# drop table s07;
DROP TABLE
t=# create table s07 (i int, r int, t text);
CREATE TABLE
t=# insert into s07 select 1,1,'some text';
INSERT 0 1
t=# insert into s07 select 2,2,'some text';
INSERT 0 1
t=# insert into s07 select 3,3,'some text';
INSERT 0 1
t=# insert into s07 select 4,4,'some text';
INSERT 0 1
t=# create index s07i on s07 (i);
CREATE INDEX
t=# analyze s07;
ANALYZE
t=# SELECT relname, relkind, reltuples, relpages FROM pg_class WHERE relname LIKE 's07%';
relname | relkind | reltuples | relpages
---------+---------+-----------+----------
s07 | r | 4 | 1
s07i | i | 4 | 2
(2 rows)
Run Code Online (Sandbox Code Playgroud)
尽管索引具有相同数量的行和较少的列,但对于少量数据,它实际上需要多两倍的空间!关系 -1 页,索引 -2,所以规划器执行 seq 扫描:
t=# explain analyze select i from s07 where i < 4;
QUERY PLAN
---------------------------------------------------------------------------------------------
Seq Scan on s07 (cost=0.00..1.05 rows=4 width=4) (actual time=0.003..0.004 rows=3 loops=1)
Filter: (i < 4)
Rows Removed by Filter: 1
Planning time: 0.086 ms
Execution time: 0.013 ms
(5 rows)
Run Code Online (Sandbox Code Playgroud)
所以在填充一些数据之后:
t=# insert into s07 select i,i,'some text' from generate_series(1,99,1) i;
INSERT 0 99
t=# analyze s07;
ANALYZE
t=# SELECT relname, relkind, reltuples, relpages FROM pg_class WHERE relname LIKE 's07%';
relname | relkind | reltuples | relpages
---------+---------+-----------+----------
s07 | r | 103 | 1
s07i | i | 103 | 2
(2 rows)
t=# explain analyze select i from s07 where i < 4;
QUERY PLAN
---------------------------------------------------------------------------------------------
Seq Scan on s07 (cost=0.00..2.29 rows=7 width=4) (actual time=0.008..0.016 rows=6 loops=1)
Filter: (i < 4)
Rows Removed by Filter: 97
Planning time: 0.119 ms
Execution time: 0.029 ms
(5 rows)
Run Code Online (Sandbox Code Playgroud)
相同的图片。所以放更多的数据:
t=# insert into s07 select i,i,'some text' from generate_series(1,299,1) i;
INSERT 0 299
t=# analyze s07;
ANALYZE
t=# SELECT relname, relkind, reltuples, relpages FROM pg_class WHERE relname LIKE 's07%';
relname | relkind | reltuples | relpages
---------+---------+-----------+----------
s07 | r | 402 | 3
s07i | i | 402 | 2
(2 rows)
t=# explain analyze select i from s07 where i < 4;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on s07 (cost=4.22..7.33 rows=9 width=4) (actual time=0.005..0.007 rows=9 loops=1)
Recheck Cond: (i < 4)
Heap Blocks: exact=1
-> Bitmap Index Scan on s07i (cost=0.00..4.21 rows=9 width=0) (actual time=0.002..0.002 rows=9 loops=1)
Index Cond: (i < 4)
Planning time: 0.099 ms
Execution time: 0.017 ms
(7 rows)
Run Code Online (Sandbox Code Playgroud)
使用条件索引,这样的计算当然会更复杂,但基本上在这里你可以看到,即使是 100 行从一页过滤,然后加载两页也更便宜。
现在当然你可以用 config 改变这个行为,例如当我们有 100 行时,如果你运行:
t=# set cpu_tuple_cost to 1;
SET
t=# set cpu_index_tuple_cost to 0.000001;
SET
t=# explain analyze select i from s07 where i < 4;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------
Index Only Scan using s07i on s07 (cost=0.14..15.16 rows=7 width=4) (actual time=0.012..0.014 rows=6 loops=1)
Index Cond: (i < 4)
Heap Fetches: 6
Planning time: 0.058 ms
Execution time: 0.028 ms
(5 rows)
Run Code Online (Sandbox Code Playgroud)
虽然页数用于 Seq 扫描:
t=# SELECT relname, relkind, reltuples, relpages FROM pg_class WHERE relname LIKE 's07%';
relname | relkind | reltuples | relpages
---------+---------+-----------+----------
s07 | r | 103 | 1
s07i | i | 103 | 2
(2 rows)
Run Code Online (Sandbox Code Playgroud)
当然你可以临时检查执行计划和时间 set enable_seqscan=off
甚至在文档中提到的 100 行对于 indes 扫描来说太少了:
从 100 行中选择 1 行几乎不会(索引的候选),因为 100 行可能适合单个磁盘页面,并且没有任何计划可以击败顺序获取 1 个磁盘页面。