下面是两个postgres查询,它们几乎相同,但产生的查询计划和执行时间截然不同。我假设第一个查询很快,因为只有196个form_instance记录form_id="W40",而有7000个form_id="W30L"。但是,为什么从200条记录跳到7000条记录(对我来说这似乎相对较小)会导致查询时间如此惊人地增加呢?我尝试过以各种方式对数据进行索引以加快速度,但基本上都很困惑。我该如何加快速度?(请注意,这两个表的模式都包含在底部)。
explain analyze select form_id,form_instance_id,answer,field_id
from form_instances,field_instances
where workflow_state = 'DRqueued' and form_instance_id = form_instances.id
and field_id in ('Book_EstimatedDueDate','H_SubmittedDate','H_Ccode','miscarriage','miscarriage_of_multiple','stillbirth','AP_IUFD_of_multiple','maternal_death','birth_includes_transport','newborn_death','H_Pid','H_Mid1','H_Mid2','H_Mid3')
and (form_id = 'W40');
QUERY PLAN
Nested Loop (cost=0.00..70736.14 rows=4646 width=29) (actual time=0.000..20.000 rows=2399 loops=1)
-> Index Scan using form_id_and_workflow_state on form_instances (cost=0.00..1041.42 rows=507 width=8) (actual time=0.000..0.000 rows=196 loops=1)
Index Cond: (((form_id)::text = 'W40'::text) AND ((workflow_state)::text = 'DRqueued'::text))
-> Index Scan using index_field_instances_on_form_instance_id on field_instances (cost=0.00..137.25 rows=17 width=25) (actual time=0.000..0.102 rows=12 loops=196)
Index Cond: (field_instances.form_instance_id = form_instances.id)
Filter: ((field_instances.field_id)::text = ANY ('{Book_EstimatedDueDate,H_SubmittedDate,H_Ccode,miscarriage,miscarriage_of_multiple,stillbirth,AP_IUFD_of_multiple,maternal_death,birth_includes_transport,newborn_death,H_Pid,H_Mid1,H_Mid2,H_Mid3}'::text[]))
Total runtime: 30.000 ms
(7 rows)
explain analyze select form_id,form_instance_id,answer,field_id
from form_instances,field_instances
where workflow_state = 'DRqueued' and form_instance_id = form_instances.id
and field_id in ('Book_EstimatedDueDate','H_SubmittedDate','H_Ccode','miscarriage','miscarriage_of_multiple','stillbirth','AP_IUFD_of_multiple','maternal_death','birth_includes_transport','newborn_death','H_Pid','H_Mid1','H_Mid2','H_Mid3')
and (form_id = 'W30L');
QUERY PLAN
Hash Join (cost=34300.46..160865.40 rows=31045 width=29) (actual time=65670.000..74960.000 rows=102777 loops=1)
Hash Cond: (field_instances.form_instance_id = form_instances.id)
-> Bitmap Heap Scan on field_instances (cost=29232.57..152163.82 rows=531718 width=25) (actual time=64660.000..72800.000 rows=526842 loops=1)
Recheck Cond: ((field_id)::text = ANY ('{Book_EstimatedDueDate,H_SubmittedDate,H_Ccode,miscarriage,miscarriage_of_multiple,stillbirth,AP_IUFD_of_multiple,maternal_death,birth_includes_transport,newborn_death,H_Pid,H_Mid1,H_Mid2,H_Mid3}'::text[]))
-> Bitmap Index Scan on index_field_instances_on_field_id (cost=0.00..29099.64 rows=531718 width=0) (actual time=64630.000..64630.000 rows=594515 loops=1)
Index Cond: ((field_id)::text = ANY ('{Book_EstimatedDueDate,H_SubmittedDate,H_Ccode,miscarriage,miscarriage_of_multiple,stillbirth,AP_IUFD_of_multiple,maternal_death,birth_includes_transport,newborn_death,H_Pid,H_Mid1,H_Mid2,H_Mid3}'::text[]))
-> Hash (cost=5025.54..5025.54 rows=3388 width=8) (actual time=980.000..980.000 rows=10457 loops=1)
-> Bitmap Heap Scan on form_instances (cost=90.99..5025.54 rows=3388 width=8) (actual time=10.000..950.000 rows=10457 loops=1)
Recheck Cond: (((form_id)::text = 'W30L'::text) AND ((workflow_state)::text = 'DRqueued'::text))
-> Bitmap Index Scan on form_id_and_workflow_state (cost=0.00..90.14 rows=3388 width=0) (actual time=0.000..0.000 rows=10457 loops=1)
Index Cond: (((form_id)::text = 'W30L'::text) AND ((workflow_state)::text = 'DRqueued'::text))
Total runtime: 75080.000 ms
# d form_instances Table "public.form_instances" Column | Type | Modifiers
-----------------+-----------------------------+-------------------------------------------------------------
id | integer | not null default nextval('form_instances_id_seq'::regclass)
form_id | character varying(255) |
created_at | timestamp without time zone |
updated_at | timestamp without time zone |
created_by_id | integer |
updated_by_id | integer |
workflow | character varying(255) |
workflow_state | character varying(255) |
validation_data | text |
Indexes:
"form_instances_pkey" PRIMARY KEY, btree (id)
"form_id_and_workflow_state" btree (form_id, workflow_state)
"index_form_instances_on_form_id" btree (form_id)
"index_form_instances_on_workflow_state" btree (workflow_state)
# d field_instances
Table "public.field_instances"
Column | Type | Modifiers
------------------+-----------------------------+--------------------------------------------------------------
id | integer | not null default nextval('field_instances_id_seq'::regclass)
form_instance_id | integer |
created_at | timestamp without time zone |
updated_at | timestamp without time zone |
created_by_id | integer |
updated_by_id | integer |
field_id | character varying(255) |
answer | text |
state | character varying(255) |
explanation | text |
idx | integer | not null default 0
Indexes:
"field_instances_pkey" PRIMARY KEY, btree (id)
"field_instances__lower_answer" btree (lower(answer))
"index_field_instances_on_answer" btree (answer)
"index_field_instances_on_field_id" btree (field_id)
"index_field_instances_on_field_id_and_answer" btree (field_id, answer)
"index_field_instances_on_form_instance_id" btree (form_instance_id)
"index_field_instances_on_idx" btree (idx)
以前是一条评论,但由于它似乎已经解决了问题,我将提出一个实际的答案。
系统对可能有多少行的估计是错误的。我们可以看到,在第二个查询中,它从位图索引扫描中估计了3388行,但实际上得到了10457行。
所以你可能想要vacuum full analyze;
另外,可以显著帮助的其他命令包括reindex
和/或cluster
。
OP表示vacuum
没有帮助,但reindex
有帮助。
我不确定摘要中的数字来自哪里,因为您发布的第二个查询计划输出102777行,而第一个计划输出2399行。这是行数的43倍,所以选择一个非常不同的查询计划并不奇怪。至于为什么运行时差异甚至更大,优化器在估计form_id和workflow_state上的过滤器的敏感度时犯了一个中等错误。您可能希望增加该数据库的default_statistics_target值,然后再次运行ANALYZE,如果您使用的是PostgreSQL 8.3,则默认值非常低,这一点尤其正确。有关该参数的详细信息,请参阅Tuning Your PostgreSQL Server。
很可能两者之间的差异如此之大,仅仅是因为回答小查询所需的所有数据都已经存在于内存中,而回答大查询则需要更多的磁盘访问权限。如果在将数据读取到缓存中后运行时间有所改善,则多次运行每个查询可能会有一些启示。您所做的REINDEX本可以将索引缩小到足以在这两种情况下放入缓存的程度,从而暂时解决问题。不过,该指数可能会再次"膨胀"。