使用Psycopg2优化一系列SQL更新查询

时间:2015-12-31 03:52:44

标签: performance psycopg2

我需要使用Psycopg2进行大量的SQL查询来更新或插入行。没有其他查询在中间运行。表A包含列namevalue

的示例
% Basically models a list of strings and how many times they "appear"
% 'foo' is some random value each time, sometimes repeating
insert into A select ('foo', 0)
    where not exists(select 1 from A where name = 'foo' limit 1);
update A set value = value + 1 where name = 'foo';
% ... and many more just like this

这只是一个例子,我正在运行的一种查询。我也在做其他事情。我不是在寻找一个涉及重新编写SQL查询的解决方案。

这很慢,Postgres(在另一台服务器上运行)会出现瓶颈。我尝试了各种各样的东西来加快速度。

  • 如果我在每次查询后都提交,那就太慢了。
  • 如果直到最后我没有connection.commit(),那会快一点。这似乎是Psycopg2文档建议我做的。 Postgres在磁盘访问方面仍然存在严重的瓶颈。
  • 如果我使用cursor.mogrify()代替cursor.execute(),将所有查询存储在一个大列表中,最后将它们连接成一个大型查询(字面意为";".join(qs)),速度会快得多,并运行它。 Postgres使用100%CPU,这是一个好兆头,因为这意味着〜没有磁盘瓶颈。但这有时会导致postgres进程耗尽我的所有内存并开始页面故障,然后永远陷入磁盘访问的瓶颈,这是一场灾难。我已经使用pgtune将Postgres的所有内存限制设置为合理的值,但我猜测Postgres正在分配一堆没有限制的工作缓冲区并且已经过了。
  • 我尝试了上述解决方案,除了提交每100,000个左右的查询以避免服务器超载,但这不是一个完美的解决方案。这就是我现在所拥有的。这似乎是一个荒谬的黑客,并且仍然比我想要的慢。

还有其他方法我应该尝试涉及Psycopg2吗?

1 个答案:

答案 0 :(得分:1)

听起来你在这里遇到很多问题。第一个是Postgres不应该页面错误,除非你有不正确的配置或你在机器上运行其他服务。正确配置的Postgres实例将使用您的内存,但不会发生页面故障。

如果您需要一次插入或更新100,000件事物,那么您肯定不希望一次执行该1个事务,因为您注意到这将非常慢。在您的第一个示例中,您正在通过网络将每个查询发送到数据库,等待结果,然后再次通过网络提交并等待该结果。

一次将多个内容串在一起将为您节省1次提交和来回的网络流量,这就是您看到性能显着提高的原因。

如果您正在进行插入操作或使用值列表而不是单个插入或更新语句,则可以将字符串更进一步并使用copy。

真正的问题是你正在做的设计流程。您从查询的外观中执行的操作是在数据库中实现计数器。如果你只是在这里或那里计算几百件事,没什么大不了的,但是当你进入10万秒+它将无法正常工作。

这就是memcached和redis等工具的用武之地。两者都有非常快速的内存计数器的优秀工具。 (如果你只有一台服务器,你可以在你的代码中实现一个计数器。)一旦计算好了,只需创建一个进程将计数保存到数据库并清除内存计数器。