在 Redshift 中取消嵌套 json 导致查询计划中出现嵌套循环

问题描述

我的表中有一个名为“数据”的列,其中包含 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。 下面是代码

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,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')
),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,'r',true) as r,'s',true)::float8 as s  
from joined J
)
select * from parsed 

当记录数量很少时,上面的代码可以工作,但这需要一天多的时间才能运行,并且 cpu 利用率(在红移中)达到 100%,如果我输入日期,甚至使用的磁盘空间也达到 100%在最近两年之间等。或者如果记录数量很大。

任何人都可以提出任何替代方法来在 redshift 中取消上述 JSON 对象的嵌套。

我的查询计划是这样说的:

查询计划中的嵌套循环联接 - 检查联接谓词以避免笛卡尔积

目标:在不使用任何交叉连接的情况下取消嵌套 输入:具有 JSON 的数据列

"tt":"452.95","c":"38"}]}

输出应该是例如 来自上述 json 的 tr,s 列

解决方法

您想取消嵌套存储在 json 数组中的最多 1000 条 json 记录,但嵌套循环连接花费的时间太长。

根本问题可能是您的数据模型。您已将结构化记录(称为“记录”)存储在半结构化文本元素 (json) 内、结构化柱状数据库的列中。你想对这些你没有描述的隐藏记录执行一些操作,但这就是问题所在。列式数据库针对执行以读取为中心的分析查询进行了优化,但您需要将这些 json 内部记录扩展为 Redshift 行(记录),这基本上是一个写入操作。这不利于数据库的优化。

与集群上的磁盘存储空间相比,这种不断扩展的数据的大小也很大,这就是磁盘被填满的原因。您的 CPU 可能正在旋转解包 jsons 并管理过载的磁盘和内存容量。在磁盘填满的边缘,Redshift 转向以执行速度为代价优化磁盘空间利用率的模式。如果您可以避免这种影响,更大的集群可能会显着加快执行速度,但这会花费您可能没有预算的资金。不是理想的解决方案。

可以提高查询速度的一个方面不是携带所有数据。您在整个查询过程中都保持 raw.* 和 J.* ,但不清楚您是否需要这些。由于问题的一部分是执行期间的数据大小,并且此执行包括循环连接,因此您通过携带所有这些数据(包括原始 jsons)使执行变得更加困难。

摆脱这种情况的最佳方法是更改​​您的数据模型,并在摄取时将这些 json 内部记录扩展为 Redshift 记录。 Json 数据适用于很少使用的信息或仅在数据较小的查询结束时才需要的信息。对于如此大量的数据,在查询的输入端需要扩展的 json 并不是 Redshift 中 json 的好用例。 json 中的这些“记录”中的每一个都是记录,如果您需要将它们作为查询输入处理,则需要按原样存储。

现在您想知道在您的情况下是否有一些巧妙的方法来解决这个问题,答案是“不太可能,但可能”。您能否描述一下您如何使用查询中的最终值(t、r 和 s)?如果您只是使用此数据的某些方面(最大值或总和或...),那么可能有一种方法可以在没有大型嵌套循环连接的情况下获得答案。但是,如果您需要所有值,则没有其他方法可以获得这些 AFAIK。对数据过程中接下来发生的事情的描述可能会带来这样的机会。