Postgresql计数+排序性能

2024-03-21

我使用 postgresql 和 psycopg2 构建了一个小型库存系统。一切都很好,除了当我想创建内容的聚合摘要/报告时,由于 count() 和排序,我的性能非常糟糕。

数据库架构如下:


CREATE TABLE hosts
(
        id SERIAL PRIMARY KEY,
        name VARCHAR(255)
);
CREATE TABLE items
(
        id SERIAL PRIMARY KEY,
        description TEXT
);
CREATE TABLE host_item
(
        id SERIAL PRIMARY KEY,
        host INTEGER REFERENCES hosts(id) ON DELETE CASCADE ON UPDATE CASCADE,
        item INTEGER REFERENCES items(id) ON DELETE CASCADE ON UPDATE CASCADE
);  

还有一些其他字段,但这些字段不相关。

我想提取 2 个不同的报告: - 所有主机的列表以及每个主机的项目数,从最高的顺序排列 到最低计数 - 所有项目的列表,其中包含每个主机的数量,从最高到最低计数排序

我为此目的使用了 2 个查询:

具有主机数量的项目:


SELECT i.id, i.description, COUNT(hi.id) AS count
FROM items AS i
LEFT JOIN host_item AS hi
ON (i.id=hi.item)
GROUP BY i.id
ORDER BY count DESC
LIMIT 10;  

主机的项目数:


SELECT h.id, h.name, COUNT(hi.id) AS count
FROM hosts AS h
LEFT JOIN host_item AS hi
ON (h.id=hi.host)
GROUP BY h.id
ORDER BY count DESC
LIMIT 10;  

问题是:查询在返回任何数据之前运行 5-6 秒。由于这是一个基于 Web 的应用程序,6 秒是不可接受的。该数据库充满了大约 50k 主机、1000 个项目和 400 000 个主机/项目关系,并且当(或者如果)使用应用程序时,数据库可能会显着增加。

经过尝试,我发现通过删除“ORDER BY count DESC”部分,两个查询都会立即执行,没有任何延迟(完成查询的时间不到 20 毫秒)。

有什么方法可以优化这些查询,以便我可以毫不延迟地对结果进行排序?我尝试了不同的索引,但由于计算了计数,因此可以使用索引。我读过 postgresql 中的 count() 很慢,但它的排序给我带来了问题......

我当前的解决方法是将上面的查询作为每小时作业运行,将结果放入一个新表中,并在计数列上有一个索引以便快速查找。

我使用 Postgresql 9.2。

更新:按顺序查询计划:)

EXPLAIN ANALYZE
SELECT h.id, h.name, COUNT(hi.id) AS count
FROM hosts AS h
LEFT JOIN host_item AS hi
ON (h.id=hi.host)
GROUP BY h.id
ORDER BY count DESC
LIMIT 10;


 Limit  (cost=699028.97..699028.99 rows=10 width=21) (actual time=5427.422..5427.424 rows=10 loops=1)
   ->  Sort  (cost=699028.97..699166.44 rows=54990 width=21) (actual time=5427.415..5427.416 rows=10 loops=1)
         Sort Key: (count(hi.id))
         Sort Method: top-N heapsort  Memory: 25kB
         ->  GroupAggregate  (cost=613177.95..697840.66 rows=54990 width=21) (actual time=3317.320..5416.440 rows=54990 loops=1)
               ->  Merge Left Join  (cost=613177.95..679024.94 rows=3653163 width=21) (actual time=3317.267..5025.999 rows=3653163 loops=1)
                     Merge Cond: (h.id = hi.host)
                     ->  Index Scan using hosts_pkey on hosts h  (cost=0.00..1779.16 rows=54990 width=17) (actual time=0.012..15.693 rows=54990 loops=1)
                     ->  Materialize  (cost=613177.95..631443.77 rows=3653163 width=8) (actual time=3317.245..4370.865 rows=3653163 loops=1)
                           ->  Sort  (cost=613177.95..622310.86 rows=3653163 width=8) (actual time=3317.199..3975.417 rows=3653163 loops=1)
                                 Sort Key: hi.host
                                 Sort Method: external merge  Disk: 64288kB
                                 ->  Seq Scan on host_item hi  (cost=0.00..65124.63 rows=3653163 width=8) (actual time=0.006..643.257 rows=3653163 loops=1)
 Total runtime: 5438.248 ms





EXPLAIN ANALYZE
SELECT h.id, h.name, COUNT(hi.id) AS count
FROM hosts AS h
LEFT JOIN host_item AS hi
ON (h.id=hi.host)
GROUP BY h.id
LIMIT 10;


 Limit  (cost=0.00..417.03 rows=10 width=21) (actual time=0.136..0.849 rows=10 loops=1)
   ->  GroupAggregate  (cost=0.00..2293261.13 rows=54990 width=21) (actual time=0.134..0.845 rows=10 loops=1)
         ->  Merge Left Join  (cost=0.00..2274445.41 rows=3653163 width=21) (actual time=0.040..0.704 rows=581 loops=1)
               Merge Cond: (h.id = hi.host)
               ->  Index Scan using hosts_pkey on hosts h  (cost=0.00..1779.16 rows=54990 width=17) (actual time=0.015..0.021 rows=11 loops=1)
               ->  Index Scan Backward using idx_host_item_host on host_item hi  (cost=0.00..2226864.24 rows=3653163 width=8) (actual time=0.005..0.438 rows=581 loops=1)
 Total runtime: 1.143 ms

更新:这个问题的所有答案对于学习和理解 Postgres 的工作原理非常有帮助。这个问题似乎没有任何明确的解决方案,但我真的很感谢您提供的所有优秀答案,我将在以后的 Postgresql 工作中使用这些答案。非常感谢大家!


@Gordon 和 @willglynn 提供了很多有用的背景知识来解释为什么你的查询很慢。

解决方法是在表中添加一个计数器items and hosts以及使它们保持最新状态的触发器 - 写入操作的成本不菲。
或者像您一样使用物化视图。我可能会选择那样。

为此,您仍然需要定期执行这些查询,并且它们can得到改善。将您的第一个重写为:

SELECT id, i.description, hi.ct
FROM   items i
JOIN  (
    SELECT item AS id, count(*) AS ct
    FROM   host_item
    GROUP  BY item
    ORDER  BY ct DESC
    LIMIT  10
    ) hi USING (id);
  • 如果表中有一行items对于表中的大多数行host_item,先聚合再聚合速度更快JOIN。与 @willglynn 推测的相反,Postgres 9.1 中不会自动优化这一点。

  • count(*)count(col)原则上 - 以及等效的同时col不能为 NULL。 (ALEFT JOIN可能会引入 NULL 值。)

  • 简化版LEFT JOIN to JOIN。可以安全地假设总是至少有十个不同的主机。对于您的原始查询来说并不重要,但这是此查询的一项要求。

  • 表上的索引host_item没有帮助,而且PKitems覆盖其余部分。

对于您的情况可能仍然不够好,但在我对 Postgres 9.1 的测试中,这种形式是快两倍以上。应该转换为 9.2,但测试时使用EXPLAIN ANALYZE为了确定。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Postgresql计数+排序性能 的相关文章

随机推荐

  • Pandoc 和 html5 导出 pdf 的利润丰厚

    所以我第一次尝试Pandoc 一切看起来都很棒 但是当通过 html5 wkhtmltopdf 导出时 我的 pdf 输出保存时各面都有巨大的边距 pandoc t html5 s example md o output pdf 输出 pd
  • 快速运行总和

    我想要一个函数runningSum在数字数组 a 或任何可添加事物的有序集合 上 返回相同长度的数组 其中每个元素i是 A 中所有元素的总和最多包括i 例子 runningSum 1 1 1 1 1 1 gt 1 2 3 4 5 6 run
  • Linq to Sql 任意关键字搜索查询

    我的应用程序中有一个案例 用户可以搜索术语列表 搜索需要按以下顺序进行三遍 与他们输入的内容完全匹配的一个 完成 简单 所有单词 单独 匹配的一种 完成了 也很简单 一处any单词匹配 如何 本质上 我如何在 Linq to Sql 中告诉
  • 如何传递 Rscript -e 多行字符串?

    有没有办法提供代码Rscript e多行 这在 vanilla R 中是可能的 R vanilla lt code a lt 3 3 quote gt cat a n quote gt ARGUMENT cat a ignored ARGU
  • Oracle Form 中的旧值和新值

    我正在使用 Oracle Forms 我有一个名为SOLD TO CUST PARTY NAME 如果我检测到字段值发生变化 我必须执行一个过程 我尝试使用when validate 但即使您只需单击该字段并移动到另一个字段 它也会执行 无
  • Entity Framework 4.1 - Code First:多对多关系

    我想建立这样的关系 一个区域位于 x 个其他区域的附近 public class Zone public string Id get set public string Name get set public virtual ICollec
  • 在 Java 中使用 ENUMS 验证值组合的最佳方法是什么?

    我通过如下定义 ENUM 来验证从数据库检索的记录的状态 public enum RecordStatusEnum CREATED CREATED INSERTED INSERTED FAILED FAILED private String
  • 在Linux中使用自定义规则在多个端口上运行的SSH服务[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我正在努力设置一台在多个端口上运行 SSH 服务的服务器 例如端口 22 和 5522 这些端口应该具有一组不同的规则 即 我们为端口 2
  • 在 C# 中如何将字符串转换为 ascii 二进制?

    不久前 高中一年级 我请一位非常优秀的大三 C 程序员制作一个简单的应用程序 将字符串转换为二进制 他给了我以下代码示例 void ToBinary char str char tempstr int k 0 tempstr new cha
  • 列表未添加 C# 中的所有值

    我尝试了下面的代码来创建 json 代码 代码工作正常 我从数据库加载值 但只有最后一个值我得到了输出 剩余值未添加 DataTable dt new DataTable var objectToSerialize new RootObje
  • 解除PDF密码保护,知道密码[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我有一堆 pdf 文件 我想从中删除密码 请注意 我知道密码 因此无需暴力破解 我正在 Mac 上工作 所以我想制作一个应用程序来删除这些
  • Git合并分支到master

    我有一个主分支和一个工作分支branch 1 我想 动 一下branch 1正是如此master 所以我想要这样的东西 git checkout master git merge branch 1 I don t know what is
  • symfony2 - twig - 如何从树枝模板内部渲染树枝模板

    我有一个 xxx html twig 文件 它显示一个页面 但是当我想用不同的数据刷新页面并用新数据更新它时 我有一个选择和一个提交按钮 问题是我不知道如何在控制器中调用一个动作 我从我的树枝传递参数并调用新数据 然后我用新参数再次渲染相同
  • Python:单击按钮[重复]

    这个问题在这里已经有答案了 我在单击此按钮时遇到问题 该按钮的 HTML 代码如下所示
  • Eventbug 的实际工作原理

    Eventbug http getfirebug com wiki index php Firebug Extensions Eventbug是 Firebug 的一个附加组件 是的 附加组件的附加组件 其目的是跟踪分配给 DOM 元素的所
  • ld:架构armv7的871个重复符号,clang:错误:链接器命令失败,退出代码1(使用-v查看调用)

    我在 iPhone 应用程序中使用 FastPDFKit 来显示 PDF 当我在模拟器上运行该项目时 它工作正常 但是 当我在 iPhone 上运行该项目时 出现以下错误 duplicate symbol value map in User
  • 如何多次查询并最后关闭连接?

    我想打开与 mysql 数据库的连接并使用不同的查询检索数据 我是否需要在每次获取数据时关闭连接 或者是否有更好的方法可以多次查询并仅在最后关闭连接 目前我这样做 db dbConnect MySQL user root password
  • 我们可以导出 Kibana 中的所有搜索结果数据吗?

    我正在尝试导出 Kibana 5 中的所有搜索结果数据 但它仅导出结果的计数 有没有办法将所有数据导出为 CSV 格式 在基巴纳 到目前为止尝试过 单击搜索结果底部的符号 可视化 尝试使用 原始 和 格式化 选项 数据以 CSV 格式导出
  • symfony:如何设置不同环境的配置参数文件?

    如何为每个环境设置不同的配置参数文件 目前参数在parameters yml两者都使用dev and prod环境 但我需要不同的参数才能在产品中部署我的应用程序 您可以将所有使用的参数放入dev环境在一个app config parame
  • Postgresql计数+排序性能

    我使用 postgresql 和 psycopg2 构建了一个小型库存系统 一切都很好 除了当我想创建内容的聚合摘要 报告时 由于 count 和排序 我的性能非常糟糕 数据库架构如下 CREATE TABLE hosts id SERIA