Оптимизация пространства хранения, скорости запросов, данных столбца JSON в таблице Postgres - PullRequest
0 голосов
/ 24 октября 2018

Рассмотрим следующую таблицу, в которой записаны изменения цен на разные товары, принадлежащие разным компаниям разных категорий.

     Column    |  Type  | Modifiers
-----------------+--------+-----------
 category_id   | bigint | not null
 product_id    | bigint | not null
 industry_id   | bigint | not null
 time          | bigint | not null
 price         | bigint | not null
 product_info  | json   | not null

Indexes:
    "price_change_pk" PRIMARY KEY, btree (category_id, product_id, price, "time")

Foreign-key constraints:
    "orders_industry_id" FOREIGN KEY (industry_id) REFERENCES industry_info(industry_id)
    "orders_product_id" FOREIGN KEY (product_id) REFERENCES device_info(product_id)
    "orders_category_id" FOREIGN KEY (categoy_id) REFERENCES category_info(category_id)

Для ясности значения столбца будут:

category_id- отдельная таблица будет иметь идентификатор (уникальное значение bigint), сопоставленный с именем категории - 100 категорий

(электроника, мода, здоровье, спорт, игрушки, книги)

industry_id- отдельная таблица будет иметь идентификатор (уникальное значение bigint), сопоставленный с именем отрасли - несколько тысяч отраслей в категории

(Nokia, Apple, Microsoft, PeterEngland, Rubik, Nivia, Cosco)

product_id - отдельная таблица будет иметь идентификатор (уникальное значение bigint), сопоставленный с именем продукта - миллионы продуктов в отрасли

time (время unix как bigint) - время, в котороецена была изменена,

price - несколько тысяч различных значений - (200, 10000, 14999, 30599, 450)

product_info - JSON, который содержит дополнительные деталипродукт(количество пар ключ / значение может варьироваться)

{seller:"ABC Assured", discount:10, model:XYZ, EMIoption:true, EMIvalue:12, festival_offer:28, market_stat:comingsoon}

Таблица запрашивается несколькими способами для анализа тенденции изменения цены продукта в виде графика в виде дня / недели / месяца в виде часа/ день / неделя / месяцТенденция может быть основана на нет.продуктов, при этом изменяются уникальные продукты.

Например, Google Sample Trend

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

Как

Keys (citext, bigint)
seller - 1
discount - 2
model - 3
EMIoption - 4
EMIvalue - 5
festival_offer - 6
...
...
currency - 25

Values (citext, bigint)
ABC Assured - 1
10 - 2
XYZ - 3
true - 4
12 - 5
28 - 6
comingsoon - 7
...
...
ZYX - 106
rupees - 107
american dollars - 108
canadian dollars - 109
Prime seller - 110

{seller:"ABC Assured", discount:10, model:XYZ, EMIoption:true, EMIvalue:12, festival_offer:28, market_stat:comingsoon, curreny: rupees}

становится

{"1":1, "2":2", "3":3, "4":4, "5":5, "6":6, "7":7, "25":107}


{seller:"Prime seller", discount:10, model:XYZ, EMIoption:true, EMIvalue:12, festival_offer:28, market_stat:comingsoon, curreny: "canadian dollars"}

становится

{"1":110, "2":2", "3":3, "4":4, "5":5, "6":6, "7":7, "25":109}


Дляоколо 20 МБ данных, он уменьшен примерно на 1,5 ГБ.

Увеличение количества ключей в ключе, увеличение серийных номеров.Поэтому я попытался сохранить десятичное число в шестнадцатеричном формате.

{"1":1, "2":2", "3":3, "4":4, "5":5, "6":6, "7":7, "25":107}

становится

{"1":1, "2":2", "3":3, "4":4, "5":5, "6":6, "7":7, "19":"6B"}


{"1":110, "2":2", "3":106, "4":4, "5":5, "6":6, "7":7, "25":109}

становится

{"1":, "2":2", "3":"6A", "4":4, "5":5, "6":6, "7":7, "19":"6D"}


Так что же значит хранить эти десятичные целые числа в виде шестнадцатеричных целых.

  1. Сохранить место для хранения?(потому что визуально он выглядит сжатым)
  2. Сохраняет ли JSON тип данных ключ-значение или они хранятся в виде строк?
  3. Сжатие данных?
  4. Улучшениепроизводительность чтения?
  5. или в любом случае ее можно улучшить?(Индексирование или любое другое?)

В обычном приложении psql выполнение запросов занимает несколько минут.Поскольку он соответствует данным временного ряда, мы используем расширение TimescaleDB, а его механизм шардинга ускоряет выполнение запроса, но нам нужны результаты в секундах.

Примеры запросов : чтобы проверить, сколькораз цена была изменена на 500, для всех продуктов в данной категории, в группе месяцев по дням.

select count(*), to_char(date_trunc('day', to_timestamp(time/1000) at time zone 'Asia/Kolkata'), 'YYYY/MM/DD') as unit, price 
from price_change 
where category_id = 1000000010 and time between 1514745000000 and 1517423400000 
  and price = 500 
group by price, unit;

Чтобы проверить, сколько раз цена была изменена на любой из (100 200 300 400 800 500 600 700 800 800 900 000),для всех продуктов в данной категории в группе за последние 10 месяцев по каждому месяцу.

select count(*), to_char(date_trunc('month', to_timestamp(time/1000) at time zone 'Asia/Kolkata'), 'YYYY/MM/DD') as unit, price 
from price_change 
where category_id = 1000000010 and time between  1514745000000 and 1517423400000  
   and price in (100,200,300,400,500,600,700,800,900,1000) group by price, unit;

Чтобы выбрать сведения о продукте, цена которого была изменена в данном временном диапазоне, в данной категории

select product_id, product_name, price, to_char(date_trunc('day', to_timestamp(time/1000) at time zone 'Asia/Kolkata'), 'YYYY/MM/DD') as timestamp 
from price_change 
  join products using product_id 
where price_change.category_id = 1000000010 
  and price_change.time between 1514745000000 and 1517423400000;

Чтобы выбрать данные об отрасли и идентификаторе продукта, цена которого была изменена в заданном временном диапазоне, в данной категории

select industry_id, product_id, price 
from price_change 
  join industries using industry_id 
where price_change.category_id = 1000000010 
  and price_change.time between 1514745000000 and 1517423400000;

Чтобы выбрать подробности об изменении цены продукта, во временном диапазонесо скидкой 10%, в определенной категории

select product_id, product_name, price, to_char(date_trunc('day', to_timestamp(time/1000) at time zone 'Asia/Kolkata'), 'YYYY/MM/DD') as timestamp 
from price_change 
  join products using product_id 
where price_change.category_id = 1000000010 
  and price_change.time between 1514745000000 and 1517423400000
  and product_info->>'discount'=10;

Чтобы выбрать подробности изменения цены товара, во временном диапазоне, проданном конкретным продавцомв определенной категории

select product_id, product_name, price, to_char(date_trunc('day', to_timestamp(time/1000) at time zone 'Asia/Kolkata'), 'YYYY/MM/DD') as timestamp 
from price_change 
  join products using product_id 
where price_change.category_id = 1000000010 
  and price_change.time between 1514745000000 and 1517423400000
  and product_info->>'seller'='ABC Assured';

В большинстве случаев запрос не будет содержать category_id в столбцах выбора.

1 Ответ

0 голосов
/ 25 октября 2018

Будет полезно, если вы также предоставите несколько примеров того, к чему вы обычно обращаетесь.Существуют разные способы оптимизации индексов / как данные записываются на диск, которые очень сильно зависят от того, какой тип запроса вы выполняете (более конкретно, что содержится в предложении where)?Если вы используете предложения where, которые рассматривают JSON, вам следует рассмотреть возможность разбивки их на столбцы или построения индексов на самом JSON.

Похоже, одной из ваших проблем является хранение.Поскольку TimescaleDB и PostgreSQL являются реляционными, они занимают больше памяти, чем, возможно, столбчатое хранилище, которое может иметь лучшие характеристики сжатия.Вы также можете использовать что-то вроде ZFS для сжатия файлов.

...