我在一个表中有340 GB的数据(270天的数据)。现在计划将此数据移动到分区表。
这意味着我将有270个分区。将此数据移动到分区表的最佳方法是什么?
我不想运行270个查询,这是非常昂贵的操作。所以寻找优化的解决方案。
我有这样的多个表。我需要将所有这些表迁移到分区表。
谢谢,
我看到三种选择
摘要
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 Standard 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
我希望我有更多的时间来写下来,所以如果缺少某些东西,不要判断重 - 这是更有方向性的答案 - 但同时示例是非常合理的,如果你有简单的架构 - 几乎没有额外的想法是必须的。当然有了记录,架构中的嵌套东西 - 最具挑战性的部分是序列化/反序列化 - 但这就是有趣的地方 - 以及额外的$节省
如果您的数据位于分片表中(即使用YYYYmmdd
后缀),您可以使用"bq partition"
命令。但是对于单个表中的数据 - 您必须多次扫描它,在分区键列上应用不同的WHERE子句。我能想到的唯一优化就是分层次地进行,即代替270次查询,这将进行270次全表扫描 - 首先将分割表分成两半,然后将每一半分成两半等。这样你就需要为2*log_2(270) = 2*9 = 18
全扫描付费。
转换完成后 - 可以删除所有临时表以消除额外的存储成本。
我将为@ Mikhail的回答添加另一个选项
DML QUERY
使用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`
我希望它有帮助:)