我需要使用Psycopg2进行大量的SQL查询来更新或插入行。没有其他查询在中间运行。表A包含列name
和value
:
% 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正在分配一堆没有限制的工作缓冲区并且已经过了。还有其他方法我应该尝试涉及Psycopg2吗?
答案 0 :(得分:1)
听起来你在这里遇到很多问题。第一个是Postgres不应该页面错误,除非你有不正确的配置或你在机器上运行其他服务。正确配置的Postgres实例将使用您的内存,但不会发生页面故障。
如果您需要一次插入或更新100,000件事物,那么您肯定不希望一次执行该1个事务,因为您注意到这将非常慢。在您的第一个示例中,您正在通过网络将每个查询发送到数据库,等待结果,然后再次通过网络提交并等待该结果。
一次将多个内容串在一起将为您节省1次提交和来回的网络流量,这就是您看到性能显着提高的原因。
如果您正在进行插入操作或使用值列表而不是单个插入或更新语句,则可以将字符串更进一步并使用copy。
真正的问题是你正在做的设计流程。您从查询的外观中执行的操作是在数据库中实现计数器。如果你只是在这里或那里计算几百件事,没什么大不了的,但是当你进入10万秒+它将无法正常工作。
这就是memcached和redis等工具的用武之地。两者都有非常快速的内存计数器的优秀工具。 (如果你只有一台服务器,你可以在你的代码中实现一个计数器。)一旦计算好了,只需创建一个进程将计数保存到数据库并清除内存计数器。