繁体   English   中英

Postgres 索引未使用正确的计划

[英]Postgres index not using correct plan

我的 postgresql 版本是 10.6。 我创建了一个索引,但它并不用于所有 where 子句条件检查。 以下是更多详细信息:

Create index concurrently ticket_created_at_portal_id_created_by_id_assigned_group_id_idx on ticket(created_at, portal_id, created_by_id, assigned_group);
EXPLAIN (analyze true, verbose true, costs true, buffers true, timing true ) select * from ticket where status is not null
and (assigned_group in ('447') or created_by_id in ('39731566'))
and portal_id=8
and created_at>='2020-12-07T03:00:10.973'
and created_at<='2021-02-05T03:00:10.973'
order by updated_at DESC limit 10;


                                                          QUERY PLAN                                                                                                                                                                                                                                                                       
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=18975.23..18975.25 rows=10 width=638) (actual time=278.340..278.345 rows=10 loops=1)
   Output: id, action, assigned_agent, assigned_at, assigned_group, attachments, closed_at, created_at, created_by_email, created_by_id, description, first_response_time, parent_id, portal_id, priority, reopened_at, resolution_id, resolved_at, resolved_by_id, resource_id, resource_type, source, status, subject, tags, ticket_category, ticket_id, ticket_sub_category, ticket_sub_sub_category, type, updated_at, custom_fields, updated_by_id, first_assigned_at, mode, sla_breached, agent_assist_tags, comm_vendor, from_email
   Buffers: shared hit=2280 read=3105
   ->  Sort  (cost=18975.23..18975.45 rows=87 width=638) (actual time=278.338..278.339 rows=10 loops=1)
         Output: id, action, assigned_agent, assigned_at, assigned_group, attachments, closed_at, created_at, created_by_email, created_by_id, description, first_response_time, parent_id, portal_id, priority, reopened_at, resolution_id, resolved_at, resolved_by_id, resource_id, resource_type, source, status, subject, tags, ticket_category, ticket_id, ticket_sub_category, ticket_sub_sub_category, type, updated_at, custom_fields, updated_by_id, first_assigned_at, mode, sla_breached, agent_assist_tags, comm_vendor, from_email
         Sort Key: ticket.updated_at DESC
         Sort Method: top-N heapsort  Memory: 33kB
         Buffers: shared hit=2280 read=3105
         ->  Bitmap Heap Scan on public.ticket  (cost=17855.76..18973.35 rows=87 width=638) (actual time=111.871..275.835 rows=1256 loops=1)
               Output: id, action, assigned_agent, assigned_at, assigned_group, attachments, closed_at, created_at, created_by_email, created_by_id, description, first_response_time, parent_id, portal_id, priority, reopened_at, resolution_id, resolved_at, resolved_by_id, resource_id, resource_type, source, status, subject, tags, ticket_category, ticket_id, ticket_sub_category, ticket_sub_sub_category, type, updated_at, custom_fields, updated_by_id, first_assigned_at, mode, sla_breached, agent_assist_tags, comm_vendor, from_email
               Recheck Cond: (((ticket.assigned_group = '447'::bigint) AND (ticket.portal_id = 8)) OR ((ticket.created_at >= '2020-12-07 03:00:10.973'::timestamp without time zone) AND (ticket.created_at <= '2021-02-05 03:00:10.973'::timestamp without time zone) AND (ticket.portal_id = 8) AND (ticket.created_by_id = '39731566'::bigint)))
               Filter: ((ticket.status IS NOT NULL) AND (ticket.created_at >= '2020-12-07 03:00:10.973'::timestamp without time zone) AND (ticket.created_at <= '2021-02-05 03:00:10.973'::timestamp without time zone))
               Rows Removed by Filter: 1517
               Heap Blocks: exact=2638
               Buffers: shared hit=2277 read=3105
               ->  BitmapOr  (cost=17855.76..17855.76 rows=291 width=0) (actual time=106.215..106.216 rows=0 loops=1)
                     Buffers: shared hit=336 read=2408
                     ->  Bitmap Index Scan on ticket_assigned_group_portal_id_assigned_agent_idx  (cost=0.00..11.25 rows=282 width=0) (actual time=10.661..10.661 rows=2776 loops=1)
                           Index Cond: ((ticket.assigned_group = '447'::bigint) AND (ticket.portal_id = 8))
                           Buffers: shared hit=4 read=15
                     ->  Bitmap Index Scan on ticket_created_at_portal_id_created_by_id_assigned_group_id_idx  (cost=0.00..17844.47 rows=9 width=0) (actual time=95.551..95.551 rows=2 loops=1)
                           Index Cond: ((ticket.created_at >= '2020-12-07 03:00:10.973'::timestamp without time zone) AND (ticket.created_at <= '2021-02-05 03:00:10.973'::timestamp without time zone) AND (ticket.portal_id = 8) AND (ticket.created_by_id = '39731566'::bigint))
                           Buffers: shared hit=332 read=2393
 Planning time: 43.083 ms
 Execution time: 278.556 ms
(25 rows)

ticket_created_at_portal_id_created_by_id_assigned_group_id_idx 具有 where 子句的所有列,除了 status 不是 null,但查询仍然使用 Index Cond 的单独索引:((ticket.assigned_group = '447'::bigint) AND (ticket.portal_id = 8))已经存在于第二个索引 ticket_created_at_portal_id_created_by_id_assigned_group_id_idx 中。

为什么会这样? 即使我在索引中也包含状态列,查询仍然使用 2 个索引,并且 hen 对索引堆扫描进行了大量过滤。

  1. 我们如何优化它?

我也尝试过表的索引,但仍然无法删除索引。 似乎在不同查询的多个索引中重复了相同的列,如果我们可以减少这些索引数,请提供帮助。 表的所有索引是:

"ticket_pkey" PRIMARY KEY, btree (id)
"ticket_ticket_id_idx" UNIQUE, btree (ticket_id)
"uk2uors84i0m8sjxc6oaocuy6oj" UNIQUE CONSTRAINT, btree (ticket_id)
"idx_resource_id" btree (resource_id)
"idx_ticket_created_at" btree (created_at)
"ticket_assigned_agent_idx" btree (assigned_agent)
"ticket_assigned_group_idx" btree (assigned_group)
"ticket_assigned_group_portal_id_assigned_agent_idx" btree (assigned_group, portal_id, assigned_agent)
"ticket_created_at_portal_id_created_by_id_assigned_group_id_idx" btree (created_at, portal_id, created_by_id, assigned_group)
"ticket_created_at_portal_id_status_idx" btree (created_at, portal_id, status)
"ticket_id_resolved_at_assigned_group_status_idx" btree (id, resolved_at, assigned_group, status)

使用电话簿(按姓氏然后名字排序)找到名字为“Francis”且姓氏以 K 和 T 之间的字母开头的每个人有多容易? 不是很容易,因为它不是按名字排序的。 您将不得不通过电话簿的整个中间部分 go,阅读每个人的名字。

同样在这里。 当索引中的第一列用于范围/不等式查询而不是相等时,它会使之后的所有列的效率大大降低。 您可能希望将用于相等的列而不是放在 OR 中。 不幸的是,这只是portal_id。 接下来最好的事情取决于我们无法从提供的信息中猜测的其他每个条件的选择性。

在决定这一点时, status IS NULL将与平等相同,但status IS NOT NULL不是因为它可能是任何数量的值,但仍然不是 Z37A6259CC0C1DAE299A7866489DFF0,因此它实际上与不等式相同。 如果此条件具有高度选择性,则将其合并的最佳方法是在部分索引的 WHERE 中。

由于 OR,您可能仍然最好使用 2 个索引,这些索引可以组合在 bitmap 或。

...(portal_id, assigned_group, created_at) WHERE status IS NOT NULL;
...(portal_id, created_by_id, created_at) WHERE status IS NOT NULL;

另一种方法是避免获取和排序所有匹配的行,方法是使用索引按 updated_at 的顺序遍历行,并在找到其中的 10 个后停止。 只要在 ORDER BY 列之前只有经过相等性测试(并且没有 OR)的事物发生在 ORDER BY 列之前,索引就可以用于按顺序遍历列,因此:

...(portal_id, updated_at) WHERE status IS NOT NULL;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM