PostgreSQL: эффективная загрузка данных в схему Star - PullRequest
7 голосов
/ 03 января 2011

Представьте себе таблицу со следующей структурой в PostgreSQL 9.0:

create table raw_fact_table (text varchar(1000));

Для упрощения я упомяну только один текстовый столбец, на самом деле он имеет дюжину. В этой таблице 10 миллиардов строк, и в каждом столбце много дубликатов. Таблица создается из плоского файла (CSV) с помощью COPY FROM.

Чтобы повысить производительность, я хочу преобразовать в следующую структуру звезды:

create table dimension_table (id int, text varchar(1000));

Таблица фактов затем будет заменена таблицей фактов, как показано ниже:

create table fact_table (dimension_table_id int);

Мой текущий метод заключается в том, чтобы по существу выполнить следующий запрос для создания таблицы измерений:

Create table dimension_table (id int, text varchar(1000), primary key(id));

Затем для создания заполните таблицу измерений, которую я использую:

insert into dimension_table (select null, text from raw_fact_table group by text);

После этого мне нужно выполнить следующий запрос:

select id into fact_table from dimension inner join raw_fact_table on (dimension.text = raw_fact_table.text);

Только представьте, какую ужасную производительность я получаю, сравнивая все строки со всеми другими строками несколько раз.

На MySQL я мог запустить хранимую процедуру во время COPY FROM. Это может создать хеш строки, и все последующие сравнения строк будут выполняться для хеша вместо длинной необработанной строки. Это кажется невозможным на PostgreSQL, что мне тогда делать?

Примером данных будет CSV-файл, содержащий что-то вроде этого (я использую кавычки также вокруг целых и двойных чисел):

"lots and lots of text";"3";"1";"2.4";"lots of text";"blabla"
"sometext";"30";"10";"1.0";"lots of text";"blabla"
"somemoretext";"30";"10";"1.0";"lots of text";"fooooooo"

Ответы [ 5 ]

6 голосов
/ 23 января 2011

Только представьте, какую ужасную производительность я получаю, сравнивая все строки со всеми другими строками несколько раз.

Когда вы делаете это некоторое время, вы перестаете воображать производительность, и выначать измерять это.«Преждевременная оптимизация - корень всего зла».

Что значит для вас "миллиард"?Для меня в США это означает 1 000 000 000 (или 1e9).Если это также верно для вас, вы, вероятно, просматриваете от 1 до 7 терабайт данных.

Мой текущий метод заключается в том, чтобы по существу выполнить следующий запрос для создания таблицы измерений:

Create table dimension_table (id int, text varchar(1000), primary key(id));

Как вы поместите 10 миллиардов строк в таблицу, которая используетцелое число для первичного ключа?Давайте даже скажем, что половина строк является дубликатами.Как работает эта арифметика, когда вы это делаете?

Не представляйте.Читай сначала.Затем проверьте.

Чтение Хранилище данных с PostgreSQL .Я подозреваю, что эти слайды презентации дадут вам некоторые идеи.

Также прочитайте Заполнение базы данных и подумайте, какие предложения следует реализовать.

Тест с миллионами (1e6) строк, следуя процессу «разделяй и властвуй».То есть не пытайтесь загрузить миллион за раз;написать процедуру, которая разбивает его на более мелкие куски.Выполните

EXPLAIN <sql statement>

Вы сказали, что оцениваете как минимум 99% повторяющихся строк.Вообще говоря, есть два способа избавиться от дубликатов

  1. Внутри базы данных, не обязательно той же платформы, которую вы используете для производства.
  2. За пределами базы данных, в файловой системе,не обязательно та же файловая система, которую вы используете для производства.

Если у вас все еще есть загруженные текстовые файлы, я бы сначала попробовал за пределами базы данных.Этот awk one-liner будет выводить уникальные строки из каждого файла.Это относительно экономично, так как он делает только один проход по данным.

awk '!arr[$0]++' file_with_dupes > file_without_dupes

Если у вас действительно есть 99% дупликов, к концу этого процесса вы должны были сократить свои от 1 до 7 терабайт до примерно50 концертов.И, сделав это, вы также можете нумеровать каждую уникальную строку и создавать файл с разделителями табуляции, прежде чем копировать его в хранилище данных.Это еще одна строка:

awk '{printf("%d\t%s\n", NR, $0);}' file_without_dupes > tab_delimited_file

Если вам нужно сделать это под Windows, я бы использовал Cygwin .

Если вы должны сделать это вбазы данных, я бы постарался избежать использования вашей производственной базы данных или вашего производственного сервераНо, может быть, я слишком остороженПеремещение нескольких терабайт вокруг - дорогая вещь.

Но я бы протестировал

SELECT DISTINCT ...

перед использованием GROUP BY.Я мог бы провести некоторые тесты для большого набора данных для вас, но, вероятно, не на этой неделе.(Я обычно не работаю с файлами размером в терабайты. Это довольно интересно. Если вы можете подождать.)

2 голосов
/ 25 января 2011

Просто на вопросы: - необходимо конвертировать ваши данные за 1 или 2 шага?- Можем ли мы изменить таблицу при конвертации?

Выполнение более простых запросов может повысить вашу производительность (и нагрузку на сервер при этом)

Один из подходов будет:

  1. генерировать размерную_таблицу (если я правильно понимаю, с этим у вас нет проблем с производительностью) (возможно, с дополнительным временным логическим полем ...)
  2. повтор: выберите одну ранее не выбранную запись из размерной_таблицы,выберите все строки из raw_fact_table, содержащие его, и вставьте их в fact_table.Пометить размерную запись как выполненную, а затем ... Вы можете написать это как хранимую процедуру, и она может преобразовывать ваши данные в фоновом режиме, потребляя минимальные ресурсы ...

Или другой (возможно, лучше):

  1. создать факт_таблицу как КАЖДУЮ запись из raw_fact_table И одного измерения_ид.(включая строки измерения_текста и измерения_идентификатора)
  2. создать размерную_таблицу
  3. создать триггер после вставки для факт_таблицы, который:
    • ищет размерный_текст в таблице фактов
    • если нетнайдено, создает новую запись в измерении_таблица
    • обновляет измерение_идентификатора до этого идентификатора
  4. в цикле симуляции, вставляя каждую запись из raw_fact_table в fact_table
2 голосов
/ 25 января 2011
-- add unique index
CREATE UNIQUE INDEX uidx ON dimension_table USING hash(text);
-- for non case-sensitive hash(upper(text))

попробуйте хеш (текст); и btree (текст), чтобы увидеть, какой из них быстрее

2 голосов
/ 03 января 2011

Вы опускаете некоторые детали там в конце, но я не вижу, что обязательно есть проблема.Это не свидетельствует о том, что все строки фактически сравниваются со всеми другими строками.Если вы выполняете объединение, PostgreSQL вполне может выбрать более разумный алгоритм объединения, такой как объединение хешей, которое может дать вам то же хеширование, которое вы реализуете сами в своем решении MySQL.(Опять же, ваши данные неясны.)

1 голос
/ 24 января 2011

Я вижу несколько способов решения вашей проблемы. В PostgreSql есть функция md5 md5 (строка). Вычисляет хеш строки MD5, возвращая результат в шестнадцатеричном виде

вставка в размерную таблицу (выберите ноль, md5 (текст).), текст из группы raw_fact_table по тексту)

добавить поле md5 в raw_fact_table, а также выбрать идентификатор в fact_table из внутреннего соединения измерения raw_fact_table on (dimension.md5 = raw_fact_table.md5);

Индексы вТакже может помочь поле MD5

Или вы можете вычислить MD5 на лету при загрузке данных.Например, наш ETL инструмент Расширенный процессор ETL может сделать это за вас.Кроме того, он может одновременно загружать данные в несколько таблиц.

На нашем веб-сайте доступно несколько интерактивных учебных пособий. Например, эта демонстрирует загрузку медленно меняющегося измерения

http://www.dbsoftlab.com/online-tutorials/advanced-etl-processor/advanced-etl-processor-working-with-slow-changing-dimension-part-2.html

...