分区

时间:2016-09-15 17:30:55

标签: google-bigquery

我在一个表中有340 GB的数据(270天的数据)。现在计划将此数据移动到分区表。

这意味着我将拥有270个分区。将此数据移动到分区表的最佳方法是什么?

我不想运行270次查询,这是非常昂贵的操作。所以寻找优化的解决方案。

我有这样的多个表。我需要将所有这些表迁移到分区表。

谢谢,

3 个答案:

答案 0 :(得分:5)

我看到三个选项

  1. 直接提取超出原始表格:
    操作(要运行的查询数量)=天[提取] = 270
    完全扫描(在原始表的完整扫描中测量的扫描数据量)=天= 270
    成本,$ = $ 5 x表格大小,TB x全扫描= $ 5 x 0.34 x 270 = $ 459.00

  2. 分层(递归)提取 :(在Mosha的回答中描述)
    操作 = 2 ^ log2(天) - 2 = 510
    完全扫描 = 2 * log2(天)= 18
    成本,$ = $ 5 x桌面尺寸,TB x全扫描= $ 5 x 0.34 x 18 = $ 30.60

  3. 集群提取 :(我将在一秒内描述)
    操作 =天+ 1 = 271
    完全扫描 = [始终] 2 = 2
    成本,$ = $ 5 x表格大小,TB xFull Scans = $ 5 x 0.34 x 2 = $ 3.40

  4. 摘要

    Method                              Actions Total Full Scans    Total Cost  
    Direct Extraction                       270              270       $459.00
    Hierarchical(recursive) Extraction      510               18        $30.60
    Clustered Extraction                    271                2         $3.40  
    

    当然,出于大多数实际目的,Mosha的解决方案是可行的(我在大多数情况下使用它)
    这是相对简单和直接的

    即使你需要运行查询 510 次 - 查询是"相对"简单和编排逻辑很容易实现,无论您经常使用什么客户端 节省成本非常明显! 从 $ 460 下降到 $ 31
    几乎 15 次下降!

    万一你 -
    a)希望进一步降低成本9倍(因此总计将降低x135倍) b)喜欢玩乐和更多挑战 - 看看第三个选项

      

    “集群提取”说明

    创意/目标
    第1步
    我们希望将原始表格转换为另一个具有270列的[单个]表格 - 一天为一列 每列将从原始表中保存一天的序列化行 此新表中的总行数将等于大多数"重"的行数。天
    这只需要一次查询(参见下面的示例)并进行一次完整扫描

    第2步 在这个新表准备就绪之后 - 我们将逐日查询各自的列并写入最终的每日表(每日表的模式与原始表的模式完全相同,所有这些表都可以预先创建) 这将需要运行270个查询,扫描大致相当(这实际上取决于您的架构的复杂程度,因此可能会有所不同)到原始表的一个完整大小 在查询列时 - 我们需要对行的值进行反序列化并将其解析回原始方案

    非常简化示例 :( 在此使用BigQuery标准SQL

    这个例子的目的只是为了给你一个方向,如果你会发现有趣的想法 序列化/反序列化极其简化,以便专注于创意而不是特定的实现,这可能因情况而异(主要取决于架构

    因此,假设原始表(theTable)看起来像下面

      SELECT  1 AS id, "101" AS x, 1 AS ts UNION ALL
      SELECT  2 AS id, "102" AS x, 1 AS ts UNION ALL
      SELECT  3 AS id, "103" AS x, 1 AS ts UNION ALL
      SELECT  4 AS id, "104" AS x, 1 AS ts UNION ALL
      SELECT  5 AS id, "105" AS x, 1 AS ts UNION ALL
      SELECT  6 AS id, "106" AS x, 2 AS ts UNION ALL
      SELECT  7 AS id, "107" AS x, 2 AS ts UNION ALL
      SELECT  8 AS id, "108" AS x, 2 AS ts UNION ALL
      SELECT  9 AS id, "109" AS x, 2 AS ts UNION ALL
      SELECT 10 AS id, "110" AS x, 3 AS ts UNION ALL
      SELECT 11 AS id, "111" AS x, 3 AS ts UNION ALL
      SELECT 12 AS id, "112" AS x, 3 AS ts UNION ALL
      SELECT 13 AS id, "113" AS x, 3 AS ts UNION ALL
      SELECT 14 AS id, "114" AS x, 3 AS ts UNION ALL
      SELECT 15 AS id, "115" AS x, 3 AS ts UNION ALL
      SELECT 16 AS id, "116" AS x, 3 AS ts UNION ALL
      SELECT 17 AS id, "117" AS x, 3 AS ts UNION ALL
      SELECT 18 AS id, "118" AS x, 3 AS ts UNION ALL
      SELECT 19 AS id, "119" AS x, 4 AS ts UNION ALL
      SELECT 20 AS id, "120" AS x, 4 AS ts
    

    第1步 - 转换表并将结果写入tempTable

    SELECT 
      num, 
      MAX(IF(ts=1, ser, NULL)) AS ts_1, 
      MAX(IF(ts=2, ser, NULL)) AS ts_2, 
      MAX(IF(ts=3, ser, NULL)) AS ts_3, 
      MAX(IF(ts=4, ser, NULL)) AS ts_4
    FROM (
      SELECT 
        ts, 
        CONCAT(CAST(id AS STRING), "|", x, "|", CAST(ts AS STRING)) AS ser, 
        ROW_NUMBER() OVER(PARTITION BY ts ORDER BY id) num
      FROM theTable
    )
    GROUP BY num
    

    tempTable将如下所示:

    num    ts_1        ts_2         ts_3        ts_4     
    1   1|101|1     6|106|2     10|110|3    19|119|4     
    2   2|102|1     7|107|2     11|111|3    20|120|4     
    3   3|103|1     8|108|2     12|112|3        null     
    4   4|104|1     9|109|2     13|113|3        null     
    5   5|105|1        null     14|114|3        null     
    6      null        null     15|115|3        null     
    7      null        null     16|116|3        null     
    8      null        null     17|117|3        null     
    9      null        null     18|118|3        null    
    

    在这里,我使用简单串联进行序列化

    第2步 - 提取特定日期的行并将输出写入各自的日常表
    请注意:在下面的示例中 - 我们提取ts = 2的行:这对应于列ts_2

    SELECT
      r[OFFSET(0)] AS id,
      r[OFFSET(1)] AS x,
      r[OFFSET(2)] AS ts
    FROM (
      SELECT SPLIT(ts_2, "|") AS r 
      FROM tempTable 
      WHERE NOT ts_2 IS NULL
    )
    

    结果将如下所示(预期):

    id    x    ts    
    6   106     2    
    7   107     2    
    8   108     2    
    9   109     2   
    

    我希望我有更多的时间来写下来,所以如果缺少某些东西,不要判断重 - 这是更方向性的答案 - 但同时例子是非常合理的,如果你有简单的架构 - 几乎不需要额外的思考。当然,有了记录,架构中的嵌套东西 - 最具挑战性的部分是序列化/反序列化 - 但这就是有趣的 - 以及额外的节省

答案 1 :(得分:1)

如果您的数据位于分片表中(即后缀为YYYYmmdd),则可以使用"bq partition"命令。但是对于单个表中的数据 - 您必须多次扫描它,在分区键列上应用不同的WHERE子句。 我能想到的唯一优化就是分层次地进行,即代替270次查询,这将进行270次全表扫描 - 首先将表分成两半,然后每半次分成两半等。这样你就需要支付{{1全扫描。

转换完成后 - 可以删除所有临时表以消除额外的存储成本。

答案 2 :(得分:0)

我将在@Mikhail的答案中添加另一个第四个选项

DML查询

  • 操作= 1个要运行的查询
  • 完整扫描= 1
  • 成本= $ 5 x 0.34 = 1.7 $(比解决方案#1 \ o /便宜x270倍)

借助BiQuery的DML新功能,您可以在不执行仅对源表进行一次完整扫描的情况下将无分区表转换为分区表

为说明我的解决方案,我将使用BQ的公共表之一,即bigquery-public-data:hacker_news.comments。下面是表架构

name    | type      | description
_________________________________
id      | INTGER    | ...
_________________________________
by      | STRING    | ...
_________________________________
author  | STRING    | ...
_________________________________
...     |           |
_________________________________
time_ts | TIMESTAMP | human readable timestamp in UTC YYYY-MM-DD hh:mm:ss /!\ /!\ /!\
_________________________________
...     |           |
_________________________________

我们将基于 time_ts

comments表进行分区
#standardSQL
CREATE TABLE my_dataset.comments_partitioned
PARTITION BY DATE(time_ts)
AS 
SELECT *
FROM `bigquery-public-data:hacker_news.comments` 

我希望这会有所帮助:)