在 Redshift 中取消嵌套 json 导致查询计划中出现嵌套循环
Unnesting a json in Redshift causing nested loop in the query plan
我的表中有一个名为 'data' 的列,其中包含 JSONs,如下所示:
{"tt":"452.95","records":[{"r":"IN184366","t":"812812819910","s":"129.37","d":"982.7","c":"83"},{"r":"IN183714","t":"8028028029093","s":"33.9","d":"892","c":"38"}]}
我已经编写了一个代码来将其取消嵌套到单独的列中,例如 tr、r、s。
下面是代码
with raw as (
SELECT json_extract_path_text(B.Data, 'records', true) as items
FROM tableB as B where B.date::timestamp between
to_timestamp('2019-01-01 00:00:00','YYYY-MM-DD HH24:MA:SS') AND
to_timestamp('2022-12-31 23:59:59','YYYY-MM-DD HH24:MA:SS')
UNION ALL
SELECT json_extract_path_text(C.Data, 'records', true) as items
FROM tableC as C where C.date-5 between
to_timestamp('2019-01-01 00:00:00','YYYY-MM-DD HH24:MA:SS') AND
to_timestamp('2022-12-31 23:59:59','YYYY-MM-DD HH24:MA:SS')
),
numbers as (
SELECT ROW_NUMBER() OVER (ORDER BY TRUE)::integer- 1 as ordinal
FROM <any_random_table> limit 1000
),
joined as (
select raw.*,
json_array_length(orders.items, true) as number_of_items,
json_extract_array_element_text(
raw.items,
numbers.ordinal::int,
true
) as item
from raw
cross join numbers
where numbers.ordinal <
json_array_length(raw.items, true)
),
parsed as (
SELECT J.*,
json_extract_path_text(J.item, 'tr',true) as tr,
json_extract_path_text(J.item, 'r',true) as r,
json_extract_path_text(J.item, 's',true)::float8 as s
from joined J
)
select * from parsed
上面的代码在记录数量较少时有效,但是 运行 和 CPU 利用率(在红移中)达到 100% 甚至磁盘 space 如果我将日期放在最近两年之间等等,或者如果记录数量很大,则使用也达到 100%。
任何人都可以提出任何替代方法来在 redshift 中取消嵌套 JSON 对象。
我的查询计划是这样的:
Nested Loop Join in the query plan - review the join predicates to avoid Cartesian products
目标:在不使用任何交叉连接的情况下取消嵌套
输入:具有 JSON
的数据列
"tt":"452.95","records":[{"r":"IN184366","t":"812812819910","s":"129.37","d":"982.7","c":"83"},{"r":"IN183714","t":"8028028029093","s":"33.9","d":"892","c":"38"}]}
输出应该是例如
上面的 tr,r,s 列 json
您想取消嵌套 json 存储在 json 数组中的最多 1000 条记录,但嵌套循环连接花费的时间太长。
根本问题可能是您的数据模型。您已将结构化记录(称为“记录”)存储在半结构化文本元素 (json) 中,位于结构化列式数据库的列中。您想对这些您未描述的隐藏记录执行一些操作,但这就是问题所在。列式数据库针对执行以读取为中心的分析查询进行了优化,但您需要将这些 json 内部记录扩展为 Redshift 行(记录),这从根本上是一种写入操作。这不利于数据库的优化。
与集群上的磁盘存储相比,此扩展数据的大小也很大,这就是磁盘填满的原因。您的 CPU 可能正在旋转解压缩 jsons 并管理超载的磁盘和内存容量。在填满磁盘的边缘,Redshift 转变为以牺牲执行速度为代价优化磁盘 space 利用率的模式。如果可以避免这种影响,更大的集群可能会给您带来明显更快的执行速度,但这会花费您可能没有预算的钱。不是一个理想的解决方案。
可以提高查询速度的一个方面是不携带所有数据。您在整个查询过程中保留 raw.* 和 J.*,但不清楚您是否需要这些。由于部分问题是执行期间的数据大小,并且此执行包括循环连接,因此您通过携带所有这些数据(包括原始 jsons)使执行变得更加困难。
摆脱这种情况的最佳方法是更改您的数据模型并在摄取时将这些 json 内部记录扩展到 Redshift 记录中。 Json 数据适用于很少使用的信息或仅在数据较小的查询结束时才需要的信息。对于如此大量的数据,在查询的输入端需要扩展 json 对于 Redshift 中的 json 来说不是好的用例。 json 中的每个“记录”都是记录,如果您需要将它们作为查询输入进行处理,则需要这样存储。
现在你想知道在你的案例中是否有一些巧妙的方法来解决这个问题,答案是“不太可能,但也许”。您能否描述一下您如何在查询中使用最终值(t、r 和 s)?如果您只是使用此数据的某些方面(最大值或总和或...),那么可能有一种方法可以在没有大型嵌套循环连接的情况下获得答案。但是,如果您需要所有的值,那么就没有其他方法可以获取这些 AFAIK。对数据流程中接下来的内容的描述可能会带来这样的机会。
我的表中有一个名为 'data' 的列,其中包含 JSONs,如下所示:
{"tt":"452.95","records":[{"r":"IN184366","t":"812812819910","s":"129.37","d":"982.7","c":"83"},{"r":"IN183714","t":"8028028029093","s":"33.9","d":"892","c":"38"}]}
我已经编写了一个代码来将其取消嵌套到单独的列中,例如 tr、r、s。 下面是代码
with raw as (
SELECT json_extract_path_text(B.Data, 'records', true) as items
FROM tableB as B where B.date::timestamp between
to_timestamp('2019-01-01 00:00:00','YYYY-MM-DD HH24:MA:SS') AND
to_timestamp('2022-12-31 23:59:59','YYYY-MM-DD HH24:MA:SS')
UNION ALL
SELECT json_extract_path_text(C.Data, 'records', true) as items
FROM tableC as C where C.date-5 between
to_timestamp('2019-01-01 00:00:00','YYYY-MM-DD HH24:MA:SS') AND
to_timestamp('2022-12-31 23:59:59','YYYY-MM-DD HH24:MA:SS')
),
numbers as (
SELECT ROW_NUMBER() OVER (ORDER BY TRUE)::integer- 1 as ordinal
FROM <any_random_table> limit 1000
),
joined as (
select raw.*,
json_array_length(orders.items, true) as number_of_items,
json_extract_array_element_text(
raw.items,
numbers.ordinal::int,
true
) as item
from raw
cross join numbers
where numbers.ordinal <
json_array_length(raw.items, true)
),
parsed as (
SELECT J.*,
json_extract_path_text(J.item, 'tr',true) as tr,
json_extract_path_text(J.item, 'r',true) as r,
json_extract_path_text(J.item, 's',true)::float8 as s
from joined J
)
select * from parsed
上面的代码在记录数量较少时有效,但是 运行 和 CPU 利用率(在红移中)达到 100% 甚至磁盘 space 如果我将日期放在最近两年之间等等,或者如果记录数量很大,则使用也达到 100%。
任何人都可以提出任何替代方法来在 redshift 中取消嵌套 JSON 对象。
我的查询计划是这样的:
Nested Loop Join in the query plan - review the join predicates to avoid Cartesian products
目标:在不使用任何交叉连接的情况下取消嵌套 输入:具有 JSON
的数据列"tt":"452.95","records":[{"r":"IN184366","t":"812812819910","s":"129.37","d":"982.7","c":"83"},{"r":"IN183714","t":"8028028029093","s":"33.9","d":"892","c":"38"}]}
输出应该是例如 上面的 tr,r,s 列 json
您想取消嵌套 json 存储在 json 数组中的最多 1000 条记录,但嵌套循环连接花费的时间太长。
根本问题可能是您的数据模型。您已将结构化记录(称为“记录”)存储在半结构化文本元素 (json) 中,位于结构化列式数据库的列中。您想对这些您未描述的隐藏记录执行一些操作,但这就是问题所在。列式数据库针对执行以读取为中心的分析查询进行了优化,但您需要将这些 json 内部记录扩展为 Redshift 行(记录),这从根本上是一种写入操作。这不利于数据库的优化。
与集群上的磁盘存储相比,此扩展数据的大小也很大,这就是磁盘填满的原因。您的 CPU 可能正在旋转解压缩 jsons 并管理超载的磁盘和内存容量。在填满磁盘的边缘,Redshift 转变为以牺牲执行速度为代价优化磁盘 space 利用率的模式。如果可以避免这种影响,更大的集群可能会给您带来明显更快的执行速度,但这会花费您可能没有预算的钱。不是一个理想的解决方案。
可以提高查询速度的一个方面是不携带所有数据。您在整个查询过程中保留 raw.* 和 J.*,但不清楚您是否需要这些。由于部分问题是执行期间的数据大小,并且此执行包括循环连接,因此您通过携带所有这些数据(包括原始 jsons)使执行变得更加困难。
摆脱这种情况的最佳方法是更改您的数据模型并在摄取时将这些 json 内部记录扩展到 Redshift 记录中。 Json 数据适用于很少使用的信息或仅在数据较小的查询结束时才需要的信息。对于如此大量的数据,在查询的输入端需要扩展 json 对于 Redshift 中的 json 来说不是好的用例。 json 中的每个“记录”都是记录,如果您需要将它们作为查询输入进行处理,则需要这样存储。
现在你想知道在你的案例中是否有一些巧妙的方法来解决这个问题,答案是“不太可能,但也许”。您能否描述一下您如何在查询中使用最终值(t、r 和 s)?如果您只是使用此数据的某些方面(最大值或总和或...),那么可能有一种方法可以在没有大型嵌套循环连接的情况下获得答案。但是,如果您需要所有的值,那么就没有其他方法可以获取这些 AFAIK。对数据流程中接下来的内容的描述可能会带来这样的机会。