Где было бы корректно обрабатывать данные, на стороне БД или ЯП?
Приветствую!
Имеется задача достать данные из базы данных, по довольно сложному алгоритму. Из сложных подзадач можно выделить - подготовка расписания пользователя, таким образом, чтобы на выходе всегда было хоть что-то, но по-умолчанию выводится наиболее удачное расписание, то есть есть некий алгоритм ранжирования отрезков расписания. Например, между датами должно быть минимум один день, и они должны быть в одно и тоже время(но если это невозможно, то тогда увеличить разброс). Также идет фильтрация по расстоянию от местоположения пользователя. В идеале планировалось составлять расписание таким образом, чтобы все события в расписании проходили как можно ближе друг к другу(но не обязательно). Подобная не строгость фильтрации добавляет сложность алгоритму поиска.
Сейчас фильтруется в php, и запрос не успевает отработать в большинстве случаев, большей частью из-за проблем в конвертировании timestamp из базы в понятный формат для php и операции фильтрации со временем. Плюс построении матрицы расстояний. Это возможно перенести в базу, но для гибкости запроса(алгоритм ранжирования может меняться произвольным образом), будут написаны хранимки, которые частично похерят индексы, плюс будет геморрой при возможной кластеризации БД, да и поддерживать этот код будет трудно.
Вопрос заключается в следующем, где было бы правильно реализовать подобный сложный поиск? Можно вынести в монгу, сделать mapReduce, а скорость подтянуть исключительно кол-вом машин, обрабатывающий этот запрос. Например, на каком уровне решают задачу коммивояжера, в бд или в коде? Но в том же php можем упереться в ограничение памяти, т.к. нам нужно получить все данные, коих довольно много.
Решение алгоритма не требуется, требуется просто совет, где было бы корректней реализовать подобное, как делают поиск крупные компании. Всем добра!
я бы делал всю логику в коде и начал бы со следующих шагов
В базу:
- берем выборку самых тормозных запросов
далее EXPLAIN в руки и выясняем где что тупит)
- делаем индексы (с умом) если нужно
- делаем вьюшки (если нужно) в базе что б не работать с совсем сырыми данными
- даты на стороне базы не конвертируем - это тоже очень медленно будет
В коде:
- профайлер в руки и профилировать, профилировать, профилировать
- используем методы динамического программирования (у вас задача практически классическая)
- если все равно тормозит как вариант наколбасить что-нить типа сишной либы для этой конкретной задачи и дергать ее из хпх
Как один из вариантов. А это не противоречит ответственности "слоев"? Например, код - бизнес-логика, а база - хранение и фильтрация/сортировка/etc данных. Получается часть задач базы на себя берет код, тем самым мы признаем что наше хранилище не способно совершать подобного рода запросы. Может тогда хранилище не то? Не холивара ради, а понимания для) За динамическое программирование спасибо, ща буду изучать!
ну если уж так формально смотреть то мы ничего не нарушаем
база за фильтрацию, сортировки и не должна отвечать
ее задача (грубо говоря) дать вам хранимое состояние системы которое гарантированно устойчиво к ребутам рестартам и прочей ерунде (кароче то что называется persistence )
все остальное - ну бонусы опциональные
Возможно мне не хватит квалификации, но:
Посмотрите, сколько времени занимает запрос, частота запросов и обновление данных, возможно есть смысл кэшировать результат и самому инвалидировать его. Изучите код внимательнее, там могут быть места для локального кэша из серии, когда значение некой переменной уже рассчитывалось и можно его заново не узнавать. Для PHP timestamp не так уж и сложно, загоните его в DateTime и может вам станет проще. В больших циклах можно использовать yeild, вроде как это несколько решает вопрос с памятью, почитайте об этом. Также, конечно же сделайте профилирование своего кода и узнайте, где самые тяжелые места и пляшите от этого.
В целом вы правы. Даты загоняются в DateTime, но получается что их там 300000+(дальше будет больше 10000000), где надо еще каждый подравнять и найти пересечения, все отсортировать, а потом сгрупировать. С другой стороны было бы возможно хранить прямо ts и вычислять исключительно математически без создания лишних объектов, по факту получается, но также нужно исключить выходные дни и тут без преобразования в DateTime я уже не обойдусь. Кешировать получиться малую часть, запросы хоть и похожие, но исходные данные постоянно меняются, т.к. сам запрос меняет эти данные, выигрыш будет, но не большой + будет повышенное потребление памяти. Узкие места знаю где, их довольно много, и решение каждого будет напоминать затыкание дыр тонущего корабля) Потому и вопрос поднялся. Возможно я просто взял неподходящий инструмент.
nobodywithbody, как долго уже сейчас выполняется скрипт? Как часто его нужно вызывать? Он вызывается массово для всех пользователей или для конкретного?
Как вариант разбить БД на партиции, это может ускорить процесс, за счет того, что будет извлекаться меньшее количество данных. И опять же, посмотрите, что дольше всего выполняется. Если PHP много считает, но не очень тяжел, его можно переписать, чтобы он обрабатывал разные порции и запускался параллельно. Почитайте про mysqli_real_query и mysqli_multi_query, это может помочь выжать еще немного. В целом всё крайне специфично.
И повторюсь: профайлинг творит чудеса. Ускорение функции на 10-20мс, что вызывается 300000, может дать хороший прирост.
Узнайте, может есть смысл в SSD(если его нет), дальше новые технологии и горизонтальный шардинг.
nobodywithbody, P.S. К примеру, вы уже знаете, что какая-то дата это выходной, вы можете сохранить это значение, чтобы потом его не высчитывать и скорее всего, если есть пересечения, заново не проверять. Ищите isset'ом в массиве/кэше и всё. Посмотрите в сторону графовых БД, не исключено, что они помогут в пересечениях.
Скрипт способен на текущей момент выполняться до 2 минут. Это можно сказать сердце сервиса, потому вызывается часто. Вызывается для конкретного пользователя. Распараллелить не плохо звучит. Но я так понял, вы за вариант решения задачи непосредственно в коде?
А о каком кол-ве данных идет речь? Чтобы сделать алгоритм работащий для таких задачь я так понимаю данных должно быть много. Чтобы работать в бекенде быстро и еффективно придется очень и очень сильно оптимизировать этот потом с запросами и обработкой, при условии, что нету возможности просто взять все нужные данных и работать с ними уже в оперативке.
Если же все таки такой возможности нету, то или как уже написал оптимизация запросов - обработок. Или же вообще все сделать на основе триггеров или сторедпроцедур.
Ну и про поддержку верно, есть ли у вас или являетесь вы сами таким специалистом, который в sql чувствует себя как дома?
Трудно конечно ответить на такое без тестов) я тоже не гуру, но в моем случае я бы писал 2 варианта и тестил наилучший из них. Да это в 2 раза больше времени, но если важен качественный результат, то приходится и таким жертвовать )
Данных правда много(относительно конечно), более 1000000. К сожалению специалистом в БД такого уровня не являюсь, как и такого же рода специалистом в алгоритмах. Поэтому на данном этапе оно умудряется тормозить везде) Можно конечно наделать разных вариантов, сейчас есть около 4, плюс всякие вариации на тему, но хотел не велосипедить, а узнать может кто сталкивался. Типа "когда запросы подобной сложности и кол-во данных тоже много, то надо копать в elasticsearch/postgres/mongo/erlang/алгоритмы", или вообще комбинация "Простые запросы правильно в бд, потом парареллить фильтрацию в коде", мол best practice и все такое.
nobodywithbody, как уже и написал я не гуру в таких делах. Но первым делом я бы смотрел возможность сделать такой скрипт через сторед процедуру с вводными параметрами от пользователя, которые передаются уже путем пхп в эту процедуру. В любом случае это все запросы. И если делать их частично буферизируя уйму данных - вот что и будет оверкиллом в первую очередь.
Можно более конкретный пример? Желательно целым Use-case'ом.