我的相关部分中的数据库模式是有一个名为User的表,它有一个布尔字段Admin。这个字段Admin有一个索引。
前一天我将完整的生产数据库恢复到我的开发机器上,然后对数据库进行了非常小的更改,因此它们应该非常相似。
当我在开发机器上运行以下命令时,我得到了预期的结果:
EXPLAIN SELECT * FROM user WHERE admin IS TRUE;
Index Scan using index_user_on_admin on user (cost=0.00..9.14 rows=165 width=3658)
Index Cond: (admin = true)
Filter: (admin IS TRUE)
但是,当我在生产机器上运行完全相同的命令时,我得到了这个:
Seq Scan on user (cost=0.00..620794.93 rows=4966489 width=3871)
Filter: (admin IS TRUE)
因此,不是使用与查询完美匹配的精确索引,而是使用近500万行的顺序扫描!
然后我尝试运行EXPLAIN ANALYZE SELECT * FROM user WHERE admin IS TRUE;
,希望ANALYZE
能让Postgres意识到500万行的顺序扫描不如使用索引那么好,但这并没有改变任何东西
我还尝试运行REINDEX INDEX index_user_on_admin
以防索引损坏,没有任何好处。
最后,我致电VACUUM ANALYZE user
,并在短时间内解决了问题。
我对真空的主要理解是它用于回收浪费的空间。可能发生的事情会导致我的指数行为异常严重,以及为什么要真空修复它?
答案 0 :(得分:5)
通过更新计划程序使用的数据统计信息来确定运行查询的最佳方式,最有可能帮助ANALYZE
VACUUM ANALYZE
。 VACUUM
只按顺序运行这两个命令,ANALYZE
优先,ANALYZE
秒,但ANALYZE
本身可能足以提供帮助。
EXPLAIN
的{{1}}选项与ANALYZE
命令完全无关。它只会导致Postgres运行查询并报告实际运行时间,以便可以将它们与计划程序预测进行比较(EXPLAIN
,而ANALYZE
只显示查询计划以及计划员认为它将会是什么成本,但实际上并不运行查询)。所以EXPLAIN ANALYZE
没有帮助,因为它没有更新统计数据。 ANALYZE
和EXPLAIN ANALYZE
是两个完全不同的行为,恰好使用同一个词。
答案 1 :(得分:2)
PostgreSQL保留了许多关于表条件,索引条件,数据等的高级统计信息......有时可能会失去同步。运行VACUUM
将解决问题。
当您在开发时从头开始重新加载表时,它可能会产生相同的效果。
看看这个:
http://www.postgresql.org/docs/current/static/maintenance.html#VACUUM-FOR-STATISTICS
答案 2 :(得分:1)
partial index对您的问题似乎是一个很好的解决方案:
CREATE INDEX admin_users_ix ON users (admin)
WHERE admin IS TRUE;;
在同一个字段中索引很多元组是没有意义的。
答案 3 :(得分:1)
以下是我认为最可能的解释。
只有当返回非常少量的行时,你的索引才有用(顺便说一句,我不喜欢因为这个原因而索引bool--你可能会考虑使用部分索引,或者甚至添加where admin为true因为那样只会将你的索引保留在可能可用的情况下。)
如果超过iirc,表中10%的页面都要被检索,那么规划者可能会选择大量的顺序磁盘I / O而不是少量的随机磁盘I / O,因为这样你不必等待盘子转动。寻求速度是一个很大的问题,PostgreSQL倾向于尝试将其与从关系中检索的实际数据量进行平衡。
您收集的统计信息表明该表格比较小或者管理员的数量比您的用户数量多,因此规划人员使用错误的信息做出决定。
VACUUM ANALYZE做了三件事。首先,它冻结所有事务可见的元组,以便事务环绕不是问题。然后它将没有事务可见的元组分配为可用空间。这些都没有影响您的问题。然而,第三是它分析表并收集表的统计信息。请记住,这是随机抽样,因此有时可能会关闭。我的猜测是之前的运行,它抓住了很多管理员的页面,因此严重高估了系统管理员的数量。
这可能是仔细检查您的autovacuum设置的好时机,因为统计数据也可能在其他地方过时,但这远非确定。特别是,基于成本的真空设置有默认值,有时会使真空从未完全赶上。