[英]ruby on rails postgresql active record parallle update
我有一个名为 AisSignal 的 model,它有大约 3000 条记录,我将每个记录都与另一个名为 Footprint 的 model 一起运行,它有大约 10 条记录,所以我们有一个 3000 x 10 的循环。
我试过了:
Parallel.each(AisSignal.all, in_processes: 8) do |signal|
Footprint.all.each do |footprint|
if footprint.cover([signal.lon, signal.lat])
signal.update(imo: 'in')
break
end
end
end
但它像普通块一样在 10 秒内运行。
我尝试从进程更改为线程,如下所示,但这会导致应用程序冻结。
Parallel.each(AisSignal.all, in_threads: 8) do |signal|
Footprint.all.each do |footprint|
if footprint.cover([signal.lon, signal.lat])
signal.update(imo: 'in')
break
end
end
end
我在 database.yml 中有 50 个池大小
让多个线程并行运行以更新记录的任何想法或方法。 我实际上需要更新更多记录,这可能需要几分钟时间。
线程和分叉通常不能很好地处理数据库连接。 如果处理不当,线程/进程可能会同时尝试使用相同的连接。
Parallel 在他们的文档中提到了这一点。 您需要使用连接池。
连接池将线程访问同步到有限数量的数据库连接。 基本思想是每个线程从池中检出一个数据库连接,使用该连接,然后重新检入该连接。ConnectionPool 是完全线程安全的,将确保一个连接不能同时被两个线程使用,只要正确遵守 ConnectionPool 的合同。 它还将处理线程数多于连接数的情况:如果所有连接都已签出,并且线程尝试签出连接,则 ConnectionPool 将等待其他线程签入连接。
Parallel.each(AisSignal.all, in_threads: 8) do |signal|
ActiveRecord::Base.connection_pool.with_connection do
Footprint.all.each do |footprint|
if footprint.cover([signal.lon, signal.lat])
signal.update(imo: 'in')
break
end
end
end
end
请注意,此代码非常低效。
AisSignal
表。Footprint
表。 它将使用大量 memory,并将在 s*f 时间内运行,其中s
是信号数, f
是足迹数。
您可以通过将Footprint.all.each
替换为Footprint.find_each
来减少 memory 占用空间。 这将分批加载行。
线程不是使数据库查询更快的方法。 根本问题是您在 Ruby 中多次扫描足迹,而不是让数据库执行此操作。 if footprint.cover([signal.lon, signal.lat])
应该改为 where 子句。
AisSignal.find_each do |signal|
# With ... being the equivalent of `cover([signal.lon, signal.lat])`
# as a where clause.
signal.update!(imo: 'in') if Footprint.exists?(...)
end
这可以作为连接更快地完成。
# ... is the equivalent of `cover([signal.lon, signal.lat])`
AisSignal.joins("inner join footprints on ...").update_all(imo: 'in')
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.