BigQuery UDF 内存在多行上超出错误,但在单行上工作正常

Posted

技术标签:

【中文标题】BigQuery UDF 内存在多行上超出错误,但在单行上工作正常【英文标题】:BigQuery UDF memory exceeded error on multiple rows but works fine on single row 【发布时间】:2016-02-20 02:17:45 【问题描述】:

我正在编写一个 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) 

没有。

我的印象是任何内存限制都是每行的?我尝试了几种技术,例如在emit(return_dict); 之前设置row = null;(其中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是realself-main:bquijob_4c30bd3d_152fbfcd7fd

【问题讨论】:

1. javascript 处理环境对每个查询的可用内存有限。累积过多本地状态的 UDF 查询可能会因内存耗尽而失败; 2. 处理单行时,UDF 输出的数据量应在 5 Mb 左右或更少; 3. 您可能需要提供您的 UDF 代码 @MikhailBerlyant 是的,我知道这些事情(在我开始写这篇文章之前,我曾多次阅读 Google 的 UDF 页面)。我在上面提到过,我在最大的一行上进行了尝试,这表明我每行返回 @Grayson :我不得不从示例中修改您的代码以使其运行(我无权访问您的 GCS 存储桶中的代码) - 但是这个示例确实适用于我在飞行中的更改。当更改在生产中时,我会在这里再次更新。 【参考方案1】:

2016 年 8 月更新:我们推出了一项更新,允许 JavaScript 工作线程使用两倍的 RAM。我们将继续监视因 JS OOM 失败的作业,看看是否需要增加更多;同时,如果您还有其他工作因 OOM 失败,请告诉我们。谢谢!

更新:这个问题与我们对 UDF 代码大小的限制有关。看起来 V8 的 UDF 代码的优化+重新编译过程生成的数据段大于我们的限制,但这仅在 UDF 运行“足够”行数时发生。本周我将与 V8 团队会面,以进一步深入了解细节。

@Grayson - 我能够成功地在整个 20160201 表上运行你的工作;查询需要 1-2 分钟才能执行。您能否确认这对您有效?


我们收到了一些类似问题的报告,这些问题似乎与已处理的 # 行有关。很抱歉给您带来麻烦;我将对我们的 JavaScript 运行时进行一些分析,以尝试找出内存是否泄漏以及在何处泄漏。请继续关注分析。

与此同时,如果您能够隔离导致错误的任何特定行,那也将非常有帮助。

【讨论】:

太好了,谢谢。失败的并不是任何特定的行(实际上只是任意行的组合)。您是否大致知道用于 JS 运行时的分析和剖析的 ETA 是多少? 这是我三月份调查的首选。 我从 3 月 1 日起一直不在办公室,昨天刚回来。现在调查这个! 我们的一个 UDF 也有同样的问题,并且还在我们能找到的最大记录上对其进行了测试,并且运行良好。在大量记录(~ 10 毫米记录)上运行时失败 @thomaspark “UDF 内存不足”错误真的很奇怪。我们使用相同的 UDF 运行相同的查询,有时会成功,有时会失败。我们还尝试在桌子上零散地运行失败的 UDF,首先在桌子的前半部分,然后在后半部分,并且它有效。我们还会在不使用时立即删除每个变量;使用更少的变量;一切都无济于事......顺便说一句,我们在一个有 3,500 行和 6.6MB 的表上运行它。该函数通过一个 JSON 字段并找到最小/最大值。没什么特别的【参考方案2】:

如果 UDF 有很多 if/then 级别,那么它会在除非常小的数据集之外的任何数据集上失败,例如: 如果 () .... if() .........if () 等等

我们必须追踪并删除最深的 if/then 语句。

但是,这还不够。此外,当您将数据传递到 UDF 时,对所有变量运行“GROUP EACH BY”。这将迫使 BQ 将输出发送给多个“工人”。否则也会失败。

我已经在这个烦人的错误上浪费了 3 天的时间。啊。

【讨论】:

坚持住,我可能会为您解决问题。如果您可以为失败的查询粘贴作业 ID,我会看到它适用于我的更新。【参考方案3】:

我喜欢在 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
);

【讨论】:

你应该将此作为新问题发布,否则我不知道如何回答这个问题!当你这样做时,我还建议提供日志行和预期输出的示例 我为您提供了潜在的解决方法。如果您将发布您的问题 - 我将能够分享它

以上是关于BigQuery UDF 内存在多行上超出错误,但在单行上工作正常的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript 内存不足错误:超出 UDF 线程内存限制-Snowflake

Bigquery UDF 重复查询。错误:标量子查询不能超过一列

谷歌 BigQuery UDF 错误

BigQuery GROUP BY/GROUP EACH BY 资源超出错误,但查询不包含 GROUP BY 运算符

在 bigquery 中创建一个 udf 以匹配数组输入

UDF 内存泄漏的解决方法