Представление разреженных данных в PostgreSQL - PullRequest
17 голосов
/ 07 апреля 2010

Каков наилучший способ представления разреженной матрицы данных в PostgreSQL? Я вижу два очевидных метода:

  1. Храните данные в одной таблице с отдельным столбцом для каждой мыслимой функции (потенциально миллионы), но со значением по умолчанию NULL для неиспользуемых функций. Это концептуально очень просто, но я знаю, что в большинстве реализаций RDMS это обычно очень неэффективно, поскольку значения NULL обычно занимают некоторое пространство. Однако я прочитал статью (к сожалению, не могу найти ее ссылку), в которой утверждается, что PG не принимает данные для значений NULL, что делает его более подходящим для хранения разреженных данных.

  2. Создайте отдельные таблицы «строки» и «столбцы», а также промежуточную таблицу, чтобы связать их и сохранить значение для столбца в этой строке. Я считаю, что это более традиционное решение RDMS, но с ним связано больше сложности и накладных расходов.

Я также нашел PostgreDynamic , который утверждает, что лучше поддерживает разреженные данные, но я не хочу переключать весь свой сервер базы данных на вилку PG только для этой функции.

Есть ли другие решения? Какой из них мне следует использовать?

Ответы [ 4 ]

13 голосов
/ 28 апреля 2011

Я предполагаю, что вы думаете о разреженных матрицах из математического контекста: http://en.wikipedia.org/wiki/Sparse_matrix (Описанные здесь методы хранения предназначены для хранения в памяти (быстрая арифметическая операция), а не для постоянного хранения (низкое использование диска).)

Так как обычно все работают с этими матрицами на стороне клиента, а не на стороне сервера, SQL-ARRAY [] - лучший выбор!

Вопрос в том, как воспользоваться разреженностью матрицы? Вот результаты некоторых исследований.

Установка:

  • Postgres 8,4
  • Матрицы с 400 * 400 элементами с двойной точностью (8 байт) -> 1,28 МБ необработанного размера на матрицу
  • 33% ненулевых элементов -> эффективный размер 427киБ на матрицу
  • усреднено с использованием ~ 1000 различных случайных заполненных матриц

Методы конкуренции:

  • Положитесь на автоматическую сторону сервера сжатие столбцов с помощью SET STORAGE MAIN или EXTENDED.
  • Храните только ненулевые элементы плюс растровое изображение (bit varying(xx)), описывающее, где найти ненулевые элементы в матрице. (Одна двойная точность в 64 раза больше, чем один бит. Теоретически (игнорируя издержки) этот метод должен быть улучшен, если <= 98% отличны от нуля ;-).) Включено сжатие на стороне сервера. </li>
  • Заменить нулей в матрице на NULL . (СУБД очень эффективны для хранения значений NULL.) Сжатие на стороне сервера активировано.

(Индексирование ненулевых элементов с использованием 2nd index-ARRAY [] не очень многообещающе и поэтому не проверено.)

Результаты:

  • Автоматическое сжатие
    • никаких дополнительных усилий по реализации
    • нет снижения сетевого трафика
    • минимальные накладные расходы на сжатие
    • постоянное хранение = 39% от необработанного размера
  • Bitmap
    • приемлемые усилия по реализации
    • сетевой трафик немного уменьшился; зависит от редкости
    • постоянное хранение = 33,9% от необработанного размера
  • Заменить нули на NULL
    • некоторые усилия по реализации (API должен знать, где и как устанавливать значения NULL в ARRAY [] при создании запроса INSERT)
    • без изменений в сетевом трафике
    • постоянное хранение = 35% от необработанного размера

Вывод: Начните с параметра EXTENDED / MAIN. Если у вас есть свободное время, изучите ваши данные и используйте мою настройку теста с вашим уровнем разреженности. Но эффект может быть ниже, чем вы ожидаете.

Я предлагаю всегда использовать сериализацию матрицы (например, порядок старших строк) плюс два целочисленных столбца для размеров матрицы NxM. Поскольку большинство API используют текстовый SQL, вы экономите много сетевого трафика и клиентской памяти для вложенных «ARRAY [ARRAY [..], ARRAY [..], ARRAY [..], ARRAY [..], ..]» !!!

Tebas


CREATE TABLE _testschema.matrix_dense
(
  matdata double precision[]
);
ALTER TABLE _testschema.matrix_dense ALTER COLUMN matdata SET STORAGE EXTERN;


CREATE TABLE _testschema.matrix_sparse_autocompressed
(
  matdata double precision[]
);

CREATE TABLE _testschema.matrix_sparse_bitmap
(
  matdata double precision[]
  bitmap bit varying(8000000)
);

Вставить одинаковые матрицы во все таблицы. Конкретные данные зависят от определенной таблицы. Не изменяйте данные на стороне сервера из-за неиспользуемых, но выделенных страниц. Или сделайте ВАКУУМ.

SELECT 
pg_total_relation_size('_testschema.matrix_dense') AS dense, 
pg_total_relation_size('_testschema.matrix_sparse_autocompressed') AS autocompressed, 
pg_total_relation_size('_testschema.matrix_sparse_bitmap') AS bitmap;
7 голосов
/ 08 апреля 2010

На ум приходит несколько решений,

1) Разделите ваши функции на группы, которые обычно устанавливаются вместе, создайте таблицу для каждой группы с отношением внешнего ключа один к одному с основными данными, объединяйте только те таблицы, которые вам нужны при запросе

2) Используйте анти-шаблон EAV, создайте таблицу «Feature» с полем внешнего ключа из вашей первичной таблицы, а также с именем поля и столбцом значения и сохраните объекты в виде строк в этой таблице, а не в качестве атрибутов. в вашей основной таблице

3) Аналогично тому, как это делает PostgreDynamic, создайте таблицу для каждого «столбца» в вашей первичной таблице (они используют отдельное пространство имен для этих таблиц) и создайте функции, чтобы упростить (а также эффективно индексировать) доступ и обновление данные в этих таблицах

4) создать столбец в ваших первичных данных, используя XML или VARCHAR, и сохранить в нем некоторый структурированный текстовый формат, представляющий ваши данные, создать индексы для данных с функциональными индексами, написать функции для обновления данных (или использовать XML функции, если вы используете этот формат)

5) использовать модуль contrib / hstore для создания столбца типа hstore, который может содержать пары ключ-значение и может быть проиндексирован и обновлен

6) жить с множеством пустых полей

2 голосов
/ 21 января 2015

Я знаю, что это старая ветка, но MadLib предоставляет разреженный векторный тип для Postgres, а также несколько методов машинного обучения и статистики.

2 голосов
/ 08 апреля 2010

Значение NULL не будет занимать пробел, когда оно равно NULL. Это займет один бит в битовой карте в заголовке кортежа, но это будет там, независимо от того.

Однако система не может иметь дело с миллионами столбцов, точка. Есть теоретический максимум чуть более тысячи, IIRC, но вы действительно не хотите заходить так далеко.

Если вам действительно нужно такое количество в одной таблице, вам нужно использовать метод EAV, который, по сути, является тем, что вы говорите в (2).

Если каждая запись имеет только относительно небольшое количество ключей, я предлагаю вам взглянуть на модули "hstore" contrib, которые позволяют очень эффективно хранить этот тип данных, как третий вариант. В следующей версии 9.0 он был усовершенствован, поэтому, если вы немного отстали от производственного развертывания, вы можете взглянуть непосредственно на него. Тем не менее, в 8.4 оно того стоит. И он поддерживает некоторые довольно эффективные поиски на основе индекса. Определенно стоит заглянуть.

...