dez*_*zso 17 postgresql performance index join primary-key
我有以下表格和索引定义:
CREATE TABLE munkalap (
munkalap_id serial PRIMARY KEY,
...
);
CREATE TABLE munkalap_lepes (
munkalap_lepes_id serial PRIMARY KEY,
munkalap_id integer REFERENCES munkalap (munkalap_id),
...
);
CREATE INDEX idx_munkalap_lepes_munkalap_id ON munkalap_lepes (munkalap_id);
Run Code Online (Sandbox Code Playgroud)
为什么在以下查询中没有使用 munkalap_id 上的任何索引?
EXPLAIN ANALYZE SELECT ml.* FROM munkalap m JOIN munkalap_lepes ml USING (munkalap_id);
QUERY PLAN
Hash Join (cost=119.17..2050.88 rows=38046 width=214) (actual time=0.824..18.011 rows=38046 loops=1)
Hash Cond: (ml.munkalap_id = m.munkalap_id)
-> Seq Scan on munkalap_lepes ml (cost=0.00..1313.46 rows=38046 width=214) (actual time=0.005..4.574 rows=38046 loops=1)
-> Hash (cost=78.52..78.52 rows=3252 width=4) (actual time=0.810..0.810 rows=3253 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 115kB
-> Seq Scan on munkalap m (cost=0.00..78.52 rows=3252 width=4) (actual time=0.003..0.398 rows=3253 loops=1)
Total runtime: 19.786 ms
Run Code Online (Sandbox Code Playgroud)
即使我添加过滤器也是一样的:
EXPLAIN ANALYZE SELECT ml.* FROM munkalap m JOIN munkalap_lepes ml USING (munkalap_id) WHERE NOT lezarva;
QUERY PLAN
Hash Join (cost=79.60..1545.79 rows=1006 width=214) (actual time=0.616..10.824 rows=964 loops=1)
Hash Cond: (ml.munkalap_id = m.munkalap_id)
-> Seq Scan on munkalap_lepes ml (cost=0.00..1313.46 rows=38046 width=214) (actual time=0.007..5.061 rows=38046 loops=1)
-> Hash (cost=78.52..78.52 rows=86 width=4) (actual time=0.587..0.587 rows=87 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 4kB
-> Seq Scan on munkalap m (cost=0.00..78.52 rows=86 width=4) (actual time=0.014..0.560 rows=87 loops=1)
Filter: (NOT lezarva)
Total runtime: 10.911 ms
Run Code Online (Sandbox Code Playgroud)
dbe*_*hur 29
很多人都听说过“顺序扫描不好”的指导,并试图将它们从他们的计划中消除,但事实并非如此简单。如果查询要覆盖表中的每一行,顺序扫描是获取这些行的最快方法。这就是您原来的连接查询使用 seq 扫描的原因,因为两个表中的所有行都是必需的。
在规划查询时,Postgres 的规划器会估计不同可能方案下各种操作(计算、顺序和随机 IO)的成本,并选择其估计成本最低的计划。当从旋转存储(磁盘)进行 IO 时,随机 IO 通常比顺序 IO 慢得多,random_page_cost 和 seq_page_cost的默认 pg 配置估计成本差异为 4:1。
在考虑使用索引与顺序扫描表的连接或过滤方法时,这些考虑因素会起作用。使用索引时,计划可能会通过索引快速找到一行,然后必须考虑随机块读取来解析行数据。对于添加了过滤谓词的第二个查询WHERE NOT lezarva,您可以在 EXPLAIN ANALYZE 结果中看到这如何影响规划估计。规划器估计连接产生 1006 行(与 964 的实际结果集非常接近)。考虑到较大的表 munkalap_lepes 包含大约 38K 行,规划器看到连接将必须访问表中大约 1006/38046 或 1/38 的行。它还知道平均行宽是 214 字节,一个块是 8K,所以大约有 38 行/块。
有了这些统计信息,规划器认为连接可能必须读取所有或大部分表的数据块。由于索引查找也不是免费的,并且扫描评估过滤条件的块的计算相对于 IO 非常便宜,规划器选择顺序扫描表并在计算 seq 扫描时避免索引开销和随机读取会更快。
在现实世界中,数据通常通过操作系统页面缓存在内存中可用,因此并非每个块读取都需要 IO。很难预测缓存对于给定查询的有效性,但 Pg 规划器确实使用了一些简单的启发式方法。配置值effective_cache_size通知招致实际IO成本的情形产生的规划者估计。较大的值将导致它估计随机 IO 的成本较低,因此可能会使其偏向于顺序扫描的索引驱动方法。
| 归档时间: |
|
| 查看次数: |
15024 次 |
| 最近记录: |