Размер столбца PostgreSQL JSONB увеличился после оптимизации

#postgresql #jsonb

#postgresql #jsonb

Вопрос:

Изначально мой столбец jsonb выглядит так

 {
   addio: { percent : "10.0000"},
   inputId: 'c2a06eb0-ccf6-4845-bf33-a930f3790f37',
   discount: { percent : "10.0000"},
   some: { percent : "10.0000"},
   details: [
       { 
         amount: "10.0000",
         systemItemPriceCode: 'A',
         systemItemCategoryCode: 'B',
         systemItemPriceNumberOfShelfs: 4
       },
       { 
         amount: "5.0000",
         systemItemPriceCode: 'B',
         systemItemCategoryCode: 'C',
         systemItemPriceNumberOfShelfs: 4
       },
    ],
   field: { percent : "10.0000"},
   retention: { percent : "10.0000"},
   customerTesterPropertyItemMode: { percent : "10.0000"}
}
  

Размер моей таблицы с 18594 строками составляет 8472 КБ;

Я решил оптимизировать его дальше, сделав ключ на одну букву короче и полностью удалив некоторые ключи, такие как systemItemCategoryCode и systemItemPriceNumberOfShelfs .

 {
   a: { p : "10.0000"},
   i: 'c2a06eb0-ccf6-4845-bf33-a930f3790f37',
   d: { p : "10.0000"},
   s: { p : "10.0000"},
   l: [
       { 
         a: "10.0000",
         c: 'A'
       },
       { 
         a: "5.0000",
         c: 'B'
       },
    ],
   f: { p : "10.0000"},
   r: { p : "10.0000"},
   m: { p : "10.0000"}
}
  

Однако размер таблицы с 18594 строками (все остальное то же самое, изменен только формат JSONB) составляет 10088 КБ!!

  1. Что произошло, почему размер увеличился, я чувствовал, что удалил много материала.
  2. Если это касается Toast, как я могу получить точный размер, чтобы знать, что я действительно экономлю место?

Для вычисления размера используется следующий код https://wiki.postgresql.org/wiki/Disk_Usage в разделе Общие сведения о размере таблицы, сгруппированные для секционированных таблиц

Минимальный код для воспроизведения поведения

 -- TABLE CREATION
CREATE TABLE jsonb_big (
    jsonb_col JSONB NOT NULL
);

CREATE TABLE jsonb_optimized (
    jsonb_col JSONB NOT NULL
);

-- CLEAN TABLE
DELETE  FROM jsonb_big WHERE TRUE;
DELETE  FROM jsonb_optimized WHERE TRUE;

-- POPULATE TABLE
INSERT INTO jsonb_big (jsonb_col) SELECT '{"hxxx": {"pxxxxxx": "0.00000"}, "lxxxxxx": [{"axxxxx": "2000.00000", "cxxxxxxxxxxxxxxxxxx": "A", "yxxxxxxxxxxxxxxxxxxxxx": "TES", "zxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx": 4}, {"axxxxx": "1000.00000", "cxxxxxxxxxxxxxxxxxx": "B", "yxxxxxxxxxxxxxxxxxxxxx": "TES", "zxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx": 4}, {"axxxxx": "500.00000", "cxxxxxxxxxxxxxxxxxx": "C", "yxxxxxxxxxxxxxxxxxxxxx": "TES", "zxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx": 4}, {"axxxxx": "250.00000", "cxxxxxxxxxxxxxxxxxx": "D01", "yxxxxxxxxxxxxxxxxxxxxx": "TES", "zxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx": 4}, {"axxxxx": "250.00000", "cxxxxxxxxxxxxxxxxxx": "D02", "yxxxxxxxxxxxxxxxxxxxxx": "TES", "zxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx": 4}, {"axxxxx": "250.00000", "cxxxxxxxxxxxxxxxxxx": "D03", "yxxxxxxxxxxxxxxxxxxxxx": "TES", "zxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx": 4}, {"axxxxx": "250.00000", "cxxxxxxxxxxxxxxxxxx": "D04", "yxxxxxxxxxxxxxxxxxxxxx": "TES", "zxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx": 4}, {"axxxxx": "250.00000", "cxxxxxxxxxxxxxxxxxx": "D05", "yxxxxxxxxxxxxxxxxxxxxx": "TES", "zxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx": 4}, {"axxxxx": "250.00000", "cxxxxxxxxxxxxxxxxxx": "D06", "yxxxxxxxxxxxxxxxxxxxxx": "TES", "zxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx": 4}, {"axxxxx": "250.00000", "cxxxxxxxxxxxxxxxxxx": "D07", "yxxxxxxxxxxxxxxxxxxxxx": "TES", "zxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx": 4}, {"axxxxx": "250.00000", "cxxxxxxxxxxxxxxxxxx": "D08", "yxxxxxxxxxxxxxxxxxxxxx": "TES", "zxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx": 4}, {"axxxxx": "250.00000", "cxxxxxxxxxxxxxxxxxx": "D09", "yxxxxxxxxxxxxxxxxxxxxx": "TES", "zxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx": 4}, {"axxxxx": "250.00000", "cxxxxxxxxxxxxxxxxxx": "D10", "yxxxxxxxxxxxxxxxxxxxxx": "TES", "zxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx": 4}, {"axxxxx": "75.00000", "cxxxxxxxxxxxxxxxxxx": "E01", "yxxxxxxxxxxxxxxxxxxxxx": "TES", "zxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx": 4}, {"axxxxx": "75.00000", "cxxxxxxxxxxxxxxxxxx": "E02", "yxxxxxxxxxxxxxxxxxxxxx": "TES", "zxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx": 4}, {"axxxxx": "75.00000", "cxxxxxxxxxxxxxxxxxx": "E03", "yxxxxxxxxxxxxxxxxxxxxx": "TES", "zxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx": 4}, {"axxxxx": "75.00000", "cxxxxxxxxxxxxxxxxxx": "E04", "yxxxxxxxxxxxxxxxxxxxxx": "TES", "zxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx": 4}, {"axxxxx": "75.00000", "cxxxxxxxxxxxxxxxxxx": "E05", "yxxxxxxxxxxxxxxxxxxxxx": "TES", "zxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx": 4}, {"axxxxx": "75.00000", "cxxxxxxxxxxxxxxxxxx": "E06", "yxxxxxxxxxxxxxxxxxxxxx": "TES", "zxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx": 4}, {"axxxxx": "75.00000", "cxxxxxxxxxxxxxxxxxx": "E07", "yxxxxxxxxxxxxxxxxxxxxx": "TES", "zxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx": 4}, {"axxxxx": "75.00000", "cxxxxxxxxxxxxxxxxxx": "E08", "yxxxxxxxxxxxxxxxxxxxxx": "TES", "zxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx": 4}, {"axxxxx": "75.00000", "cxxxxxxxxxxxxxxxxxx": "E09", "yxxxxxxxxxxxxxxxxxxxxx": "TES", "zxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx": 4}, {"axxxxx": "75.00000", "cxxxxxxxxxxxxxxxxxx": "E10", "yxxxxxxxxxxxxxxxxxxxxx": "TES", "zxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx": 4}], "dxxxxxxxxx": {"pxxxxxx": "31.00000"}}'
FROM generate_series(1, 20000);
INSERT INTO jsonb_optimized (jsonb_col) SELECT '{"d": {"p": "31.00000"}, "h": {"p": "0.00000"}, "l": [{"a": "2000.00000", "c": "A"}, {"a": "1000.00000", "c": "B"}, {"a": "500.00000", "c": "C"}, {"a": "250.00000", "c": "D01"}, {"a": "250.00000", "c": "D02"}, {"a": "250.00000", "c": "D03"}, {"a": "250.00000", "c": "D04"}, {"a": "250.00000", "c": "D05"}, {"a": "250.00000", "c": "D06"}, {"a": "250.00000", "c": "D07"}, {"a": "250.00000", "c": "D08"}, {"a": "250.00000", "c": "D09"}, {"a": "250.00000", "c": "D10"}, {"a": "75.00000", "c": "E01"}, {"a": "75.00000", "c": "E02"}, {"a": "75.00000", "c": "E03"}, {"a": "75.00000", "c": "E04"}, {"a": "75.00000", "c": "E05"}, {"a": "75.00000", "c": "E06"}, {"a": "75.00000", "c": "E07"}, {"a": "75.00000", "c": "E08"}, {"a": "75.00000", "c": "E09"}, {"a": "75.00000", "c": "E10"}]}'
FROM generate_series(1, 20000);

-- VACUUM TABLE
VACUUM FULL;

-- CALCULATE SIZE
WITH RECURSIVE pg_inherit(inhrelid, inhparent) AS
    (select inhrelid, inhparent
    FROM pg_inherits
    UNION
    SELECT child.inhrelid, parent.inhparent
    FROM pg_inherit child, pg_inherits parent
    WHERE child.inhparent = parent.inhrelid),
pg_inherit_short AS (SELECT * FROM pg_inherit WHERE inhparent NOT IN (SELECT inhrelid FROM pg_inherit))
SELECT table_schema
    , TABLE_NAME
    , row_estimate,
       total_bytes as total_bytes
    , pg_size_pretty(total_bytes) AS total
    , pg_size_pretty(index_bytes) AS INDEX
    , pg_size_pretty(toast_bytes) AS toast
    , pg_size_pretty(table_bytes) AS TABLE
  FROM (
    SELECT *, total_bytes-index_bytes-COALESCE(toast_bytes,0) AS table_bytes
    FROM (
         SELECT c.oid
              , nspname AS table_schema
              , relname AS TABLE_NAME
              , SUM(c.reltuples) OVER (partition BY parent) AS row_estimate
              , SUM(pg_total_relation_size(c.oid)) OVER (partition BY parent) AS total_bytes
              , SUM(pg_indexes_size(c.oid)) OVER (partition BY parent) AS index_bytes
              , SUM(pg_total_relation_size(reltoastrelid)) OVER (partition BY parent) AS toast_bytes
              , parent
          FROM (
                SELECT pg_class.oid
                    , reltuples
                    , relname
                    , relnamespace
                    , pg_class.reltoastrelid
                    , COALESCE(inhparent, pg_class.oid) parent
                FROM pg_class
                    LEFT JOIN pg_inherit_short ON inhrelid = oid
                WHERE relkind IN ('r', 'p')
             ) c
             LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
  ) a
  WHERE oid = parent
) a WHERE table_schema ='public'
ORDER BY total_bytes DESC ;
  

Комментарии:

1. @Zanko Когда вы update в Postgres записываете новые данные и помечаете старые строки для удаления. Вы очистили таблицу ?

2. откуда вы знаете вычисленный размер? Очищена ли таблица?

3. @Schwern Я включил код для воспроизведения поведения. Неэффективный JSON занимал 8 МБ, в то время как оптимизированная версия занимала 22 МБ!! Вы можете запустить и убедиться сами!

4. Вероятно, это больше связано с алгоритмом jsonb хранения. Исходная версия была ограничителем показа для выпуска Postgres 9.4, см. Этот [поток] ( postgresql.org/message-id/27839.1407467863@sss.pgh.pa.us ), так как это раздуло хранилище. Я предполагаю, что вы попали в угловой случай, который не был исправлен последующим исправлением.

5. Использование ALTER TABLE jsonb_big ALTER COLUMN jsonb_col SET STORAGE PLAIN , похоже, показывает, что проблема может быть связана с порогом ТОСТА.