Tha*_*Guy 3 postgresql performance postgresql-9.1 query-performance
我编写了一个小程序来从文件导入产品详细信息更新,这比预期的要长得多。(为简洁起见,我将使用精简的示例。)
该程序执行以下操作:
COPY
s 修改后的数据到临时表中。这一切都很好,除了UPDATE
查询需要约 20 秒的时间来处理约 2000 行的小文件。
临时表如下所示:
CREATE TEMPORARY TABLE tmp_products (
product_id integer,
detail text
);
Run Code Online (Sandbox Code Playgroud)
我的更新查询非常简单:
UPDATE products
SET detail = t.detail
FROM tmp_products t
WHERE t.product_id = products.product_id
Run Code Online (Sandbox Code Playgroud)
为了加快速度,我尝试了以下方法,但收效甚微:
在临时表上创建 BTREE 索引。
CREATE INDEX tmp_products_idx
ON tmp_products
USING BTREE
(product_id);
Run Code Online (Sandbox Code Playgroud)
创建哈希索引:
CREATE INDEX tmp_products_idx
ON tmp_products
USING HASH
(product_id);
Run Code Online (Sandbox Code Playgroud)
这两个索引都没有显着改善更新时间。然后我想也许对表进行聚类会有所帮助,但这意味着我不能使用 HASH 索引。所以我修改了程序中的查询以使用 BTREE 索引,然后使用 CLUSTER/ANALYZE:
CREATE INDEX tmp_products_idx
ON tmp_products
USING BTREE
(product_id);
-- Program inserts data
CLUSTER tmp_products USING tmp_products_idx;
ANALYZE tmp_products;
Run Code Online (Sandbox Code Playgroud)
这也没有任何帮助。我通过同时使用 BTREE 和 HASH 索引再次尝试了一下,希望 CLUSTER 将使用 BTREE,而 UPDATE 将使用 HASH:
CREATE INDEX tmp_products_btree_idx
ON tmp_products
USING BTREE
(product_id);
CREATE INDEX tmp_products_hash_idx
ON tmp_products
USING BTREE
(product_id);
-- Program inserts data
CLUSTER tmp_products USING tmp_products_btree_idx;
ANALYZE tmp_products;
Run Code Online (Sandbox Code Playgroud)
再一次,没有任何帮助。我仍然在我离开的地方 - 20 秒 2000 行。在我的工作场所,通常 20 秒的更新是可以接受的,但是 2000 行的文件是我用于测试的一个小样本。较大的文件将花费太长时间。
products
如果重要的话,有关该表的一些详细信息:
行:~630k
列:54
索引:19
触发器:14
表大小:~1.2GB
索引大小:~2.2GB
我强烈怀疑瓶颈在一个或多个触发器中,但是我无法删除/修改这些触发器。我可以做些什么来提高我的更新效率?
可以做的事情,尽管它们是否有帮助......是另一个故事:
鉴于你UPDATE
很简单,我的第一个猜测是你的触发器正在损害你的表现。处理触发器通常很耗时,特别是如果它们是用任何解释性语言编写的(这意味着,或多或少,它们不是用 C 编写的)。如果您有可能使用开发机器进行检查,请测试禁用所有触发器,并查看它有什么影响(时间查询!)。然后一一重新启用它们,看看每个对时间有什么影响。你会发现一些伤害(很多)的触发器。如果有几个很费时间,请有资格的人修改,优化它们,甚至必要时用C重写它们。我的经验是,任何一种记录或审核您的插入可能会使过程变慢(容易地)10 倍。考虑到,在您的 14 个触发器之上,数据库可能添加了更多触发器以确保满足所有约束(CHECK
, REFERENCES
, UNIQUE
, . ...)。尝试禁用它们通常不是一个好主意(如果可能的话,这样做并不简单)。
尝试找出您是否真的需要设置中的所有索引。在 PostgreSQL wiki 上查看有关未使用索引的解释。您的查询的工作方式(仅更新列detail
),如果detail
不是任何索引的一部分,则不会产生太大影响。PostgreSQL应该能够执行Heap Only Tuple (HOT)更新,并且索引不会有任何大的影响。
要使 HOT 更新成功,您的表中需要一些可用空间。因此,请确保您的表fillfactor
少于 100。从文档开始CREATE TABLE
:
fillfactor (integer)
表的填充因子是 10 到 100 之间的百分比。100(完全填充)是默认值。当指定较小的填充因子时,INSERT 操作仅将表页打包到指定的百分比;每页上的剩余空间保留用于更新该页上的行。这使 UPDATE 有机会将行的更新副本放置在与原始页面相同的页面上,这比将其放置在不同的页面上效率更高。对于条目从不更新的表,完全填充是最佳选择,但在大量更新的表中,较小的填充因子是合适的。不能为 TOAST 表设置此参数。
(强调我的)
考虑在临时表上设置覆盖索引。那是:
CREATE INDEX tmp_products_idx
ON tmp_products
USING BTREE
(product_id, detail);
ANALYZE tmp_products;
Run Code Online (Sandbox Code Playgroud)
这只有在长度detail
适中时才有意义。我认为这不会有太大的不同......因为这可能允许对更新的源部分进行仅索引扫描,但除非您尝试,否则您不会确定。
需要更多有关您的执行计划的信息才能提供更好的建议。