当我浏览API返回的数据集时,我遇到了大量内存峰值的问题。 API正在返回~150k记录,我一次请求10k记录并翻阅15页数据。数据是一个散列数组,每个散列包含25个具有~50个字符的字符串值的键。这个过程杀死了我的512mb Heroku dyno。
我有一个用于分页API响应数据集的方法。
def all_pages value_key = 'values', &block
response = {}
values = []
current_page = 1
total_pages = 1
offset = 0
begin
response = yield offset
#The following seems to be the culprit
values += response[value_key] if response.key? value_key
offset = response['offset']
total_pages = (response['totalResults'].to_f / response['limit'].to_f).ceil if response.key? 'totalResults'
end while (current_page += 1) <= total_pages
values
end
我将此方法称为:
all_pages("items") do |current_page|
get "#{data_uri}/data", query: {offset: current_page, limit: 10000}
end
我知道这是导致问题的数组的串联,因为删除该行允许进程在没有内存问题的情况下运行。我究竟做错了什么?整个数据集可能不超过20mb - 如何消耗所有的dyno内存?我该怎么做才能提高效率?
更新
响应如下:{"totalResults":208904,"offset":0,"count":1,"hasMore":true, limit:"10000","items":[...]}
更新2
使用report
运行显示以下内容:
[HTTParty] [2014-08-13 13:11:22 -0700] 200 "GET 29259/data" -
Memory 171072KB
[HTTParty] [2014-08-13 13:11:26 -0700] 200 "GET 29259/data" -
Memory 211960KB
... removed for brevity ...
[HTTParty] [2014-08-13 13:12:28 -0700] 200 "GET 29259/data" -
Memory 875760KB
[HTTParty] [2014-08-13 13:12:33 -0700] 200 "GET 29259/data" -
Errno::ENOMEM: Cannot allocate memory - ps ax -o pid,rss | grep -E "^[[:space:]]*23137"
更新3
我可以使用下面的基本脚本重新创建问题。该脚本硬编码为仅提取100k记录,并且已在本地VM上消耗超过512MB的内存。
#! /usr/bin/ruby
require 'uri'
require 'net/http'
require 'json'
uri = URI.parse("https://someapi.com/data")
offset = 0
values = []
begin
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.set_debug_output($stdout)
request = Net::HTTP::Get.new(uri.request_uri + "?limit=10000&offset=#{offset}")
request.add_field("Content-Type", "application/json")
request.add_field("Accept", "application/json")
response = http.request(request)
json_response = JSON.parse(response.body)
values << json_response['items']
offset += 10000
end while offset < 100_000
values
更新4
我做了一些改进,似乎有所帮助,但没有完全缓解这个问题。
1)结果使用symbolize_keys
消耗更少的内存。这是因为每个哈希的键是相同的,并且象征它们然后将它们解析为单独的字符串会更便宜。
2)切换到ruby-yajl
以进行JSON解析也会消耗更少的内存。
处理200k记录的内存消耗:
JSON.parse(response.body)
:861080KB(在完全耗尽内存之前)
JSON.parse(response.body, symbolize_keys: true)
:573580KB
Yajl::Parser.parse(response.body)
:357236KB
Yajl::Parser.parse(response.body, symbolize_keys: true)
:264576KB
但这仍然是一个问题。
我会为能够彻底回答这三个问题的人买啤酒!
提前多多感谢。
答案 0 :(得分:0)
您已确定在您的阵列中使用+=
的问题。因此,可能的解决方案是在不创建新数组的情况下添加数据。
values.push response[value_key] if response.key? value_key
或使用<<
values << response[value_key] if response.key? value_key
如果您真的想要一个新阵列,则应该只使用+=
。它看起来并不是真的想要一个新的数组,但实际上只是想要一个数组中的所有元素。