【问题标题】:Performance drop in Python loop with psycopg2使用 psycopg2 的 Python 循环中的性能下降
【发布时间】:2017-08-17 06:57:40
【问题描述】:

我使用的是 Python 3.6,我的 table1 包含相当大量的数据,我只需要包含在区域(表“区域”的)中的 10%。我认为首先创建一个视图并通过它解析而不是完整的 table1 可以大大提高性能,但相反我看到在第 200 次迭代左右,速度急剧下降到每秒一次迭代(虽然起初它是连续的屏幕上的数字流)。这是代码:

import psycopg2

conn = psycopg2.connect("dbname=db_name user=postgres")
cur = conn.cursor()
cur.execute("create view temp as select st_setsrid(the_geom::geometry,2154), table1.gid from tout.area, public.table1 where (st_contains(area.geom,st_setsrid(table1.the_geom::geometry,2154)));")
cur.execute("select count(*) from temp")
nbtotal = cur.fetchone()[0]
print(nbtotal)
for i in range(nbtotal):
    print(i+1, " sur ", nbtotal)
    cur.execute ("insert into tout.table2 (geom) select st_setsrid FROM temp order by gid limit 1 offset "+str(i)+ ";")
    conn.commit()
cur.execute("drop view temp;")
conn.commit()

关于为什么会发生这种情况以及如何解决它的任何想法?在此之前,我尝试不创建视图(因此进行预选),并且循环在数千次迭代后减慢(我理解这是很正常的),但与那种节奏相去甚远。一种解决方法是加载所有数据,然后过滤结果并将其写入新表中。但这看起来不是一个合适的长期解决方案。

谢谢

【问题讨论】:

  • 可能有太多的插入和单独的提交 - 短时间内的事务,你检查过服务器端发生了什么,是否有任何自动真空,自动分析启动。你有多少索引有在表上,该表上是否有任何可能影响数据库性能的触发器,您是否检查过 PostgreSQL 日志是否有与检查点相关的警告等?
  • 这看起来像您的数据通过您的应用程序代码从数据库往返并不必要地返回。即使这样,您也正在通过插入进行单个 sql 的插入工作。性能将是悲惨的,相比之下。
  • @LongBeard_Boldy 恐怕我是数据库的新手,所以这些是我什至不知道存在的操作。我不明白如何插入许多插件。当我在没有预选的情况下启动它时,它没有任何性能问题,即使它解析了 10 倍长的表。

标签: python postgresql postgis psycopg2


【解决方案1】:

您是否尝试过一次完成所有操作?:

cur.execute ('''
    insert into tout.table2 (geom)
    select st_setsrid(the_geom::geometry, 2154)
    from
        tout.area
        inner join
        public.table1 on
            st_contains(area.geom, st_setsrid(table1.the_geom::geometry, 2154))
''')

【讨论】:

  • 这很可能会起作用,但它仍然会解析整个 table1,我特别希望避免使用大数据集(这是我的问题的目的)。
  • @GuiOmClair 您应该尝试一下,特别是如果您对 RDBMSes 不是很熟悉的话。衡量而不是假设绩效。
  • @pvg 真的哇!我对我之前的评论很不满意......它就像一个魅力!
【解决方案2】:

事实上,问题似乎来自 PostgreSQL 中视图的存在方式。创建物化视图似乎解决了我的问题。如果有人有解释,那将是受欢迎的。

如果它对任何人都有帮助,这里是我的代码的新版本:

import os, psycopg2

conn = psycopg2.connect("dbname=db_name user=postgres")
cur = conn.cursor()
cur.execute("create materialized view temp as select st_setsrid(the_geom::geometry,2154), table1.gid from tout.area, public.table1 where (st_contains(area.geom,st_setsrid(table1.the_geom::geometry,2154))); select count(*) from temp")
nbtotal = cur.fetchone()[0]
print(nbtotal)
for i in range(nbtotal):
    print(i+1, " sur ", nbtotal)
    cur.execute ("insert into tout.table2 (geom) select st_setsrid FROM temp order by gid limit 1 offset "+str(i)+ ";")
cur.execute("drop materialized view temp;")
conn.commit()

【讨论】:

  • 使用字符串连接来形成 SQL 命令(而不是参数化绑定变量)容易发生所谓的“注入攻击”。即使您的值是整数,也应该避免这种情况——StackOverflow 上的答案是一种教学资源,我们不想教授不良做法。
  • 作为一个更大的项目——物化视图在它们将被重用时是合适的;不用于临时目的。如果您想限制昂贵查询中考虑的内容,这就是 WHERE 子句的用途——数据库的优化器将尝试在昂贵的过滤器之前执行廉价的过滤器。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-08-14
  • 1970-01-01
  • 2013-05-17
  • 2013-09-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多