【发布时间】:2018-08-13 23:42:14
【问题描述】:
我希望得到一些专家的关注,看看我的查询为什么会收到不同的性能。
我要解决的问题是我需要可以包含一对多项目的订单。这些订单需要分页。
为此,我采用了以下方法。我正在使用子查询按所需的项目属性过滤订单。然后,我将重新加入这些项目以获取其必填字段。这意味着在分页时,当订单包含 2 个或更多商品时,我不会错误地过滤订单行。
我看到间歇性缓慢的查询。第二次运行时,它们的速度要快得多。我想这是因为 Postgres 正在将索引等加载到内存中?
从解释中我不完全理解发生了什么。看起来它需要扫描每个订单以查看他们是否有适合子查询的项目?我对以下行有点困惑。是说它需要扫描 286853 行但也只有 165 行?
Index Scan Backward using orders_created_at_idx on orders (cost=0.42..2708393.65 rows=286853 width=301) (actual time=64.598..2114.676 rows=165 loops=1)
有没有办法让 Postgres 先按项目过滤,还是我读错了,它已经这样做了?
查询:
SELECT
"orders"."id_orders" as "orders.id_orders",
"items"."id_items" as "items"."id_items",
...,
orders.created_at, orders.updated_at
FROM (
SELECT
orders.id_orders,
orders.created_at,
orders.updated_at
FROM orders
WHERE orders.status in ('completed','pending') AND
(
SELECT fk_vendor_id FROM items
WHERE (
items.fk_order_id = orders.id_orders AND
items.fk_vendor_id = '0012800001YVccUAAT' AND
items.fk_offer = '0060I00000RAKFYQA5' AND
items.status IN ('completed','cancelled')
) LIMIT 1
) IS NOT NULL ORDER BY orders.created_at DESC LIMIT 50 OFFSET 150
) as orders INNER JOIN items ON items.fk_order_id = orders.id_orders;
第一次解释:
Nested Loop (cost=1417.11..2311.77 rows=67 width=1705) (actual time=2785.221..17025.325 rows=17 loops=1)
-> Limit (cost=1416.68..1888.77 rows=50 width=301) (actual time=2785.216..17024.918 rows=15 loops=1)
-> Index Scan Backward using orders_created_at_idx on orders (cost=0.42..2708393.65 rows=286853 width=301) (actual time=1214.013..17024.897 rows=165 loops=1)
Filter: ((status = ANY ('{completed,pending}'::orders_status_enum[])) AND ((SubPlan 1) IS NOT NULL))
Rows Removed by Filter: 313631
SubPlan 1
-> Limit (cost=0.42..8.45 rows=1 width=19) (actual time=0.047..0.047 rows=0 loops=287719)
-> Index Scan using items_fk_order_id_index on items items_1 (cost=0.42..8.45 rows=1 width=19) (actual time=0.047..0.047 rows=0 loops=287719)
Index Cond: (fk_order_id = orders.id_orders)
Filter: ((status = ANY ('{completed,cancelled}'::items_status_enum[])) AND (fk_vendor_id = '0012800001YVccUAAT'::text) AND (fk_offer = '0060I00000RAKFYQA5'::text))
Rows Removed by Filter: 1
-> Index Scan using items_fk_order_id_index on items (cost=0.42..8.44 rows=1 width=1404) (actual time=0.002..0.026 rows=1 loops=15)
Index Cond: (fk_order_id = orders.id_orders)
Planning time: 1.791 ms
Execution time: 17025.624 ms
(15 rows)
第二次解释:
Nested Loop (cost=1417.11..2311.77 rows=67 width=1705) (actual time=115.659..2114.739 rows=17 loops=1)
-> Limit (cost=1416.68..1888.77 rows=50 width=301) (actual time=115.654..2114.691 rows=15 loops=1)
-> Index Scan Backward using orders_created_at_idx on orders (cost=0.42..2708393.65 rows=286853 width=301) (actual time=64.598..2114.676 rows=165 loops=1)
Filter: ((status = ANY ('{completed,pending}'::orders_status_enum[])) AND ((SubPlan 1) IS NOT NULL))
Rows Removed by Filter: 313631
SubPlan 1
-> Limit (cost=0.42..8.45 rows=1 width=19) (actual time=0.006..0.006 rows=0 loops=287719)
-> Index Scan using items_fk_order_id_index on items items_1 (cost=0.42..8.45 rows=1 width=19) (actual time=0.006..0.006 rows=0 loops=287719)
Index Cond: (fk_order_id = orders.id_orders)
Filter: ((status = ANY ('{completed,cancelled}'::items_status_enum[])) AND (fk_vendor_id = '0012800001YVccUAAT'::text) AND (fk_offer = '0060I00000RAKFYQA5'::text))
Rows Removed by Filter: 1
-> Index Scan using items_fk_order_id_index on items (cost=0.42..8.44 rows=1 width=1404) (actual time=0.002..0.002 rows=1 loops=15)
Index Cond: (fk_order_id = orders.id_orders)
Planning time: 2.011 ms
Execution time: 2115.052 ms
(15 rows)
订单索引:
"cart_pkey" PRIMARY KEY, btree (id_orders)
"orders_legacy_id_uindex" UNIQUE, btree (legacy_id_orders)
"orders_transaction_key_uindex" UNIQUE, btree (transaction_key)
"orders_created_at_idx" btree (created_at)
"orders_customer_email_idx" gin (customer_email gin_trgm_ops)
"orders_customer_full_name_idx" gin (customer_full_name gin_trgm_ops)
Referenced by:
TABLE "items" CONSTRAINT "items_fk_order_id_fkey" FOREIGN KEY (fk_order_id) REFERENCES orders(id_orders) ON DELETE RESTRICT
TABLE "items_log" CONSTRAINT "items_log_fk_order_id_fkey" FOREIGN KEY (fk_order_id) REFERENCES orders(id_orders)
项目索引:
"items_pkey" PRIMARY KEY, btree (id_items)
"items_fk_vendor_id_booking_number_unique" UNIQUE, btree (fk_vendor_id, booking_number) WHERE legacy_id_items IS NULL
"items_legacy_id_uindex" UNIQUE, btree (legacy_id_items)
"items_transaction_key_uindex" UNIQUE, btree (transaction_key)
"items_booking_number_index" btree (booking_number)
"items_fk_order_id_index" btree (fk_order_id)
"items_fk_vendor_id_index" btree (fk_vendor_id)
"items_status_index" btree (status)
Foreign-key constraints:
"items_fk_order_id_fkey" FOREIGN KEY (fk_order_id) REFERENCES orders(id_orders) ON DELETE RESTRICT
【问题讨论】:
-
我自己的理解有些有限,但我会沿着您指出的“索引向后扫描”行的方向前进,这是优化器期望为查询返回的内容与实际返回的内容。看看查询提示,看看你能不能告诉安静的星球你只从 orders_created_at_idx 表中返回了几行
标签: sql postgresql optimization query-optimization