LINUX.ORG.RU

Посоветуйте БД технологии для оптимизации поиска по большому объёму данных

 , , , ,


1

1

В БД имеется таблица products содержащая более полутора миллионов записей. Данная таблица заполняется/обновляется через крон скрипты ежедневно из разных источников.

Посредством web UI администратор системы имеет возможность добавлять/удалять fields - колонки этой таблицы и конфигурировать какая колонка из CSV фида (источника данных) соответствует какой колонке в этой таблице. В настоящее время имеется 37 колонок таблицы данных таких как например Vendor Name, VPN, SKU, EANUPC, Dealer Price, Stock, Stock Backlog Quantity, Stock Backlog ETA, Warehouse, Description, Category, OEM Part Number, etc.

Таким образом администратор системы может для CSV-фида из источника http://sourceX/feed.csv установить через конфигуратор:
колонка CSV #1 -> VPN
колонка CSV #2 -> Dealer Price
колонка CSV #12 -> Stock
колонка CSV #13 -> EANUPC
колонка CSV #14 -> Stock Backlog Quantity
...........etc.............

Требуется организовать поиск по таблице products в котором администратор может искать по любой комбинации колонок, например:
- найти все продукты где $5.55 <= Dealer Price <= $7.50 и Stock > 10
- найти все продукты где Description содержит «keyboard» и EANUPC начинается с XXXXXXX
- найти все продукты в которых VPN содержит XXX или SKU содержит XXX
...................etc...............

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

В настоящее время после запроса можно сходить покурить пока появятся результаты. Используется mongodb, индексы есть но не помогают либо расставлены неправильно. Количество записей ~1.6M.

Прошу посоветовать возможно ли как то оптимизировать скорость работы поиска (с добавлением записей проблем нет) возможно засчёт замедления крон-скриптов импортирующих/добавляющих записи.

Машина довольно мощная:
- восьмиядерный AMD Opteron(TM) Processor 6220
- 32 гигабайта памяти
- диск вроде SSD 120 гигабайт

Прежде всего хотелось бы спросить совета относительно оптимальной БД для таких нужд. Оптимально ли подходит mongodb или стоит копать в другую сторону? Слышал что то про Elastic Search. Время для изучения новых технологий есть. Однако хотелось бы быть уверенным что копаю в правильном направлении.

Также буду благодарен если посоветуете какую то литературу по теме.


Подойдет любая мейнстримная реляционная СУБД с индексами, постгрес например

annulen ★★★★★
()

ElasticSearch - обертка над Lucene Apache, довольно мощной либой для fulltext-search. У ES не так все гладко при индексации новых данных в плане производительности, а также переиндексации (который по сути нет, вы просто пересоздаете индексы), а вы пишите что структура таблиц очень часто будет меняться, как и сами данные.
Если процентное соотношение (alter/update/insert) к (select) будет в пределах 5-10%/90-95% - тогда да, можете рискнуть. Сам использую ES и очень счастлив :)

znenyegvkby
()
Ответ на: комментарий от znenyegvkby

Насколько проблемно update/insert? Обновление записей происходит через скрипты которые запускаются раз в день. В принципе ничего страшного если это будет занимать 20 часов но не больше суток хотя бы.

mongo
() автор топика
Ответ на: комментарий от annulen

Подойдет любая мейнстримная реляционная СУБД с индексами, постгрес например

Тогда как организовать данные? Делать одну таблицу со всеми колонками или в отдельной таблице хранить [product_id, field_id, field_value]? Как ставить индексы? Для всех комбинаций поиска - ведь я правильно понимаю что для поиска по Field1, Field2, Field3 нужен один индекс а для поиска по Field1, Field3 уже другой? Или есть какие то решения в PostgreSQL?

mongo
() автор топика
Ответ на: комментарий от mongo

Я бы тоже посоветовал PostgreSQL.

Только для него нужно зафиксировать число полей. Таблица на 40 столбцов — это хорошо, но если будет надо удалить/добавить столбец, то это надолго; может быть даже проще пересоздать базу. Если таблица часто меняется, то NoSQL подойдет гораздо лучше, другое дело, что это порочная практика. Но если число полей не меняется, то все OK.

Поиск и добавление должны быть не хуже, чем в монге, но это зависит от индексов. Простые индексы для чисел («цена больше 15.99 и меньше 20.45») работают почти мгновенно; полнотекстовый поиск («в названии есть сочетание абырвалг123») — уже хуже, надо смотреть конкретные запросы. Позиции лучше не удалять, а завести отдельный флаг «удалено», и во всех запросах проверять, что он=false.

anonymous
()
Ответ на: комментарий от mongo

Вот прочтите, там пару параграфов.
ES реально крут, но сама по себе индексация и в частности переиндексация - не его основная фишка. Он базируется на простом правиле - вы проиндексировали документ - и вы хотите искать по нему. Искать гибко, быстро и точно. Вы не хотите каждые пять минут изменять что-то в индексе, вы просто хотите качественный поиск. И это ES вам даст без сомнения. Я последний раз вчера индексировал небольшую таблицу, и ниже дам вам примерные данные, чтобы вы для себя смогли немного посчитать что к чему

  • 50к документов (все на одном шарде делал), каждый в среднем всего по 2,5-3Кб. Шесть различных анализаторов, в каждом из крупных фильтров ru/en морфология и n-gramm с диапазоном 2-25 + по 3-5 мелких, типа всяких стопов, lowercase и т.д.
  • Машина 4-intel по 1.6, 2-е рамы по 2Gb и h-sata2 250Gb

В итоге по логам 2567s. Ну вот считайте, ~40 минут за ~120 метров практически чистых индексированных данных. Не так уж плохо, но если сравнивать с тем же Sphinx, это конечно небо и земля. Но ES умеет качественно RT-индексы, поэтому я все равно в любой ситуации предпочту его (у него еще много фич, которые мало где есть).
Так что считайте для себя. Переиндексация в будущем не будет для вас проблемой. Изначальной проблемой будет полный индекс по уже имеющейся у вас куче :)

znenyegvkby
()
Ответ на: комментарий от anonymous

Если таблица часто меняется, то NoSQL подойдет гораздо лучше, другое дело, что это порочная практика.

Для дополнительных полей можно завести поле типа hstore или jsonb, но эффективность поиска по таким полям будет ниже.

annulen ★★★★★
()
Ответ на: комментарий от anonymous

В общем-то, взять и погонять совсем несложно.

Таблица имя-описание-цена.

Создаем, добавляем индексы, заполняем данными, делаем запросы.

Данных можно нагенирировать те самые 1.5М, но мне это как-то лениво.

drop table parts;

create table parts (
    part_id BIGINT,
    name    VARCHAR(40),
    descr VARCHAR(80),
    price  NUMERIC(16,4),

    PRIMARY KEY(part_id)
);

-- Indices

CREATE INDEX ON parts (name);
CREATE INDEX ON parts (descr);
CREATE INDEX ON parts (price);

-- Autoincrement
--DROP SEQUENCE part_id_seq;
CREATE SEQUENCE part_id_seq;
ALTER TABLE parts ALTER part_id SET DEFAULT NEXTVAL('part_id_seq');

-- Data
INSERT INTO parts(name, descr, price) VALUES('hdd01', '1TB drive', 100.00);
INSERT INTO parts(name, descr, price) VALUES('cpu01', 'Intel Core something', 150.00);
INSERT INTO parts(name, descr, price) VALUES('cpu01', 'Intel Core something2', 250.00);
INSERT INTO parts(name, descr, price) VALUES('memory01', '8GB', 50.00);

-- Queries

select * from parts where price > 60;

select * from parts where price > 60 and price < 120;

select * from parts where descr like 'Intel%' and price < 160;

Для строк '%' заменяет регулярку-звездочку. Единственное, индексы не будут работать, если проценты идут с двух сторон (like '%Core%'), и поиск будет очень медленным. Но, если можно точно задать начало строки, как в примере, то все будет отлично.

anonymous
()
Ответ на: комментарий от annulen

Да, про json постоянно забываю. Крутая штука. Но запутаться гораздо проще, когда нет маппинга «поле данных»-«поле БД».

anonymous
()
Ответ на: комментарий от anonymous

Единственное, индексы не будут работать, если проценты идут с двух сторон (like '%Core%'), и поиск будет очень медленным

А ведь есть ещё full text search:

http://www.postgresql.org/docs/9.5/static/textsearch.html

Её как то можно совместить? Объём диска в принципе можно увеличить если это потребует много места.

mongo
() автор топика
Ответ на: комментарий от anonymous

Таблица на 40 столбцов — это хорошо, но если будет надо удалить/добавить столбец, то это надолго

Думаю просто сделать например 50 столбцов с запасом часть из которых пусты - не используются. В случае потребности добавить - использовать один из них. В принципе директор давно уже ничего не менял, так что не так уж и часто хотя в ТЗ было такое требование изначально. Поэтому и использовалась mongodb.

mongo
() автор топика

про обезъяну с гранатой

Это - не большой объём данных. Это объём, который можно целиком в ОЗУ разместить. Для таких мелких задач монструозные решения просто неэффективны. Проведи простой эксперимент - экспортни свою базу в файл с разделителями и ищи в нём скриптом на AWK. Вряд ли ты успеешь покурить добежать.

anonymous
()

В БД имеется таблица products содержащая более полутора миллионов записей.

Это не большая база. у нас в среднем ~40 мил. записей за квартал было 10 лет назад. вот это большой объём над которым надо было немного подумать.

тут любая база, просто надо индексы положить.

vtVitus ★★★★★
()

man explain. Это не нормально что оно так тормозит. Должно давать ответ очень быстро.

redixin ★★★★
()

И да, прелесть монги во всяких распределенных штуках. В ней нет смысла если будет всего один сервер.

redixin ★★★★
()
Ответ на: комментарий от redixin

И да, прелесть монги во всяких распределенных штуках. В ней нет смысла если будет всего один сервер.

А как же отсутствие строго определённой схемы. Мне кажется в некоторых задачах это большой плюс.

mongo
() автор топика
Ответ на: комментарий от redixin

man explain. Это не нормально что оно так тормозит. Должно давать ответ очень быстро.

Там набыдлокодили очень много. Добавляли новые фичи и запихали в эту таблицу разные массивы на скорую руку. Сейчас пишу скрипт что бы преобразовать данные в новую базу данных в приемлемом формате (вынести часть данных в отдельные таблицы). Потом буду переписывать код работающий с этим и тестировать скорость поиска.

mongo
() автор топика
Ответ на: комментарий от mongo

а нужны? если постгря 9.5 отдирает монгу даже в задачах NoSql, а у вас в оп-посте хотелки для реляционной СУБД, то что еще говорить то?

SevikL ★★★★★
()
Ответ на: комментарий от mongo

Аргументы будут?

Ты уже убедился, что даже скрипт AWK отработает быстрее чем твоя монга? Какие ещё аргументы ты тут требуешь?

anonymous
()

О, бравые админы потерли разумного анонимуса. ТС, бери эластик и не мучай жопку.

A1
()
Ответ на: комментарий от mongo

Хотел бы я увидеть этот скрипт который осуществит поиск на 40 колонок.

Где тут у тебя «поиск на 40 колонок»?

- найти все продукты где $5.55 <= Dealer Price <= $7.50 и Stock > 10 - найти все продукты где Description содержит «keyboard» и EANUPC начинается с XXXXXXX - найти все продукты в которых VPN содержит XXX или SKU содержит XXX

anonymous
()
Ответ на: комментарий от A1

на те колонки по которым возможен поиск и индекс будет эффективен. если поиск возможен по всем колонкам то возможно имеет смысл посмотреть на индекс сервер типа solr, elasticsearch и т.п.

vtVitus ★★★★★
()

Попробуй Elasticsearch. Для ускорения построения индекса нужно поднять refresh_interval и использовать batch запросы.

maxcom ★★★★★
()
Ответ на: комментарий от vtVitus

Просто не надо использовать несчастный pg для всего.

A1
()
Ответ на: комментарий от SevikL

Это тролинг? Нет понятия «быстрая бд» и «медленная бд», а есть наличие и отсутсвие нужно индекса. Что в монге, что в постгре ты индексы на все случаи жизни не сделаешь. А вот elasticsearch индексит ВСЕ.

dizza ★★★★★
()
Ответ на: комментарий от dizza

Индексы делаются не для «всех случаев жизни», а для конкретных выборок. В бизнес-логике приложения выборки наверняка одни и те же, на них и надо делать индексы.

Wizard_ ★★★★★
()

В настоящее время после запроса можно сходить покурить пока появятся результаты. Используется mongodb, индексы есть но не помогают либо расставлены неправильно. Количество записей ~1.6M.
Прошу посоветовать ...

Посоветую :) Сишников уволить, нанять 1 специалиста работающего с Lazarus.

superuser ★★★★☆
()
Ответ на: комментарий от superuser

Lazarus с каких пор стал базой данных? Вот когда напишите на нём систему управления базой данных тогда и поговорим.

mongo
() автор топика
Ответ на: комментарий от superuser

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

mongo
() автор топика
Ответ на: комментарий от mongo

В Лазарусе можно написать и без всякой базы данных, будет шерстить файлик как тот же AWK. Глупо ведь использовать эту тормозную монгу только чтобы быть не таким как все. Производство не должно страдать от твоих придурей.

anonymous
()
Ответ на: комментарий от anonymous

Ждём с нетерпением демонстрации возможностей лазаруса и awk.

mongo
() автор топика
Ответ на: комментарий от anonymous

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

mongo
() автор топика
Ответ на: комментарий от anonymous

Ждём с нетерпением демонстрации возможностей лазаруса и awk.

Да не вопрос воще, давай сюда свой набор данных и примеры выборок на которых твоя монга причмокивает :)

anonymous
()
Ответ на: комментарий от Wizard_

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

dizza ★★★★★
()
Последнее исправление: dizza (всего исправлений: 1)
Ответ на: комментарий от mongo

Ну ты сам уже себе ответил: «А пока могу сказать только одно - болтать на гигабайты ворочать.»

Ты же просто болтун, при этом приносить вред своему преприятию.

anonymous
()
Ответ на: комментарий от dizza

Ой да ладно, спорим по половине полей они вообще никогда ничего не ищут?

anonymous
()
Ответ на: комментарий от anonymous

Пшол вон из темы онанимный тролль.

mongo
() автор топика
Ответ на: комментарий от mongo

А как же отсутствие строго определённой схемы. Мне кажется в некоторых задачах это большой плюс.

Это тебе кажется.

Считай сам - объём всех твоих данных при размере записи в килобайт, составляет полтора гигабайта. Ну даже если что-то от нас утаил и записи у тебя занимают не один, а целых два килобайта каждая, вся база занимает всего лишь 3Гб. Такой объём полностью считывается с SSD за 6 секунд (500 МБ/с) а потом, если другой активности нет, и вовсе живёт в файловом кеше. Т.е. в самом медленном случае поиск по такой базе потребует 6 секунд. Это вообще без использования каких-либо индексов и вообще СУБД. Просто тупое последовательное чтение с диска.

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

anonymous
()
Ответ на: комментарий от znenyegvkby

50к документов (все на одном шарде делал), каждый в среднем всего по 2,5-3Кб
2567s

Батенька, я правильно понял, что ES для поиска среди 150 мегабайт данных требуется в районе 40 минут? Это курам на смех.

anonymous
()
Ответ на: комментарий от anonymous

Либо вы что то не учли либо что то действительно пошло не так потому что /var/lib/mongodb занимает 95 G. Может это индексы занимают много места, так что если их снести будет так как вы пишите? Добавление информации в запись или её обновление может так раздувает размер базы данных.

mongo
() автор топика
Ответ на: комментарий от anonymous

Нет, вы не правильно поняли. Это индексация данных, причем чистая тестовая (чистая, это значит что 99% данных строго анализируются перед попаданием в индекс), я ведь расписал, разжую еще раз, чтобы стало понятнее:
6 различных анализаторов, то есть для каждого отдельного анализатора (состоящего из 5-6 фильтров, из которых есть довольно «тяжелые», например, n-gramm. Если представляете как он работает, то представьте сколько частей нужно хранить, например, для слова «дихлордифенилтрихлорэтан» с диапазоном от 2 до 25) будет свой индекс. Плюс учитывайте _source, то есть входные данные, которые также нужно хранить в изначальном виде (если вы, конечно, не попросили обратного)). Естественно, внутри есть оптимизации, можете глянуть код, но все же в конечный индекс люцены попадает довольно много данных.
~140 метров синтетики умножайте на 6, это ~850 метров чистого индекса + _source. И это очень грубо сказано. Причем я отметил там же, что специально используют только один шард, что конечно же увеличивает время индексации (но это все мне для тестов нужно).
Если соблюдать правила, устанавливать экспериментально нужные параметры (шарды, подбор index.refresh_interval (как уже подметили выше) для нахождения оптимального кол-ва I/O-операций в секунду именно для вашего диска, bulb, и т.д. (а если честно нюансов у ES очень много, и оптимизацию на уровне спичек можно начать от всяких index.compound_on_flush/index.compount_format для установки меньшего/большего кол-ва файлов, если у вас SSD/SAS, и закончить тем же index.index_concurrency для нахождения баланса параллельных индексирующих потоков именно для вашей конфиги)).
Это было отступление о том, почему ES не очень быстр в индексации. Но в этом вся его фишка. Индексация довольно «глубокая», и поиск в конечном счете получается гибким и быстрым. Вот вам пример для поиска.
На тестовые данные что я предоставил выше, добавим через mapping еще парочку полей для «псевдонагрузки» в запрос. Я добавлю поле типа string и назову его type. Это позволит установить нам 5 различных типов документов (st1/.../st5). Ну и еще добавлю поле is_enabled, скажем, просто в довесок (учтем что в каждом следующем запросе эти поля еще сверху накидываются, причем я индекс на них не вешаю, как обычно это делаю в СУБД, в тех полях хранится только _source для точного совпадения). Перезапустим индексацию, распределив типы документов на все 50к в равной доле, и установим для 5% документов is_enabled в 0. После этого формируем глобальный запрос. Если простыми словами - я склеиваю несколько запросов в один (не важно чем bool/dismax/что вам удобно). Получается примерно такой конечный результат (все увеличения веса, делаем скажем с помощью boost), ищем по всем типам документов (st1 - some_type_1):

  • все запросы с точным совпадением для st1 (показатель веса (score) умножаем на 10к - N, где N от 0 c шагом в 1к, допустим)
  • ...
  • все запросы с точным совпадением для st5 (score*6к)
  • далее такие же 5 запросов, но уже для префиксного поиска (для них N от 6к до 1к)
  • ну и в самом конце, допустим, запрос для неточных совпадений сразу для всех типов, чтобы из *пагоды в маем городи* делать *погоду в моем городе*, (ну вы понялЕ) (причем через levenshtein distance/n-gramm/словари/или как вам будет удобно, я использую специально избыточный n-gramm для увеличения нагрузки)

На каждый из этих подзапросов я выставляю процентное совпадение (для точных префиксных, допустим, не менее 50% совпадений), и еще кучи разных настроек. И у меня на такой объединенный запрос (на тех же тест-данных и на той же тест-машине) уходит в среднем от 0.06s до максимум 0.09s. Согласитесь, ни одна СУБД вам не даст такой свободы ни в плане времени, ни в плане извращенности :)

znenyegvkby
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.