Postgresql仅索引扫描无法在Group By上正常工作

Postgresql仅索引扫描无法在Group By上正常工作,postgresql,indexing,query-performance,Postgresql,Indexing,Query Performance,我有一张像这样的桌子: CREATE TABLE summary ( id serial NOT NULL, user_id bigint NOT NULL, country character varying(5), product_id bigint NOT NULL, category_id bigint NOT NULL, text_id bigint NOT NULL, text character varying(255),

我有一张像这样的桌子:

CREATE TABLE summary
(
    id serial NOT NULL,
    user_id bigint NOT NULL,
    country character varying(5),
    product_id bigint NOT NULL,
    category_id bigint NOT NULL,
    text_id bigint NOT NULL,
    text character varying(255),
    product_type integer NOT NULL,
    event_name character varying(255),
    report_date date NOT NULL,
    currency character varying(5),
    revenue double precision,
    last_event_time timestamp
);
我的表大小是1786MB(索引除外)。在这里,我创建了如下索引:

CREATE INDEX "idx_as_type_usr_productId_eventTime"
    ON summary USING btree
    (product_type, user_id, product_id, last_event_time)
    INCLUDE(event_name);
select 
   event_name, 
   max(last_event_time) 
from summary s 
where s.user_id = ? and s.product_id = ? and s.product_type = ? 
   and s.last_event_time > '2020-03-01' and s.last_event_time < '2020-03-25'
group by event_name;
我的简单查询如下所示:

CREATE INDEX "idx_as_type_usr_productId_eventTime"
    ON summary USING btree
    (product_type, user_id, product_id, last_event_time)
    INCLUDE(event_name);
select 
   event_name, 
   max(last_event_time) 
from summary s 
where s.user_id = ? and s.product_id = ? and s.product_type = ? 
   and s.last_event_time > '2020-03-01' and s.last_event_time < '2020-03-25'
group by event_name;
选择
事件名称,
最大值(上次事件时间)
从摘要s
其中s.user_id=?和s.product_id=?和s.product_type=?
和s.last_event_time>'2020-03-01'和s.last_event_time<'2020-03-25'
按事件名称分组;
当我解释它时,它看起来像

HashAggregate(成本=93.82..96.41行=259宽度=25)(实际时间=9187.533..9187.536行=10个循环=1)
组键:事件名称
缓冲区:共享命中=70898读=10579脏=22650
I/O定时:读取=3876.367
->仅使用摘要s上的“idx_as_type_usr_productId_eventTime”进行索引扫描(成本=0.56..92.36行=292宽度=25)(实际时间=0.485..9153.812行=87322循环=1)
索引条件:((产品类型=2)和(产品id=?)和(产品id=?)和(上次事件时间>'2020-03-01 00:00:00':无时区时间戳)和(上次事件时间<'2020-03-25 00:00:00':无时区时间戳))
堆取数:35967
缓冲区:共享命中=70898读=10579脏=22650
I/O定时:读取=3876.367
计划时间:0.452毫秒
执行时间:9187.583毫秒
在这里,一切看起来都很好。但是当我执行它的时候,它需要超过10秒,有时需要超过30秒

  • 在这里,如果我在没有GROUPBY的情况下执行它,它会以不到2秒的速度返回。分组的效果如何?“选择”部分太小(如500行)
  • 此表具有每秒30次的插入/更新操作。这是否与索引问题有关 更新:

    不带-GroupBy的查询:

    select 
       event_name, 
       last_event_time 
    from summary s 
    where s.user_id = ? and s.product_id = ? and s.product_type = ? 
       and s.last_event_time > '2020-03-01' and s.last_event_time < '2020-03-25';
    
    选择
    事件名称,
    上次活动时间
    从摘要s
    其中s.user_id=?和s.product_id=?和s.product_type=?
    和s.last_event_time>'2020-03-01'和s.last_event_time<'2020-03-25';
    
    无需解释-分组方式:

    仅使用摘要s上的“idx_as_type_usr_productId_eventTime”进行索引扫描(成本=0.56..92.36行=292宽度=25)(实际时间=0.023..79.138行=87305循环=1)
    索引条件:((产品类型=?)和(用户id=?)和(产品id=?)和(上次事件时间>'2020-03-01 00:00:00'::无时区时间戳)和(上次事件时间<'2020-03-25 00:00:00'::无时区时间戳))
    堆取数:22949
    缓冲区:共享命中=37780读取=12143脏化=15156
    I/O定时:读取=4418.930
    计划时间:0.639毫秒
    执行时间:4625.213毫秒
    
    有几个问题:

    • PostgreSQL必须设置提示位,这会弄脏页面并导致写入

    • PostgreSQL必须从磁盘获取表行以获取它们的可见性

    • PostgreSQL必须扫描80000页才能获得87000行,因此索引必须完全膨胀

    前两个问题可以通过跑步来解决

    VACUUM summary;
    
    这总是一个好主意后,大量装载,和膨胀可以治愈

    REINDEX INDEX "idx_as_type_usr_productId_eventTime";
    

    有几个问题:

    • PostgreSQL必须设置提示位,这会弄脏页面并导致写入

    • PostgreSQL必须从磁盘获取表行以获取它们的可见性

    • PostgreSQL必须扫描80000页才能获得87000行,因此索引必须完全膨胀

    前两个问题可以通过跑步来解决

    VACUUM summary;
    
    这总是一个好主意后,大量装载,和膨胀可以治愈

    REINDEX INDEX "idx_as_type_usr_productId_eventTime";
    

    请以文本格式显示解释(分析、缓冲区)和。JSON格式适合机器阅读,但我们不是机器。更好的是,如果还没有开始,先打开track\u io\u timing。请对没有分组依据的查询执行相同的操作,因为您也询问了该查询。该查询与计划不匹配<代码>(上次事件时间>'2020-03-01 00:00:00':无时区的时间戳)和(上次事件时间<'2020-03-25 00:00:00':无时区的时间戳))“不在查询中。请提出一个前后一致的问题。总是从公开你的Postgres版本开始。杰夫对文本格式的看法。@jjanes和Erwin;问题已按您所说的方式编辑。@a_horse_,没有固定名称。请以文本格式显示解释(分析、缓冲区)。JSON格式适合机器阅读,但我们不是机器。更好的是,如果还没有开始,先打开track\u io\u timing。请对没有分组依据的查询执行相同的操作,因为您也询问了该查询。该查询与计划不匹配<代码>(上次事件时间>'2020-03-01 00:00:00':无时区的时间戳)和(上次事件时间<'2020-03-25 00:00:00':无时区的时间戳))“不在查询中。请提出一个前后一致的问题。总是从公开你的Postgres版本开始。杰夫对文本格式的看法。@jjanes和Erwin;问题已按您所说的编辑。@a_horse_,没有固定名称。Laurenz,谢谢您的回答。我知道这与数据库缓存有关,因为我按顺序获取了它们。因此,我再次更新了问题。此时,我在两次查询之间留出15分钟的时间获取它们。这里我的主要问题不是group by和non group by之间的差异。我的主要问题是,;只有500行需要扫描,而此扫描是仅索引扫描,因此为什么提取时间如此长?我已根据更改后的问题调整了答案。但此表每秒至少有30次插入/更新操作。那么我应该一直重新编制索引吗?它是否适用于解决方案?这是您问题的根本原因。调整自动真空并使用热更新。但这是另一个问题的材料。劳伦兹,谢谢你的回答。我知道这与数据库缓存有关,因为我按顺序获取了它们。因此,我再次更新了问题。此时,我在两次查询之间留出15分钟的时间获取它们。这里我的主要问题不是group by和non group by之间的差异。