我正在编写UDF来处理Google Analytics数据,并在尝试处理多行时收到“UDF内存不足”错误消息。我下载了原始数据并找到了最大的记录,并尝试运行我的UDF查询,并取得了成功。一些行具有多达500个嵌套命中,并且命中记录的大小(到目前为止,原始GA数据的每一行的最大组件)似乎确实对我在获得错误之前可以处理的行数有影响
例如,查询
select
user.ga_user_id,
ga_session_id,
...
from
temp_ga_processing(
select
fullVisitorId,
visitNumber,
...
from [79689075.ga_sessions_20160201] limit 100)
返回错误,但
from [79689075.ga_sessions_20160201] where totals.hits = 500 limit 1)
没有。
我觉得任何内存限制都是每行?我尝试了几种技术,例如在row = null;
之前设置emit(return_dict);
(其中return_dict是已处理的数据),但无济于事。
UDF本身并没有做任何花哨的事;我把它贴在这里,但它的长度约为45 kB。它基本上做了很多事情:
function temp_ga_processing(row, emit) {
topic_id = -1;
hit_numbers = [];
first_page_load_hits = [];
return_dict = {};
return_dict["user"] = {};
return_dict["user"]["ga_user_id"] = row.fullVisitorId;
return_dict["ga_session_id"] = row.fullVisitorId.concat("-".concat(row.visitNumber));
for(i=0;i<row.hits.length;i++) {
hit_dict = {};
hit_dict["page"] = {};
hit_dict["time"] = row.hits[i].time;
hit_dict["type"] = row.hits[i].type;
hit_dict["page"]["engaged_10s"] = false;
hit_dict["page"]["engaged_30s"] = false;
hit_dict["page"]["engaged_60s"] = false;
add_hit = true;
for(j=0;j<row.hits[i].customMetrics.length;j++) {
if(row.hits[i].customDimensions[j] != null) {
if(row.hits[i].customMetrics[j]["index"] == 3) {
metrics = {"video_play_time": row.hits[i].customMetrics[j]["value"]};
hit_dict["metrics"] = metrics;
metrics = null;
row.hits[i].customDimensions[j] = null;
}
}
}
hit_dict["topic"] = {};
hit_dict["doctor"] = {};
hit_dict["doctor_location"] = {};
hit_dict["content"] = {};
if(row.hits[i].customDimensions != null) {
for(j=0;j<row.hits[i].customDimensions.length;j++) {
if(row.hits[i].customDimensions[j] != null) {
if(row.hits[i].customDimensions[j]["index"] == 1) {
hit_dict["topic"] = {"name": row.hits[i].customDimensions[j]["value"]};
row.hits[i].customDimensions[j] = null;
continue;
}
if(row.hits[i].customDimensions[j]["index"] == 3) {
if(row.hits[i].customDimensions[j]["value"].search("doctor") > -1) {
return_dict["logged_in_as_doctor"] = true;
}
}
// and so on...
}
}
}
if(row.hits[i]["eventInfo"]["eventCategory"] == "page load time" && row.hits[i]["eventInfo"]["eventLabel"].search("OUTLIER") == -1) {
elre = /(?:onLoad|pl|page):(\d+)/.exec(row.hits[i]["eventInfo"]["eventLabel"]);
if(elre != null) {
if(parseInt(elre[0].split(":")[1]) <= 60000) {
first_page_load_hits.push(parseFloat(row.hits[i].hitNumber));
if(hit_dict["page"]["page_load"] == null) {
hit_dict["page"]["page_load"] = {};
}
hit_dict["page"]["page_load"]["sample"] = 1;
page_load_time_re = /(?:onLoad|pl|page):(\d+)/.exec(row.hits[i]["eventInfo"]["eventLabel"]);
if(page_load_time_re != null) {
hit_dict["page"]["page_load"]["page_load_time"] = parseFloat(page_load_time_re[0].split(':')[1])/1000;
}
}
// and so on...
}
}
row = null;
emit return_dict;
}
作业ID是真实的:bquijob_4c30bd3d_152fbfcd7fd
答案 0 :(得分:1)
2016年8月更新:我们推出了一项更新,允许JavaScript工作人员使用两倍的RAM。我们将继续监控JS OOM失败的工作,看看是否需要增加更多工作;在此期间,如果您有其他工作因OOM失败,请告诉我们。谢谢!
更新:此问题与我们对UDF代码大小的限制有关。看起来V8的UDF代码的优化+重新编译通道会生成一个比我们的限制更大的数据段,但这只发生在UDF运行时足够的时候#34;行数。我本周会与V8团队会面,进一步深入了解细节。
@Grayson - 我能够成功地在整个20160201表上运行您的工作;查询需要1-2分钟才能执行。你能否证实这对你有用?
我们收到了一些类似问题的报告,这些问题似乎与处理过的#行有关。对不起,我很抱歉;我将对我们的JavaScript运行时进行一些分析,以尝试查找内存是否泄漏以及泄漏的位置。请继续关注分析。
与此同时,如果您能够隔离导致错误的任何特定行,那么这也会非常有用。
答案 1 :(得分:0)
如果UDF具有大量的if / then级别,UDF将在除了非常小的数据集之外的任何内容上失败,例如:
if(){
.... if(){
......... if(){
等
我们必须追踪并删除最深的if / then语句。
但是,这还不够。此外,当您将数据传递到UDF时,在所有变量上运行“GROUP EACH BY”。这将迫使BQ将输出发送给多个“工人”。否则它也会失败。
我浪费了3天的时间来讨论这个烦人的错误。哎呀。
答案 2 :(得分:0)
我喜欢在BigQuery中解析我的日志的概念,但是我遇到了同样的问题,我得到了
错误:查询执行期间超出了资源。
作业ID是bigquery-looker:bquijob_260be029_153dd96cfdb,如果这一点有帮助。
我写了一个非常基本的解析器做了一个简单的匹配并返回行。在10K行数据集上工作正常,但在尝试针对3M行日志文件运行时,我的资源就会耗尽。
有关解决方案的任何建议吗?
这是javascript代码。
function parseLogRow(row, emit) {
r = (row.logrow ? row.logrow : "") + (typeof row.l2 !== "undefined" ? " " + row.l2 : "") + (row.l3 ? " " + row.l3 : "")
ts = null
category = null
user = null
message = null
db = null
found = false
if (r) {
m = r.match(/^(\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\.\d\d\d (\+|\-)\d\d\d\d) \[([^|]*)\|([^|]*)\|([^\]]*)\] :: (.*)/ )
if( m){
ts = new Date(m[1])/1000
category = m[3] || null
user = m[4] || null
db = m[5] || null
message = m[6] || null
found = true
}
else {
message = r
found = false
}
}
emit({
ts: ts,
category: category,
user: user,
db: db,
message: message,
found: found
});
}
bigquery.defineFunction(
'parseLogRow', // Name of the function exported to SQL
['logrow',"l2","l3"], // Names of input columns
[
{'name': 'ts', 'type': 'timestamp'}, // Output schema
{'name': 'category', 'type': 'string'},
{'name': 'user', 'type': 'string'},
{'name': 'db', 'type': 'string'},
{'name': 'message', 'type': 'string'},
{'name': 'found', 'type': 'boolean'},
],
parseLogRow // Reference to JavaScript UDF
);