使用 Psycopg2 优化一系列 SQL 更新查询
Optimizing a long series of SQL update queries with Psycopg2
我需要使用 Psycopg2 进行大量 SQL 更新或插入行的查询。中间没有其他查询运行。 table 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
这只是一个示例,我正在 运行ning 查询的一种类型。我也在做其他事情。我不是在寻找涉及重新处理我的 SQL 查询的解决方案。
它真的很慢,Postgres(在另一台服务器上 运行ning)成为瓶颈。我尝试了各种方法来让它更快。
- 如果我在每次查询后都提交,速度会慢得无法忍受。
- 如果我不
connection.commit()
到最后会快一点。这似乎是 Psycopg2 文档建议我做的。 Postgres 在磁盘访问方面仍然存在严重的瓶颈。
- 如果我使用
cursor.mogrify()
而不是 cursor.execute()
,速度会快得多,将所有查询存储在一个大列表中,最后将它们连接成一个大查询(字面意思是 ";".join(qs)
), 和 运行 它。 Postgres 使用 100% CPU,这是一个好兆头,因为这意味着没有磁盘瓶颈。但这有时会导致 postgres
进程耗尽我所有的 RAM 并开始页面错误,然后永远成为磁盘访问瓶颈,这是一场灾难。我已经使用 pgtune 将 Postgres 的所有内存限制设置为合理的值,但我猜 Postgres 正在分配一堆没有限制的工作缓冲区并继续下去。
- 除了每 100,000 次左右提交一次查询以避免服务器过载之外,我已经尝试了上述解决方案,但这不是一个完美的解决方案。这就是我现在所拥有的。这似乎是一个荒谬的 hack,而且仍然比我想要的要慢。
我应该尝试使用其他方法来涉及 Psycopg2 吗?
听起来你这里有很多问题。首先是 Postgres 不应该出现页面错误,除非你没有正确配置它或者你是机器上的 运行 其他服务。正确配置的 Postgres 实例将使用您的内存,但不会出现页面错误。
如果您需要一次插入或更新 100,000 件东西,您绝对不想一次执行 1 个事务,因为您注意到这会非常慢。在您的第一个示例中,您正在通过网络将每个查询发送到数据库,等待结果,然后再次通过网络提交并等待该结果。
一次将多个东西串在一起将为您节省每次提交和来回网络流量的 1 次,这就是您看到明显更快的性能的原因。
如果您正在执行插入或使用值列表而不是单个插入或更新语句,则可以进一步将串接在一起并使用复制。
真正的问题是您正在做的事情的设计流程。从查询的外观来看,您正在做的是在数据库中实现一个计数器。如果你只想在这里或那里数几百个东西,没什么大不了的,但是当你进入 100,000s+ 时,它就不会工作了。
这就是 memcached 和 redis 等工具的用武之地。两者都具有非常快速的内存计数器的优秀工具。 (如果你只有一台服务器,你可以在你的代码中实现一个计数器。)一旦你计数了,只需创建一个进程来将计数保存到数据库并清除内存中的计数器。
我需要使用 Psycopg2 进行大量 SQL 更新或插入行的查询。中间没有其他查询运行。 table 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
这只是一个示例,我正在 运行ning 查询的一种类型。我也在做其他事情。我不是在寻找涉及重新处理我的 SQL 查询的解决方案。
它真的很慢,Postgres(在另一台服务器上 运行ning)成为瓶颈。我尝试了各种方法来让它更快。
- 如果我在每次查询后都提交,速度会慢得无法忍受。
- 如果我不
connection.commit()
到最后会快一点。这似乎是 Psycopg2 文档建议我做的。 Postgres 在磁盘访问方面仍然存在严重的瓶颈。 - 如果我使用
cursor.mogrify()
而不是cursor.execute()
,速度会快得多,将所有查询存储在一个大列表中,最后将它们连接成一个大查询(字面意思是";".join(qs)
), 和 运行 它。 Postgres 使用 100% CPU,这是一个好兆头,因为这意味着没有磁盘瓶颈。但这有时会导致postgres
进程耗尽我所有的 RAM 并开始页面错误,然后永远成为磁盘访问瓶颈,这是一场灾难。我已经使用 pgtune 将 Postgres 的所有内存限制设置为合理的值,但我猜 Postgres 正在分配一堆没有限制的工作缓冲区并继续下去。 - 除了每 100,000 次左右提交一次查询以避免服务器过载之外,我已经尝试了上述解决方案,但这不是一个完美的解决方案。这就是我现在所拥有的。这似乎是一个荒谬的 hack,而且仍然比我想要的要慢。
我应该尝试使用其他方法来涉及 Psycopg2 吗?
听起来你这里有很多问题。首先是 Postgres 不应该出现页面错误,除非你没有正确配置它或者你是机器上的 运行 其他服务。正确配置的 Postgres 实例将使用您的内存,但不会出现页面错误。
如果您需要一次插入或更新 100,000 件东西,您绝对不想一次执行 1 个事务,因为您注意到这会非常慢。在您的第一个示例中,您正在通过网络将每个查询发送到数据库,等待结果,然后再次通过网络提交并等待该结果。
一次将多个东西串在一起将为您节省每次提交和来回网络流量的 1 次,这就是您看到明显更快的性能的原因。
如果您正在执行插入或使用值列表而不是单个插入或更新语句,则可以进一步将串接在一起并使用复制。
真正的问题是您正在做的事情的设计流程。从查询的外观来看,您正在做的是在数据库中实现一个计数器。如果你只想在这里或那里数几百个东西,没什么大不了的,但是当你进入 100,000s+ 时,它就不会工作了。
这就是 memcached 和 redis 等工具的用武之地。两者都具有非常快速的内存计数器的优秀工具。 (如果你只有一台服务器,你可以在你的代码中实现一个计数器。)一旦你计数了,只需创建一个进程来将计数保存到数据库并清除内存中的计数器。