Я вот всё думаю, как бы так обтекаемо зайти в тему, чтобы было интересно и не палевно.
Сейчас я работаю в качестве бэкенд-разработчика над системой управления кассами и бизнесом «Мой кассир». Честно сказать, я никогда до этого не интересовался над позиционированием бизнеса. Во-первых, это не так чтобы моё дело, я код пишу, а не стратегию вырабатываю. А во-вторых, просто не интересовался) И вот что сейчас заметил из интересного. Я воспринимаю свою работу как систему автоматизации бизнеса, а сайт как бы намекает, что это касса + дополнительные плюшки. Ну ок-ок, чо =) Наверное, это потому что к кассам я фактически никак не отношусь, хотя с другой стороны я же пишу API на серверной стороне. Всё сложно =)
И так. Я Ильдар, я разработчик, похлопаем Ильдару.
Проблематика
От клиентов на сервер ходят много много разных документов: чеки, возвраты, списания и ещё чёрта лысого. Наш главный говорит, что нужна статистика по некоторым показателям. Типа продажи за день. Документы мы храним в монге, потому что она изначально заточена под работу именно с документами, это во-первых, а во-вторых, она быстрая и умеет в безболезненное шардирование.
Очевидное решение
Первое решение, которое приходит в голову. Берём документы из базы и на стороне кода считаем нужные числа. Но следующая мысль: «А если за месяц? А за год?» — объём потребляемой памяти может выйти из под контроля. В первом приближении это можно залить железом, и чаще это решение является самым быстрым и эффективным. Но математика вещь безжалостная, и хотя она говорит, что есть бесконечности, в действительности все ресурсы ограничены. А ещё бизнес очень хорошо умеет считать деньги, и если есть более дешёвое решение, то бизнес его захочет.
Давай порассуждаем какие преимущества есть у решения в лоб? Простота реализации. Тупая выборка документов, тупой перебор с инкрементом нужных значений. Даже джун с этой задачей справится за полдня, с учётом перерыва на кофе.
А ещё это очень гибко. Основываясь на этом подходе очень легко рассчитывать абсолютно любые показатели по любому запросу: продажи за период, продажи за период одним продавцом, продажи в определённый час каждого чётного вторника. Т.е. имея плоскую структуру можно всё что хочешь получить, лишь бы хватило мощностей.
А вот мощности в какой-то момент закончились.
Решение на стороне хранилища
Следующим решением будет переложить логику с приложения на сторону БД. Раз монга такая быстрая, то пусть она и работает. Агрегации это хорошо. Инфраструктурно не меняется ничего — всё та же коллекция документов. А код даже упрощается, вместо циклов и счётчиков получаем 1 запрос в БД и практически прямой вывод.
Только вот если построить хреновый агрегирующий запрос, то можно положить хранилище, а это уж более серьёзная проблеме, чем полёгший коннект у одного пользователя. Поэтому придётся хорошенько подумать и обмазаться индексами. В MongoDb вообще очень много завязано на правильные индексы, не надо ими пренебрегать.
И вторая проблема, которая мне особенно не нравится — размазывание логики приложения, т.е. мы как бы всё ещё в рамках API, но ответственность по расчётам перекладываем на хранилище. Что, на мой, взгляд не правильно, задача хранилища делать простые операции: хранить информацию, добавлять, удалять, обновлять — а не считать. И снова вопрос потребления ресурсов. Хотя тут мы избавляем основное приложение от чрезмерного аппетита, но наделяем этим аппетитом сервер БД.
Изощрённый подход
Идея агрегаций была интересной, поэтому много думав было предложено (надо признаться, что это не моё решение, я уже подхватил его и принял) не строить агрегации на лету, а хранить агрегирующие документы. Т.е. делим время на какие-то периоды и считаем статистику в каждом интервале, например в часовом. Теперь вместо выборки 100+ документов, можно выбрать 1, а соответственно получаем стократный прирост производительности. А если пойти дальше, то можно хранить и более крупные агрегации, неделя, год.
Получаем значительную экономию по вычислительным ресурсам, объём хранилища сейчас не проблема, когда за 8 тысяч можно купить терабайт SSD. Но при этом мы получает дофига сложностей.
Недостатки
Большие инфраструктурные вложения. Точнее это у нас так получилось, можно было бы и проще всё построить, но тогда мы бы потеряли преимущество скорости. У нас много отдельных сервисов, которые в фоновом режиме обсчитывают статистику. Это хорошо из соображений, что можно эти сервисы положить на отдельный сервер и основное приложение не будет страдать под нагрузкой. Во-вторых, агрегирующие документы можно та же унести в отдельную монгу и основная не страдает. Но за это мы получили много головной боли под разворачиванию всех сервисом, если мне хочется весь стек поднять локально, то на данный момент мне может потребоваться понять 60+ контейнеров.
Дебаг усложняется. Мы, конечно, критичные места стараемся покрывать тестами, но если что-то где-то всплывёт, и не понятно в каком месте затупило, то все эти много контейнеров приходится локально поднимать. Ну и проход по всем слоям сервисов займёт некоторое время.
Высокий порог входа. Всегда, когда приходится лезть в недра, я консультируюсь на тему «а кто как и в какой последовательности работает». Хотя большую часть кусков писал сам. Но если что-то работает и ты его полгода не трогаешь, то детали забываются. Конечно, далеко не всем нужно понимать как это в сумме работает. Тем не менее, стоимость тех.лида возрастает от сложности системы.
Потеря гибкости. Если сейчас менеджеры придумывают какой-то показатель, который не влезает в текущие агрегаты, ну например, надо посмотреть показатели по определённой категории покупателей, а у нас нет агрегатов на такое. В сырых чеках, конечно, можно это посмотреть, поэтому быстро пилится решение в лоб. А потом делается каноничное решение, канон в рамках текущей архитектуры. Сначала продумывается документ, а потом приходится запускать пересчёт нужных показателей за весь период. А вот уже нормально нагружает весь контур обсчёта статистики.
Правильный подход
Мы оказались в заложниках БД. Клиентов уже есть некоторое количество и они уже работают. Поэтому сменить хранилище быстро и безболезненно сейчас сложно. А вообще-то есть более специализированные хранилища, а именно колоночные базы данных. Очень быстрые и заточенные как раз под статистику. Сейчас мы движемся в этом направлении, оно тоже сопряжено с некоторыми особенностями построения. Но на данный момент это лучший вариант, который мы видим.
Эта история не имеет финала на данный момент, так что продолжение следует.