Postgres 当字段为 TRUE 时查询快,当字段为 FALSE 时查询慢

问题描述 投票:0回答:1

我有一个

tickets
表(Heroku 上的 Postgres),其中包含大约 700 万条记录。

我有一个经常使用

exported
字段 true 或 false 运行的单表查询。 当它为 true 时,它运行得非常快(~30ms)。 当为 false 时,速度非常慢(~7000ms)。

我已经重建了索引并运行了

VACCUM
。看起来它只是无法利用索引。 具体来说,这个:

CREATE INDEX index_tickets_owner_exported_deleted_archived_created
   ON public.tickets USING btree
   (owner_id, deleted_at, exported, archived, created_at);

这是使用

EXPLAIN
进行双向查询:

慢:导出 = false 需要 7 秒

explain (buffers, analyze, verbose)
SELECT  "tickets".*
FROM "tickets"
WHERE "tickets"."deleted_at" IS NULL
  AND "tickets"."owner_id" = 1211
  AND "tickets"."exported" = false
  AND "tickets"."archived" = false
ORDER BY tickets.created_at desc
LIMIT 200 OFFSET 0;

 Limit  (cost=0.09..1352.68 rows=200 width=1054) (actual time=20.736..7755.424 rows=161 loops=1)
   Output: id, order_id, number, gross_amount, tare_amount, status, completed_at, canceled_at, created_at, updated_at, net_amount, driver_id, plan_item_id, verified_at, verifier_id, truck_number, creator_id, is_flagged, company_id, signer_id, started_at, haul_type, deleted_at, terms_url, ruckit_trip_id, origin_name, paper_ticket_image_url, created_at_latitude, completed_at_latitude, created_at_longitude, completed_at_longitude, material_name, material_unit, loading_location_name, unloading_location_name, signed_at, signed_at_latitude, signed_at_longitude, cost_code, hours, signed_at_timezone, concrete_mix_code, concrete_type, concrete_inspection_rejected, concrete_pump_wash_out_bags, concrete_pump_primer, verifier_type, verifier_note, exported, exported_at, exported_guid, group_tag, multi_page_tag, multi_page_pdf_url, material_invoice_item_id, trucking_invoice_item_id, original_created_at, source, rotated, held_for_questioning, approver_id, approved_at, approval_note, approved, trucking_invoice_status, material_invoice_status, qa_air, qa_slump, qa_temp, qc_air, qc_slump, qc_temp, o_air, o_slump, o_temp, ocr_complete, note, cycle_time_data, total_cost, connex_exported_at, connex_export_id, black_flagged, driver_name, owner_id, custom_field_1, custom_field_2, custom_field_3, custom_field_4, site_list, verification_locked_at, archived, inventory_status, ocr_behavior, ocr_behavior_set_date, last_ruckit_update_at, hauler_lookup_status, hauler_id, plan_item_lookup_status, inventory_transaction_id, paper_ticket_date, project_id
   Buffers: shared hit=4674873
   ->  Index Scan Backward using index_tickets_on_created_at on public.tickets  (cost=0.09..910705.14 rows=134661 width=1054) (actual time=20.734..7755.402 rows=161 loops=1)
         Output: id, order_id, number, gross_amount, tare_amount, status, completed_at, canceled_at, created_at, updated_at, net_amount, driver_id, plan_item_id, verified_at, verifier_id, truck_number, creator_id, is_flagged, company_id, signer_id, started_at, haul_type, deleted_at, terms_url, ruckit_trip_id, origin_name, paper_ticket_image_url, created_at_latitude, completed_at_latitude, created_at_longitude, completed_at_longitude, material_name, material_unit, loading_location_name, unloading_location_name, signed_at, signed_at_latitude, signed_at_longitude, cost_code, hours, signed_at_timezone, concrete_mix_code, concrete_type, concrete_inspection_rejected, concrete_pump_wash_out_bags, concrete_pump_primer, verifier_type, verifier_note, exported, exported_at, exported_guid, group_tag, multi_page_tag, multi_page_pdf_url, material_invoice_item_id, trucking_invoice_item_id, original_created_at, source, rotated, held_for_questioning, approver_id, approved_at, approval_note, approved, trucking_invoice_status, material_invoice_status, qa_air, qa_slump, qa_temp, qc_air, qc_slump, qc_temp, o_air, o_slump, o_temp, ocr_complete, note, cycle_time_data, total_cost, connex_exported_at, connex_export_id, black_flagged, driver_name, owner_id, custom_field_1, custom_field_2, custom_field_3, custom_field_4, site_list, verification_locked_at, archived, inventory_status, ocr_behavior, ocr_behavior_set_date, last_ruckit_update_at, hauler_lookup_status, hauler_id, plan_item_lookup_status, inventory_transaction_id, paper_ticket_date, project_id
         Filter: ((tickets.deleted_at IS NULL) AND (NOT tickets.exported) AND (NOT tickets.archived) AND (tickets.owner_id = 1211))
         Rows Removed by Filter: 6780764
         Buffers: shared hit=4674873
 Query Identifier: -4426566847541555156
 Planning Time: 0.456 ms
 Execution Time: 7755.497 ms

快速导出 = true

explain (buffers, analyze, verbose)
SELECT  "tickets".*
FROM "tickets"
WHERE "tickets"."deleted_at" IS NULL
  AND "tickets"."owner_id" = 1211
  AND "tickets"."exported" = true
  AND "tickets"."archived" = false
ORDER BY tickets.created_at desc
LIMIT 200 OFFSET 0;

Limit  (cost=0.09..3790.43 rows=200 width=1054) (actual time=30.772..32.322 rows=200 loops=1)
   Output: id, order_id, number, gross_amount, tare_amount, status, completed_at, canceled_at, created_at, updated_at, net_amount, driver_id, plan_item_id, verified_at, verifier_id, truck_number, creator_id, is_flagged, company_id, signer_id, started_at, haul_type, deleted_at, terms_url, ruckit_trip_id, origin_name, paper_ticket_image_url, created_at_latitude, completed_at_latitude, created_at_longitude, completed_at_longitude, material_name, material_unit, loading_location_name, unloading_location_name, signed_at, signed_at_latitude, signed_at_longitude, cost_code, hours, signed_at_timezone, concrete_mix_code, concrete_type, concrete_inspection_rejected, concrete_pump_wash_out_bags, concrete_pump_primer, verifier_type, verifier_note, exported, exported_at, exported_guid, group_tag, multi_page_tag, multi_page_pdf_url, material_invoice_item_id, trucking_invoice_item_id, original_created_at, source, rotated, held_for_questioning, approver_id, approved_at, approval_note, approved, trucking_invoice_status, material_invoice_status, qa_air, qa_slump, qa_temp, qc_air, qc_slump, qc_temp, o_air, o_slump, o_temp, ocr_complete, note, cycle_time_data, total_cost, connex_exported_at, connex_export_id, black_flagged, driver_name, owner_id, custom_field_1, custom_field_2, custom_field_3, custom_field_4, site_list, verification_locked_at, archived, inventory_status, ocr_behavior, ocr_behavior_set_date, last_ruckit_update_at, hauler_lookup_status, hauler_id, plan_item_lookup_status, inventory_transaction_id, paper_ticket_date, project_id
   Buffers: shared hit=22462
   ->  Index Scan Backward using index_tickets_on_created_at on public.tickets  (cost=0.09..910705.14 rows=48054 width=1054) (actual time=30.771..32.297 rows=200 loops=1)
         Output: id, order_id, number, gross_amount, tare_amount, status, completed_at, canceled_at, created_at, updated_at, net_amount, driver_id, plan_item_id, verified_at, verifier_id, truck_number, creator_id, is_flagged, company_id, signer_id, started_at, haul_type, deleted_at, terms_url, ruckit_trip_id, origin_name, paper_ticket_image_url, created_at_latitude, completed_at_latitude, created_at_longitude, completed_at_longitude, material_name, material_unit, loading_location_name, unloading_location_name, signed_at, signed_at_latitude, signed_at_longitude, cost_code, hours, signed_at_timezone, concrete_mix_code, concrete_type, concrete_inspection_rejected, concrete_pump_wash_out_bags, concrete_pump_primer, verifier_type, verifier_note, exported, exported_at, exported_guid, group_tag, multi_page_tag, multi_page_pdf_url, material_invoice_item_id, trucking_invoice_item_id, original_created_at, source, rotated, held_for_questioning, approver_id, approved_at, approval_note, approved, trucking_invoice_status, material_invoice_status, qa_air, qa_slump, qa_temp, qc_air, qc_slump, qc_temp, o_air, o_slump, o_temp, ocr_complete, note, cycle_time_data, total_cost, connex_exported_at, connex_export_id, black_flagged, driver_name, owner_id, custom_field_1, custom_field_2, custom_field_3, custom_field_4, site_list, verification_locked_at, archived, inventory_status, ocr_behavior, ocr_behavior_set_date, last_ruckit_update_at, hauler_lookup_status, hauler_id, plan_item_lookup_status, inventory_transaction_id, paper_ticket_date, project_id
         Filter: ((tickets.deleted_at IS NULL) AND tickets.exported AND (NOT tickets.archived) AND (tickets.owner_id = 1211))
         Rows Removed by Filter: 28056
         Buffers: shared hit=22462
 Query Identifier: -4426566847541555156
 Planning Time: 0.432 ms
 Execution Time: 32.383 ms

这是该表上的索引

schemaname | tablename |                       indexname                       | tablespace |                                                                        indexdef
------------+-----------+-------------------------------------------------------+------------+---------------------------------------------------------------------------------------------------------------------------------------------------------
 public     | tickets   | tickets_pkey                                          |            | CREATE UNIQUE INDEX tickets_pkey ON public.tickets USING btree (id)
 public     | tickets   | index_tickets_on_approver_id                          |            | CREATE INDEX index_tickets_on_approver_id ON public.tickets USING btree (approver_id)
 public     | tickets   | index_tickets_on_archived                             |            | CREATE INDEX index_tickets_on_archived ON public.tickets USING btree (archived)
 public     | tickets   | index_tickets_on_company_id                           |            | CREATE INDEX index_tickets_on_company_id ON public.tickets USING btree (company_id)
 public     | tickets   | index_tickets_on_connex_export_id                     |            | CREATE INDEX index_tickets_on_connex_export_id ON public.tickets USING btree (connex_export_id)
 public     | tickets   | index_tickets_on_cost_code                            |            | CREATE INDEX index_tickets_on_cost_code ON public.tickets USING btree (cost_code)
 public     | tickets   | index_tickets_on_created_at                           |            | CREATE INDEX index_tickets_on_created_at ON public.tickets USING btree (created_at)
 public     | tickets   | index_tickets_on_custom_field_1                       |            | CREATE INDEX index_tickets_on_custom_field_1 ON public.tickets USING btree (custom_field_1)
 public     | tickets   | index_tickets_on_custom_field_2                       |            | CREATE INDEX index_tickets_on_custom_field_2 ON public.tickets USING btree (custom_field_2)
 public     | tickets   | index_tickets_on_deleted_at                           |            | CREATE INDEX index_tickets_on_deleted_at ON public.tickets USING btree (deleted_at)
 public     | tickets   | index_tickets_on_driver_id                            |            | CREATE INDEX index_tickets_on_driver_id ON public.tickets USING btree (driver_id)
 public     | tickets   | index_tickets_on_exported                             |            | CREATE INDEX index_tickets_on_exported ON public.tickets USING btree (exported)
 public     | tickets   | index_tickets_on_group_tag                            |            | CREATE INDEX index_tickets_on_group_tag ON public.tickets USING btree (group_tag)
 public     | tickets   | index_tickets_on_hauler_id                            |            | CREATE INDEX index_tickets_on_hauler_id ON public.tickets USING btree (hauler_id)
 public     | tickets   | index_tickets_on_hauler_lookup_status                 |            | CREATE INDEX index_tickets_on_hauler_lookup_status ON public.tickets USING btree (hauler_lookup_status)
 public     | tickets   | index_tickets_on_held_for_questioning                 |            | CREATE INDEX index_tickets_on_held_for_questioning ON public.tickets USING btree (held_for_questioning)
 public     | tickets   | index_tickets_on_inventory_status                     |            | CREATE INDEX index_tickets_on_inventory_status ON public.tickets USING btree (inventory_status)
 public     | tickets   | index_tickets_on_inventory_transaction_id             |            | CREATE INDEX index_tickets_on_inventory_transaction_id ON public.tickets USING btree (inventory_transaction_id)
 public     | tickets   | index_tickets_on_is_flagged                           |            | CREATE INDEX index_tickets_on_is_flagged ON public.tickets USING btree (is_flagged)
 public     | tickets   | index_tickets_on_material_invoice_item_id             |            | CREATE INDEX index_tickets_on_material_invoice_item_id ON public.tickets USING btree (material_invoice_item_id)
 public     | tickets   | index_tickets_on_multi_page_tag                       |            | CREATE INDEX index_tickets_on_multi_page_tag ON public.tickets USING btree (multi_page_tag)
 public     | tickets   | index_tickets_on_number                               |            | CREATE INDEX index_tickets_on_number ON public.tickets USING btree (number)
 public     | tickets   | index_tickets_on_order_id                             |            | CREATE INDEX index_tickets_on_order_id ON public.tickets USING btree (order_id)
 public     | tickets   | index_tickets_on_original_created_at                  |            | CREATE INDEX index_tickets_on_original_created_at ON public.tickets USING btree (original_created_at)
 public     | tickets   | index_tickets_on_owner_id                             |            | CREATE INDEX index_tickets_on_owner_id ON public.tickets USING btree (owner_id)
 public     | tickets   | index_tickets_on_owner_id_and_deleted_at_and_archived |            | CREATE INDEX index_tickets_on_owner_id_and_deleted_at_and_archived ON public.tickets USING btree (owner_id, deleted_at, archived)
 public     | tickets   | index_tickets_on_plan_item_id                         |            | CREATE INDEX index_tickets_on_plan_item_id ON public.tickets USING btree (plan_item_id)
 public     | tickets   | index_tickets_on_plan_item_lookup_status              |            | CREATE INDEX index_tickets_on_plan_item_lookup_status ON public.tickets USING btree (plan_item_lookup_status)
 public     | tickets   | index_tickets_on_rotated                              |            | CREATE INDEX index_tickets_on_rotated ON public.tickets USING btree (rotated)
 public     | tickets   | index_tickets_on_ruckit_trip_id                       |            | CREATE INDEX index_tickets_on_ruckit_trip_id ON public.tickets USING btree (ruckit_trip_id)
 public     | tickets   | index_tickets_on_signer_id                            |            | CREATE INDEX index_tickets_on_signer_id ON public.tickets USING btree (signer_id)
 public     | tickets   | index_tickets_on_trucking_invoice_item_id             |            | CREATE INDEX index_tickets_on_trucking_invoice_item_id ON public.tickets USING btree (trucking_invoice_item_id)
 public     | tickets   | index_tickets_on_verification_locked_at               |            | CREATE INDEX index_tickets_on_verification_locked_at ON public.tickets USING btree (verification_locked_at)
 public     | tickets   | index_tickets_on_verified_at                          |            | CREATE INDEX index_tickets_on_verified_at ON public.tickets USING btree (verified_at)
 public     | tickets   | index_tickets_on_verifier_id_and_verifier_type        |            | CREATE INDEX index_tickets_on_verifier_id_and_verifier_type ON public.tickets USING btree (verifier_id, verifier_type)
 public     | tickets   | index_tickets_on_approved                             |            | CREATE INDEX index_tickets_on_approved ON public.tickets USING btree (approved)
 public     | tickets   | index_tickets_on_project_id                           |            | CREATE INDEX index_tickets_on_project_id ON public.tickets USING btree (project_id)
 public     | tickets   | index_tickets_owner_exported_deleted_archived_created |            | CREATE INDEX index_tickets_owner_exported_deleted_archived_created ON public.tickets USING btree (owner_id, deleted_at, exported, archived, created_at)

我感谢你们提供的任何帮助。

ruby-on-rails postgresql heroku-postgres
1个回答
0
投票

您可以采取两种方法:

  1. 改进估计。

    首先,

    ANALYZE
    查看表格,看看这是否会产生影响。如果是,您需要调整 autovacuum 以便它更频繁地分析表:

    ALTER TABLE tickets SET (autovacuum_analyze_scale_factor = 0.01);
    

    如果这没有效果,您可以尝试收集更详细的统计数据。您可以使用

    ALTER TABLE ... ALTER COLUMN ... SET STATISTICS ...
    使 PostgreSQL 计算出更多“最常见的值”和更详细的直方图。然后再次
    ANALYZE
    桌子,看看你是否能有所作为。

    最后,还有扩展统计数据来涵盖函数依赖性:

    CREATE STATISTICS mystats
    ON owner_id, exported, archived, deleted_at
    FROM tickets;
    
    ANALYZE tickets;
    

    如果这些都没有帮助:

  2. 您可以重写

    ORDER BY
    条件,使PostgreSQL无法使用
    created_at
    上的索引:

    ORDER BY created_at + INTERVAL '0' DESC
    

    然后优化器必须求助于其他索引。

正如其他人提到的,您可能在该表上有太多索引。

© www.soinside.com 2019 - 2024. All rights reserved.