Столбец JSON для массива двойных

57
3

Я разрабатываю базу данных для приложения аналитики. Мои данные загружаются из файла CSV. Этот файл содержит ряд двойных значений (может быть, более 100 тыс. Значений) для объекта с именем "feed".

Я хочу сохранить эти массивы double в столбце JSON в PostgreSQL и "разбить" данные. Я выберу номер фактора (например: 1000), это означает, что для каждого JSON он содержит максимум 1000 значений. В результате, если у вас есть 3000 значений в CSV, тогда у вас будет 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 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? И это хорошо для рендеринга и обновления графика в реальном времени? Поскольку чтение, запись, обновление... эти данные массива так часто. Может ли кто-нибудь дать мне несколько советов по этому поводу?

благодаря

спросил(а) 2017-02-12T18:49:00+03:00 3 года, 8 месяцев назад
1
Решение
82

Использование данных JSON в описываемом вами сценарии - очень плохая идея.

JSON является легким форматом обмена данными и, как таковой, не особенно эффективен, как формат хранения данных и обработки данных, а PostgreSQL в качестве модели реляционного хранения данных не особенно подходит для JSON-манипуляции (JSON имеет иерархическую структуру данных) или больших массивов.

Еще несколько конкретных причин, по которым ваша идея неэффективна:

    Помещение 1000 двойных значений в массив дает структуру данных более 8000 байт (при условии, что jsonb данных jsonb: 8 000 байт для удвоений, некоторые накладные расходы для самой структуры и ее дескрипторов; обычный json, скорее всего, даже будет больше, поскольку каждое значение будет имеют не менее 8 символов, чтобы точно описывать величину каждого значения). Это означает, что таблица будет TOAST ed, что приведет к штрафу за производительность. Для обновления одного значения в массиве требуется переписать всю запись (в основной таблице и в таблице TOAST), что крайне неэффективно.

Вам гораздо лучше использовать простую реляционную структуру внутри PostgreSQL, с триггерами для реализации вашей логики. Если ваш графический клиент ожидает ввода документа JSON, PostgreSQL может сгенерировать его на лету. Например:

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, и вы не храните данные, которые ничего не значат (все значения -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);

ответил(а) 2017-02-12T20:03:00+03:00 3 года, 8 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема