优化在 pgAdmin 中执行速度比在应用程序中更快的 postgresql 查询以及并发查询
Posted
技术标签:
【中文标题】优化在 pgAdmin 中执行速度比在应用程序中更快的 postgresql 查询以及并发查询【英文标题】:Optimize postgresql query that executes faster in pgAdmin versus in an application along with concurrent queries 【发布时间】:2014-09-01 10:32:31 【问题描述】:以下 SQL 查询 计算下面列出的架构的每日销售额。在 Sales 表上运行 VACUUM 和 ANALYZE 后,数据没有更新:
SELECT
COUNT("Sales"."ID") AS "Sales->ID"
,"Sales"."StoreKeeper" AS "Sales->StoreKeeper"
FROM
"Sales" "Sales"
WHERE
(
(
"Sales"."DayOfSale" >= 'Sun Aug 03 00:00:00 UTC 2014'
)
)
GROUP BY
"Sales"."StoreKeeper"
ORDER BY
"Sales->ID"
,"Sales->StoreKeeper"
销售表架构和索引:
CREATE TABLE "Sales"
(
"ID" text NOT NULL,
"DayOfSale" timestamp without time zone,
"StoreKeeper" text,
"CustomerId" text, --ignorable, since no references or joins to any other table
"Rating" text,
"Location" text,
"PaymentType" text,
"CashierId" text
)
CREATE INDEX saleid_index
ON "Sales"
USING btree
("ID" COLLATE pg_catalog."default" text_pattern_ops);
CREATE INDEX sale_dayofsale_index
ON "Sales"
USING btree
("DayOfSale");
表元数据:6500000 行,所有行都具有相同的“DayOfSale”值
PostgreSQL 版本:64 位 CentOS 上的 9.3.4
硬件:专用机器有 8 GB RAM,只运行 PostGreSQL 服务器
postgresql.conf 中的缓冲区大小:
work_mem:500 MB,shared_buffers=2048MB,effective_cache_size=6138MB
2 EXPLAIN 输出:
-
当查询在 PgAdmin 中自行执行时的第一个 http://explain.depesz.com/s/m5b
http://explain.depesz.com/s/LIIG 作为应用程序的一部分,同时在“Sales”表上触发了大约 7 个查询
观察:
-
无论查询是在应用程序中还是在 pgAdmin 中运行,都使用相同的计划。
一种。对于应用程序,所有查询的顺序扫描需要更长的时间
湾。没有。与 PgAdmin 相比,应用程序在 PostGreSQL 缓冲区中的命中率更高
查询:
-
顺序扫描花费的时间最多 - 可以使用
调整一些 postgresql.conf 设置?
由于 depesz 强调顺序扫描是最耗时的区域,因此这是最有可能进行调整以帮助提高性能的地方吗?
【问题讨论】:
在解释计划中,我看到了Filter: ("DayOfSale" >= '2014-08-03'::date)
。 PostgreSQL 是在执行该转换,还是我们在查看不同的查询?
查询计划显示查询需要 Sales 表中的几乎所有行,因此 seqscan 是一个不错的选择。顺便说一句:你为什么使用文本字段作为表格的 PK?
@Mike Sherill,PostgreSQL 正在执行演员表。这里只讨论了 1 个查询
@joop,这需要更新架构,但您认为这有助于提高性能吗?
在("DayOfSale", "StoreKeeper", "ID")
上尝试复合索引
【参考方案1】:
我使用您的 DDL 创建表和索引,然后用 200 万行无意义的数据填充表。
insert into "Sales" ("ID", "DayOfSale", "StoreKeeper", "CustomerId")
select n, timestamp '2014-08-05 08:00' - (n || ' minutes')::interval, random_integer(1, 100), 3
from generate_series(1, 2000000) n;
analyze "Sales";
我选择“DayOfSale”的值是为了保证只有一小部分表可以满足 WHERE 子句。我希望对此类数据进行索引扫描,而这正是发生的事情。
“排序(成本=173.76..173.76 行=1 宽度=9)(实际时间=2.639..2.647 行=100 循环=1)” " 排序键: (count("ID")), "StoreKeeper"" “排序方法:快速排序内存:29kB” “ -> HashAggregate(成本=173.74..173.75 行=1 宽度=9)(实际时间=2.542..2.557 行=100 循环=1)” “ -> 使用 sale_dayofsale_index 对“销售”进行索引扫描(成本=0.43..156.12 行=3525 宽度=9)(实际时间=0.023..1.184 行=3360 循环=1)” " Index Cond: ("DayOfSale" >= '2014-08-03 00:00:00'::timestamp without time zone)" “总运行时间:2.689 毫秒”此外,优化器没有从时间戳到日期进行转换。
根据我的经验,如果此表真的是关于销售的,那么很少会报告任意时间范围。我希望对此类数据的查询涉及日历周、日历月或日历年。所以你可能会受益于
部分索引, 表达式的索引,或 分区。我也想试试covering index。 PostgreSQL 9.2+ 可以进行仅索引扫描;如果您有覆盖索引,则不需要从表中读取数据。
此查询将显示每个月的行数。
select date_trunc('month', "DayOfSale"), count(*)
from "Sales"
group by date_trunc('month', "DayOfSale")
order by 1;
在我的例子中,它显示值的范围是从 2010 年 10 月到 2014 年 8 月。2014 年 8 月只有大约 6000 行,并不是所有这些都满足您的 WHERE 子句。将此查询的结果粘贴到您的问题中可能会很有用。
【讨论】:
感谢 Mike,您的回答促使我仔细检查了您的设置与我的设置之间的差异,因此在我的问题中编辑了数据集大小和元数据。在我的表中,“DayOfSale”只有一个值 - 这就是不使用索引扫描的原因吗? @TreyJonn 如果某个值代表该列值的百分之几以上,则其上的索引没有用。检查cardinality
@TreyJonn:是的,索引中的单个值通常会给你一个顺序扫描。不过,您可能仍会从覆盖索引中受益。 (一个索引涵盖查询中的三列:DayOfSale、Storekeeper、ID)【参考方案2】:
我认为你让它变得比它需要的更难。首先,执行 COUNT() 将只考虑一条记录,而不考虑您拥有的 count(Sales.ID) 列。这迫使引擎返回页面数据。如果您只关心给定的 StoreKeeper 有 X 次销售,count() 就可以了。
接下来,索引。我会在 (DayOfSale, StoreKeeper) 上有一个索引,这样,WHERE 子句可以利用索引的 DayofSale 部分,而 StoreKeeper 可以用于优化分组依据。我会完全删除销售 ID 号的订单,因为您关心的只是一般意义上的计数。
简化应该是
select
s.StoreKeeper,
count(*) as NumberOfSales
from
Sales s
where
s.DayOfSale >= 'Sun Aug 03 00:00:00 UTC 2014'
group by
s.StoreKeeper
order by
s.StoreKeeper
【讨论】:
以上是关于优化在 pgAdmin 中执行速度比在应用程序中更快的 postgresql 查询以及并发查询的主要内容,如果未能解决你的问题,请参考以下文章
SSE 程序在 AMD 上比在 Intel 上花费的时间要长得多
Oracle ODP.NET 托管驱动程序在 64 位中的运行速度比在 32 位中慢 50-100%
为啥这个算法在 python 中的运行速度比在 C++ 中快得多?
为什么这个通过PHP / FastCGI在IIS中运行的java程序比在shell中慢得多?