JSON 双数组列

JSON column for array of double

我正在为分析应用程序设计数据库。我的数据是从 CSV 文件加载的。此文件包含名为 'feed'.

的对象的一系列双精度值(可能超过 100k 个值)

我想将这些双精度数组存储到 PostgreSQL 的 JSON 列和 "partition" 数据中。我将选择一个因子数(例如:1000),这意味着对于每个 JSON,它最多包含 1000 个值。因此,如果您在 CSV 中有 3000 个值,那么您将有 3 行,每行将包含一个 JSON 的 1000 个值,如下所示:

Table feed
----------------------------------------
| id | data |
| 1  | { data: [1,2,3,4...1000]}
| 2  | { data: [1001,1002,...,2000]}
| 3  | { data: [2001,2002,...,3000]}

如果您想更新任何值,那么我将使用新值复制每个 JSON 中的数组,其他值将为 -1。

例如,如果要将值 2002(位于索引 1)更改为 4500,则 table 将为:

Table feed
----------------------------------------
| id | data |
| 1  | { data: [1,2,3,4...1000]}
| 2  | { data: [1001,1002,...,2000]}
| 3  | { data: [2001,2002,...,3000], new_data: [-1,4500,-1,-1...]}

也许我会添加一些额外的元数据列来描述 JSON 数据,例如 array_length、is_modifed...以便于处理。

我不知道这个设计是否可以轻松地进行 CRUD 操作?它对实时渲染和更新图表有好处吗?因为读、写、更新……这个数组数据太频繁了。任何人都可以给我一些建议吗?

谢谢

在您描述的场景中使用 JSON 数据是一个非常糟糕的主意。

JSON is a lightweight data-interchange format 因此作为 数据存储 和 [=38= 效率不是特别高]数据操作格式和PostgreSQL作为关系数据存储模型不是特别suitable 用于 JSON 操作(JSON 具有分层数据结构)或大型数组。

您的想法效率不高的几个更具体的原因:

  • 将 1,000 个双精度值放入一个数组中会产生超过 8,000 字节的数据结构(假设 jsonb 数据类型:双精度为 8,000 字节,结构本身及其描述符有一些开销;常规 json 很可能会更大,因为每个值至少有 8 个字符来准确描述每个值的大小)。这意味着 table 将被 TOASTed,这会导致性能下降。
  • 更新数组中的单个值需要重写整个记录(在主 table 和 TOAST table 中),效率极低。

最好在 PostgreSQL 中使用一个简单的关系结构,并使用触发器来实现您的逻辑。如果您的图表客户端需要 JSON 文档作为输入,那么 PostgreSQL 可以生成该 on-the-fly。例如:

CREATE TABLE feed (
    "index"    integer,
    value      double precision
);

CREATE FUNCTION trf_upd_feed RETURNS trigger AS $$
BEGIN
    -- This DELETE statement throws out unwanted data (the -1 values in your example)
    -- Usually there will be very few rows (just 1?) that get deleted
    DELETE FROM feed
    WHERE ("index" - 1) / 1000 = (NEW."index" - 1) / 1000;  -- integer division!
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER tr_feed_update
BEFORE UPDATE ON feed
FOR EACH ROW EXECUTE PROCEDURE trf_upd_feed();

当您需要图表数据时,您可以 运行 一个简单的查询来获取值切片的数据作为 JSON 对象:

SELECT json_build_object('data', arr) AS json_data
FROM (
    SELECT json_agg(coalesce(feed.value, -1)) AS arr
    FROM generate_series(1001, 2000) i(x)
    LEFT JOIN feed ON feed."index" = i.x) sub;

这总体上效率更高,因为您不需要 TOAST tables 并且您不存储没有任何意义的数据(所有 -1 值)。

备选

根据您的描述,我了解到您的图表客户端维护自己的值缓存,然后定期轮询数据库中的新数据。如果是这样的话,你应该改变你的逻辑。您不应在更新时更改值(删除记录),而应在图表应用程序读取数据时这样做;为此,您需要一个函数而不是更新触发器(如果您已经创建了它,请删除它):

CREATE FUNCTION chart_data (start_idx integer, end_idx integer) RETURNS json AS $$
DECLARE
    json_data json;
BEGIN
    SELECT json_build_object('data', arr) INTO json_data
    FROM (
        SELECT json_agg(coalesce(feed.value, -1)) AS arr
        FROM generate_series(start_idx, end_idx) i(x)
        LEFT JOIN feed ON feed."index" = i.x) sub;

    -- Data has been read, so now it can be deleted
    DELETE FROM feed
    WHERE "index" BETWEEN start_idx AND end_idx;

    RETURN json_data;
END;
$ LANGUAGE plpgsql VOLATILE STRICT;

然后简单地调用`SELECT chart_data (1001, 2000);