【问题标题】:Using Large Look up table使用大查找表
【发布时间】:2018-04-25 10:03:46
【问题描述】:

问题陈述:

我有两个表 - Data (40 cols) 和 LookUp(2 cols) 。我需要在带有查找表的数据表中使用 col10 来提取相关值。 但是我不能让 equi join 。我需要一个基于 like/contains 的连接,因为查找表中的值仅包含数据表中值的部分内容而不是完整值。因此需要一些基于正则表达式的匹配。

数据大小:

  1. 数据表:大约 - 23 亿个条目(1 TB 数据)
  2. 查表:大约 140 万个条目(50 MB 数据)

方法一:

1.使用数据库(我使用的是 Google Big Query)- 基于 like 的 Join 需要接近 3 小时,但它没有返回任何结果。我相信基于正则表达式的连接会导致笛卡尔连接。

  1. 使用 Apache Beam/Spark - 我尝试为查找表构建一个 Trie,然后将其共享/广播到工作节点。但是,使用这种方法时,由于创建了太多字符串,我会遇到 OOM。我尝试将每个工作节点的内存增加到 4GB+,但无济于事。 我正在使用 Trie 提取最长的匹配前缀。

我愿意使用其他技术,如 Apache spark、Redis 等。 请建议我如何处理这个问题。

此处理需要每天执行,因此需要优化时间和资源。

【问题讨论】:

  • 尝试一些信封样式的计算。在单个 CPU 内核上,您认为每秒可以计算多少个字符串比较?将该数字乘以 23 亿,然后将该结果乘以 140 万。
  • 感谢您的回复。但是,我期待更多关于可以更有效地解决问题的替代算法或设计方法。
  • 很高兴看到您的用例的更多详细信息!任何域细节?查找表中 col10 和 2 cols 的任何示例?有时小细节可以改变游戏规则!
  • 如何为主表使用 BQ 分区,然后为每个分区预先计算该值并将其存储回 BQ 中?理论上,您将使用查找表连接更小的表/分区。类似的东西?更多信息会有所帮助。
  • @MatjazMuhic - 数据用于单个分区。整个表在 100+TB 中。

标签: apache-spark google-bigquery google-cloud-dataflow apache-beam lookup-tables


【解决方案1】:

但是我不能让 equi 加入

以下只是为您提供一个想法,以探索在纯 BigQuery 中解决您的 equi join 相关问题

它基于我从您的 cmets 中得出的假设 - 并且涵盖了当您从最右到左寻找最长匹配时的用例 - 中间的匹配不合格

方法是反转 url (col10) 和 shorted_url (col2) 字段,然后 SPLIT() 它们和 UNNEST() 保留位置

UNNEST(SPLIT(REVERSE(field), '.')) part WITH OFFSET position  

完成此操作后,您现在可以执行equi join,这可能会在一定程度上解决您的问题。
因此,您按部分和位置加入,然后 GROUP BY 原始 url 和 shorted_url,同时只留下那些组的匹配数等于 shorteded_url 中的部分数,最后您 GROUP BY url 并只留下匹配部分数量最多的条目

希望这可以帮助:o)

这适用于 BigQuery 标准 SQL

#standardSQL
WITH data_table AS (
  SELECT 'cn456.abcd.tech.com' url UNION ALL
  SELECT 'cn457.abc.tech.com' UNION ALL
  SELECT 'cn458.ab.com'
), lookup_table AS (
  SELECT 'tech.com' shortened_url, 1 val UNION ALL
  SELECT 'abcd.tech.com', 2
), data_table_parts AS (
  SELECT url, x, y
  FROM data_table, UNNEST(SPLIT(REVERSE(url), '.')) x WITH OFFSET y
), lookup_table_parts AS (
  SELECT shortened_url, a, b, val, 
    ARRAY_LENGTH(SPLIT(REVERSE(shortened_url), '.')) len
  FROM lookup_table, UNNEST(SPLIT(REVERSE(shortened_url), '.')) a WITH OFFSET b
)
SELECT url, 
  ARRAY_AGG(STRUCT(shortened_url, val) ORDER BY weight DESC LIMIT 1)[OFFSET(0)].* 
FROM (
  SELECT url, shortened_url, COUNT(1) weight, ANY_VALUE(val) val
  FROM data_table_parts d
  JOIN lookup_table_parts l
  ON x = a AND y = b
  GROUP BY url, shortened_url
  HAVING weight = ANY_VALUE(len)
)
GROUP BY url  

结果为

Row url                 shortened_url   val  
1   cn457.abc.tech.com  tech.com        1    
2   cn456.abcd.tech.com abcd.tech.com   2    

【讨论】:

  • 感谢您的回答。虽然查询返回非常小的样本表的结果,但是对于更大的样本表来说它花费的时间太长,最终它以 - (查询超出资源限制。)大查询计划在查询执行期间创建了太多的重新分区阶段。我认为大查询可能无法处理这个问题。 Spark/Beam 可以在这种情况下提供帮助吗?
  • 这是一个非常有创意的解决问题的方法,但是相当的蛮力,并且join确实会爆炸行数,而且HAVING只会在爆炸后过滤。当您拆分查找表部分时,您可以安全地过滤掉 offset = 0 的位置,因为大概会是“com”、“org”等片段。更进一步,可以预先生成查找表部分,过滤掉重复项,例如“com”、“co”、“uk.co”。如果您的查找有 3 列,URL,shorted_URL,val,那么您可以运行一个按 url 分区的窗口函数,按 shorted_URL desc 排序。仍然是一个巨大的搜索空间。
  • BigQuery 的执行计划器能否重新排列 CTE,或者它是否像其他数据库引擎一样将它们视为黑盒子?它可以解释所有的重新分区阶段。
猜你喜欢
  • 1970-01-01
  • 2015-05-03
  • 2011-10-18
  • 1970-01-01
  • 1970-01-01
  • 2019-12-26
  • 1970-01-01
  • 1970-01-01
  • 2012-10-22
相关资源
最近更新 更多