В этой статье я хочу рассказать о своих ошибках, которые я допустил, когда писал сервис, у которого MongoDB была основной БД для хранения пользовательских данных (да и не только, но об этом ниже).
Я ни в коем случае не считаю, что MongoDB это плохая БД и ее не нужно использовать. Более того, я считаю, что только мои кривые руки завели меня в ситуацию, из которой пришлось выходить переписыванием сервиса под другую БД (ушел на Postgres и кайфую).
Тем не менее, нельзя знать всего и чтение документации не спасает от катастроф во время самой реализации проекта. Особенно, если ваши ожидания от инструмента разошлись с реальностью.
На мой взгляд, маркетологи MongoDB приукрасили области применениях БД на своем сайте. MongoDB не универсальная. Далеко не универсальная и даже не пытайтесь на нее смотреть как на решение всех ваших проблем.
Не использовал ORM или хотя бы DTO
Начну с первой пули калибра 155 мм в ногу. Если после чтения этого блока захочется смеяться и говорить, что "ну это же очевидно", то поверьте мне, я не один такой наивный.
Используя MongoDB очень легко можно наговнокодить, если очень торопиться и не тратить время на написание dataclass или DTO.
Когда я начал писать, я взял pymongo. Без всего. Это показалось мне очень удобным: создал клиента, подцепил конфигурацию и вперед. Можно быстро обращаться к коллекциям и писать в них вообще не думая о схемах данных. Безусловно, если вы один разработчик, то в начале вам не о чем беспокоиться. Сервис маленький, вы один. Весь проект помещается в вашу голову. Какие тут могут быть проблемы?
Через месяц-два вы все-таки начнете допускать ошибки в именах полей. Первый раз это будет выглядеть как простая опечатка, которую вы ищете полчаса. "Почему же это поле не записалось?". Ааа, потому что я чудак, вот почему: user_email вместо просто email. Хорошо, если вы это заметите раньше, чем эта ошибка уедет на продакшн и часть данных создастся с неверным полем. Если это случилось: извольте написать миграцию и перенести данные. Опять-таки: хорошо, если это всего лишь простая ошибка в поле и она не связана с типом данных.
Ошибку я попытался исправить поменяв PyMongo на MongoEngine. Спойлер: сильно легче не стало.
Используйте классы или контейнеры для данных. Пишите валидации данных. Все это очевидно, но уже постфактум. И тесты пишите.
Верил, что без миграций и схем данных в MongoDB будет легче
По-началу кажется, что "инкрементить ценность проекта" будет легче без схем БД. Нет, нет и еще раз нет.
Миграции для развивающегося сервиса это не только про переименовывание полей или добавление новых. Это еще и про целостность и понятность данных. В MongoDB можно записать в одну коллекцию самые разные по структуре документы. Они могут быть вообще разные, ни одним полем (ну кроме id) не похожими друг на друга. И это очень большая проблема.
Я уже молчу про то, чтобы сделать быструю DSL на основе схемы данных, потому что... а нет ее этой вашей схемы данных. Если вам приспичит что-то переделать, то вам придется строить очень сложный запрос (я в итоге честно нагуглил, когда после второго дня сдался), чтобы собрать структуру данных сложного документа, который наполнялся в течение почти года.
Схемы данных — это порядок и дисциплина. Схемы данных — это валидация. Это возможность быстро окинуть данные проекта взглядом и понять как устроен ваша модель или объект. Без схем оооочень больно. Если сюда еще прибавить мою первую ошибку, то представьте как у меня горела задница, когда я переносил все в SQL.
Стрелял в ногу типами данных
Инт или стринг? MongoDB пофигу. У вас может быть два документа с одним и тем же полем, скажем, age, но если на стороне кода приложения ошиблись, то ловите эксепшены. Тысячи эксепшенов и громкий мат из вашего рта среди ночи. Без тестов или DTO в спешке, которая присуща почти любому стартапу, вы будете постоянно ловить эту проблему. А ваша любимая IDE этого может и не заметить (указывайте типы в ваших функциях, ага, знаю).
Думал, что MongoDB быстрая и будет такой всегда
MongoDB действительно может быть быстрой - когда нужно тупо писать и не думать о том, что когда-нибудь придется читать данные или, о боже, делать сложные запросы, где вы захотите сделать aggregate или имитировать join нескольких таблиц коллекций.
Вы где-то читали, что MongoDB экономная в плане потребления ОЗУ? Это вранье. С ростом данных MongoDB будет жрать много ОЗУ. Это зависит сразу от двух факторов: кол-во данных в БД и какого рода операции вы захотите делать.
JOIN это очень дорого, а query по вложенным документам - это гарантия сломать мозги
Авторы MongoDB рекомендуют не использовать монговский JOIN. Он там как бы есть, но он говно. Правильно делать один документ и в нем хранить все связанные данные. Все как бы здорово, но попробуйте потом сделать query по этим самым данным. Сложный query, подразумевающий исключения и логику "или". Это больно, как орбит дверная ручка.
Аналитика — тот еще цирк
Собственно, эта проблема исходит из предыдущей. Посчитать что-то сложнее, чем просто total() - нужно очень сильно заморочиться. Ответить на простые вопросы типа "сколько у нас объектов N для пользователей B, которые входят в категории C,W,Z и делали вот такие-то вещи" в MongoDB превращаются в многоэтажные нечитаемые JSON-подобные запросы, которые трудно поддерживать. Запросы нечитаемые. Можно, конечно же, научить обезьяну курить, но какой в этом смысл? MongoDB не для аналитических данных.
Поиск это тоже цирк с двуногими конями
На сайте MongoDB есть сладкие примеры инвентарок, магазина, коллекций музыки или книг. Они такие классные и такие в вакууме, что в моменте, когда вам нужно реализовать поиск с разными условиями, то волосы на жопе шевелятся. То, что в SQL делается легко даже новичками, в MongoDB превращается в адский ад.
Я тоже думал, что поиск будет легко реализуемым. Таким же простым, как писать запросы на запись без миграций, схем и всего, чем обычно пугают сравнивая SQL и NoSQL базы данных.
Я пробовал уйти от проблем с поиском путем разделения больших документов на отдельные коллекции. Ну знаете, выделить из user его список загруженных документов перенеся их в отдельную коллекцию files. Но это подразумевает, что вам нужно его обратно джойнить, когда он вам потребуется. Но чем больше одновременных запросов к БД, тем ей больнее -- потребление ОЗУ растет как на дрожжах.
Поиск по MongoDB это дорого, сложно и не имеет никакого сравнения с простотой SQL.
От чего еще болело
Одним списком, чтобы не вставать дважды.
Рецептов и готовых решений мало или вообще нет
Расчеты и встроенные процедуры — забудьте. Без опыта JS это сложнее SQL в разы и дорого
Нормальных бесплатных GUI нет (несмотря на то, что я умею в терминал и люблю консольные приложения). Все глубоко урезанные (привет, Robo3T) и все равно заставляют писать запросы руками, которые вновь подчеркну, далеко не сахар как SQL
Поддержка платная и оооочень дурная (кто пользовался, тот знает)
Так для чего MongoDB реально подходит?
Все-таки инструментом пользуются. Здесь я перечислил то, как я вижу правильное назначение для MongoDB:
Логи. Старые добрые логи, которые пишут потоком из разных сервисов и читают раз в полгода, если случился инцидент.
Данные, по которым вы никогда не будете искать дальше самого верхнего уровня. Максимум: взять какой-нибудь документ по его ID или другому полю (например, user_id, который вы до этого взяли из другой БД).
Данные, которые вы будете писать большую часть времени разом и одним документом. Редко когда придется менять отдельные поля.
Кэш большого объема, для удобства разбитый по отдельным полям. Его быстро можно прочитать, легко инвалидировать. Он в виде структурированного документа может быть сразу использован вашим приложением.
Сессии (хотя вопрос скорости работы все еще остается). Аналогично кэшу. Создали документ, бросили его в MongoDB и взяли через какое-то время по одному-двум полям полям. Без глубокого поиска. Максимально просто. MongoDB, кстати, удобно шардится и реплицируется - это прям реально ее преимущество.
Очереди. Точнее, можно использовать MongoDB как БД для хранения очередей данных для последующей обработки. Аналогично кэшу и сессиям MongoDB дает возможность хранить структурированный документ с данными очереди.
Как видите, прежде всего я пишу о вещах, которые не подразумевают поиска по данным в MongoDB и не будут требовать соединять, агрегировать или как-то еще извращаться.
Итоги
Все ошибки, которые я допустил, умножались на кол-во разработчиков, которые приходили в проект и что-то дописывали от себя. Иногда придерживаясь уже написанного, а иногда добавляя свое творчество в зависимости от своего опыта. В результате ком проблем рос и в какой-то момент добавлять новые фичи и выполнять отладку стало просто невыносимо. С учетом, что это далеко не первый проект, я сам был в шоке от того, как я в этой ситуации оказался.
Отсутствие схемы данных — это не преимущество MongoDB, а громадный недостаток. Невозможность делать нормальные джойны (подчеркиваю: нормальные, а не монговские с конским оверхедом) это тоже проблема, которая не позволяет разбить БД на логические блоки (или, проще говоря, навести порядок). Любой проект можно уложить в любую популярную SQL БД, а вот в MongoDB далеко не все.
Кто-то может сказать, что я неосилятор и вообще RTFM. Возможно, но я не хочу бороться и подгонять инструмент, я хочу его использовать и быть уверенным, что он меня не подведет, а я буду точно знать что он может и чего не может. Мне хватает опыта понять, что нет серебряной пули. Думаю, что я повелся на сладкие речи маркетологов MongoDB и решил, что она поможет стартапу быстрее развиваться. Результат: эта статья и +10 к опыту.
Тем не менее, не бойтесь экспериментировать и переписывать. В конце концов, я все равно считаю это хорошим опытом. Благо, мне хватило мозгов писать сам код приложения так, что переход из NoSQL в SQL не занял чресчур много времени и денег. Да и проблему я заметил достаточно рано. Я даже смог переписать все без остановки сервиса, по кусочкам, но это уже другая история.
Главное, если вы будете также как я наивны и поймете, в чем ваша ошибка, поделитесь вашими косяками с окружающими — может быть кто-то уже готов прыгнуть со скалы, но передумает прочитав что-то подобное.
Комментарии (107)
Fen1kz
12.10.2022 07:55+37Disclaimer — я не монгофанбой, хотя и знаю её намного лучше чем SQL.
Сейчас я просто опишу свой опыт успешного использования монги для стартапа на 4 бекендеров и предположу, почему автор огреб
Не использовал ORM или хотя бы DTO
Здесь у нас был nodejs + typescript + graphql, соответственно всё, что ты писал в коллекции, валидировалось по схеме. Хочешь поле в базе? Добавь сначала в схему. Ни разу левое поле не записывали, ни разу не читали.
Верил, что без миграций и схем данных в MongoDB будет легче
см. выше про схему
Стрелял в ногу типами данных
Третий аргумент про схему данных? Да хватит!
Думал, что MongoDB быстрая и будет такой всегда
Ну, на базе в несколько гигабайт и с 500000+ записями она была быстрой) Главное индексы ставить.
JOIN это очень дорого, а query по вложенным документам — это гарантия сломать мозги
Делали минимум вложенных документов, только join через агрегации.
сколько у нас объектов N для пользователей B, которые входят в категории C,W,Z и делали вот такие-то вещи
Подскажите, а где-то это легко?
Кстати, агрегации довольно легко читаются если привыкнуть.
Поиск это тоже цирк с двуногими конями
Да это боль. А где нет?
Я пробовал уйти от проблем с поиском путем разделения больших документов на отдельные коллекции. Ну знаете, выделить из user его список загруженных документов перенеся их в отдельную коллекцию files. Но это подразумевает, что вам нужно его обратно джойнить, когда он вам потребуется.
Конечно надо делать files в отдельной коллекции.
Стоп, wait a second…
Аргументы автора: нет схемы, join это боль, join это боль, join это боль...
Хммм...
Предполагаю, что автор вместо агрегаций писал джойны в ORM, а потом перешел с ORM на SQL и возрадовался.
Что, считаю, в случае с монго совсем неправильно, и агрегации более правильный путь.
Aggregation pipeline это тот же "язык" запросов на котором можно безболезненно делать любые джойны и прочее. Более того, за счет того что это JSON, очень удобно делать хелперы:
export const lookupUserFiles = (filePipeline) => ({ $lookup: { from: 'files', as: 'files', let: { userId: '$_id' }, pipeline: [ { $match: { $expr: { $eq: ['$userId', '$$userId'] }, } }, ...filePipeline, ] } })
Которые потом можно легко комбинировать, например найти юзера с его файлами, размер которых больше 100мб и у этих файлов взять все комментарии:
db.users.aggregate([ $match: {$_id: userId}, lookupUserFiles([ $match: {size: тут не помню, но типа больше 100мб}, lookupFileComments() ]) ]
Так же хочу заметить, что у автора проскакивает инфа о том, что у него было "мало" больших документов вместо множества мелких. Тоже считаю что это ошибка и основной источник проблем с перформансом.
Заключение:
Отсутствие схемы данных — это не преимущество MongoDB
Отсутствие схемы данных — это преимущество MongoDB, потому что схему данных мы можем сделать свою. А зачем свою? А чтобы лучше интегрировать её с остальными частями.
Невозможность делать нормальные джойны
Ещё раз предположу, что автор неправильно разбил коллекции, вместо схемы положился на ORM и не смог в агрегации, что отправляет его RTFM
ptr128
12.10.2022 13:30+26Ну, на базе в несколько гигабайт и с 500000+ записями она была быстрой) Главное индексы ставить.
Вы прикалываетесь или издеваетесь? БД в несколько гигабайт почти всегда вся в оперативку влетит. Я, обычно, занимаюсь БД размером от нескольких терабайт и миллиардами записей. Вот там уже можно что-то говорить о производительности СУБД.
И на таких объемах MongoDB действительно сильно уступает по производительности сложных аналитических запросов таким СУБД, как ClickHouse или PostgreSQL. Но замечательно подходит, как буферная БД для сбора информации из внешних сервисов, в которых без предупреждения могут меняться как типы полей, так и появляться новые поля или даже целые подчинённые структуры.
stranger_shaman
12.10.2022 13:47+18Подскажите, а где-то это легко?
в постгресе, да и в любой реляционке это легко
Fen1kz
12.10.2022 15:20-1Да? Типа если есть 3-5 связанных сущностей и надо сделать поисковый запрос по всем — это будет легко все это заджойнить да отфильтровать?
JordanCpp
12.10.2022 15:30+9Да, в этом огромное преимущество реляционных БД. Для скорости юзаются индексы + горячие данные хранятся в озу и полируется кешированием. И скорость поиска в районе скорости света:)
Fen1kz
12.10.2022 16:00+6Хех, я бы расспросил подробнее, потому что в нереляционных бд тоже есть индексы, а данные-то могут быть и не в озу, но тут публика минусует как будто я с вами спорю, поэтому просто поставлю плюсик, спасибо за объяснение)
JordanCpp
12.10.2022 16:11+5Идея в том, что join + индекс это быстро и встроено в бд. Реляционные бд созданы, что бы хранить и обрабатывать связанные объекты из коробки. К примеру в Postgresql есть поддержка json, аналог функционала mongodb. И даже если нужна keydb, можно юзать Postgresql с синтаксисом sql.
Для обеспечения производительности есть рекомендации, советы, инструкции. Первое, это хранение индексов в ОЗУ. Деление данных на горячие и старые холодные данные, что бы обрабатывать меньший объем. Что бы быстро работать с данными нужно думать как база данных:) Я просто начинал с реляционных бд и для меня был некий ступор, что в mongodb как то сложно организовать связи, типа чего? Как делать выборку. Для keydb это очень специфичные задачи типа хранения логов или не связанных данных. Но это сейчас организуется и в Postgresql, так зачем плодить сущности. Шардинг тоже есть.
vanxant
12.10.2022 21:42+7Простите, 500к записей можно хранить в CSV файле, и оно тоже будет быстрым.
У нас тут недавно автокрементный id типа unsigned int переполнился, вот это было немножко больно:)
Lexicon
12.10.2022 08:47-3Я не понимаю, как ваша статья прошла песочницу.
От статьи ощущение будто вы реально покопались в монге в 2012 в одиночку, а спустя 10 лет решили рассказать, попутно добавив людей, которые яко бы пришли в проект без знаний nosql и даже python. Уровень статьи просто пугает, от работы с кодом будто в тетради с ручкой, а не IDE с Python; До ярких заголовков типа «Поиск — двуногий конь», под которыми вы даже не удосужились обозначить «проблему поиска».
Умыть рукия считаю, что только мои кривые руки завели меня в ситуацию
прекрасный подход, но только не когда вы пишете статью с жесткими тезисами, выводами, рекомендациями и петросянством.
Insurgent2018
12.10.2022 09:51+1За что пользователи ставят высокий рейтинг статье - нет ясности. В статье нет информации какие данные(примеры), сколько их, как писал, как читал, как обновлял почему вообще автор выбрал Mongodb.
Рецептов и готовых решений мало или вообще нет
то есть отличные статьи и "университет" на самом основном ресурсе mongodb - автор пропустил?
Статья - набор клише, вредного субъективного мнения. У mongodb есть и плюсы и минусы, что уж тут говорить, инструмент выбирать по ситуации и умению им пользоваться, очевидно вроде.
Jedi_PHP
12.10.2022 12:43+5За что пользователи ставят высокий рейтинг статье
За попытку донести информацию. Хотя уже давно (с момента начала работы Postgres с JSON, т.е. ~8 лет тому) многое было ясно.
Insurgent2018
12.10.2022 12:49+2зареву? всегда считал, что разработчик, архитектор выбирают базу данных не "по-любви", а по целям и задачам. Хотя наверно да, сложно относится с понимаем к тем, кто везде всегда выбирает Postgres.
Jedi_PHP
12.10.2022 13:01+3кто везде всегда выбирает Postgres
Если это было в мой адрес - то не везде и не всегда. Как минимум Clickhouse, Greenplum и Vertica в ряде случаев оказались более подходящими.
по целям и задачам
Монго тоже подходит для некоторых задач, например для склада media assets - сырых файлов с тегами и уже агрегированной статистикой. В рекламной сетке удобно, когда баннер/преролл с результатами его работы и настройками кампании лежат вместе и всё это относительно легко шардируется. Но и потерять эти данные если что - не очень-то и жаль.
akomiagin
12.10.2022 10:11+26На самом деле основная проблема, которую описал автор в статье - это не проблема в самой СУБД MongoDB. В статье собрано практически полное собрание антипаттернов использования NoSQL и key-value хранилищ.
i360u
12.10.2022 10:31+9То есть, вам нужен был SQL, вы выбрали NoSQL, и поэтому Mongo - это фу? Ясно, понятно.
Ivan22
12.10.2022 10:47+14ну не совсем, сначала надо была "ну хоть какая-нибудь любая БД", а только потом с развитием стартапа выяснилось, что база нужна вот такая-вот, но уже поздно. И это очень нередкая ситуация.
i360u
12.10.2022 15:08Ну да, это типичная проблема роста: предсказать все требования на старте, часто, в принципе невозможно. И иногда приходится вводить серьезные изменения в архитектуру... Но вина ли это отдельно взятого инструмента, решающего свою специфичную задачу?
JordanCpp
12.10.2022 15:19+5Вина хипстеров пытающихся заложить архитектуру и объем обрабатываемых данных как у гугла. Вы простая пиццерия, торговый центр, вам не нужно 100500 микросервисов, mongodb, kafka и другие крутые штуки. Вам нужно обработать быстро клик от пользователя и выдать результат на паре миллионов записей, которые и так лежат в озу. История хранится на другой БД для аналитиков. Но нет, они же все будущие гугл с петабайтами информации. Ну вы поняли к чему я:)
Fafhrd
13.10.2022 00:53+1О, да! В первой версии бэкенда мобильного приложения(без заказов) одной из сетей быстрого питания всё было спроектировано максимально упорото. Что можно было сделать просто, делалось максимально сложно и наоборот. В итоге проблемы с производительностью решались путём натягивания совы на глобус.
А по факту на 300к юзеров в сутки всего-то надо было базу подтюнить и некоторые запросы переписать =]
ggo
12.10.2022 10:32+15Здесь я перечислил то, как я вижу правильное назначение для MongoDB:
Вообще, первый пункт, когда нужно выбирать монгу: данные непрерывно растут в объеме и необходимо дешевое горизонтальное масштабирование.
Конечно, это не мешает использовать монгу на одном инстансе. Но нужно помнить, что для реализации дешевого горизонтального масштабирования создателям монги пришлось пойти на ряд компромиссов. И когда вы не используете главную фичу монги, вы не получаете самую важную ценность монги, но при этом получаете в нагрузку все недостатки монги, которые и в одном инстансе никуда не делись.
Аналогичная история с кафкой.
lehha
12.10.2022 10:57+1Еще одна интересная особенность MongoDB - если файл на диске поврежден (в нашем случае повреждено nvme (waaat????) и побиты некоторые коллекции, то при попытке прочитать что-то из такой коллекции полностью крашит весь процесс. То есть весь прод ложится из-за повреждения одной коллекции. В mysql таблица просто помечается битой и вся остальная база продолжает работать.
По объему - да, монга работает быстрее с большими данными, но видимо из-за принципа работы. Если в mysql сделать select на таблице в 100млн записей, то они все летят на фронт. В Монге ты делаешь итерацию по объекту и данные подтягиваются по мере продвижения по массиву (хотя может я застрял в прошлом веке и mysql так же умеет?). Без индекса ходить в коллекцию нет смысла, получишь timeout (на большом объеме), как и в mysql.
euroUK
12.10.2022 11:33+7Я фигею, дорогая редакция. Вы думаете что в СУБД нет пагинации?
Статья настолько же странная - вот что бывает, когда на проект вместо архитектора берут джуна. Оказывается, что в монге сложно джойнить! Кто бы мог подумать!
lehha
12.10.2022 12:50-3Дело не limit, а в принципе отправки данных с сервера клиенту - если этот эффект делает под капотом ORM на запрос SELECT * FROM table, то ок, а если это by design самого mongodb, то вот и отличия.
euroUK
12.10.2022 16:02+5Мне не нужны такие байдизайны.
Если мне нужно 10 записей, я кверю 10, если миллион, я кверю миллион.
А программисты не должны быть рукожопами, иначе можно обезьяну вместо них сажать.
JordanCpp
12.10.2022 16:20+2Так то sql создавался для домохозяек простой язык запросов. Это было раньше, уровень образования и погружения в компьютерную науку был выше. Теперь тяп ляп на орм и в прод. Ибо горит, бизнес требует. Все быстро и что бы работало. Медленно, быстро все равно. Главное сегодня и сейчас. Как то так. Вот вам и вынужденное обезьянство.
mayorovp
13.10.2022 00:41+1Нормальная ORM идёт с построителем запросов (query builder), который умеет вызывать что-то кроме
select * from …
. Если ORM такого не умеет — её просто не надо использовать в принципе. Так что чтобы не выбирать ненужных записей — надо лишь найти как эти самые запросы правильно строить.А если программист вместо ORM и её построителя запросов фильтрует массивы — это и есть обезьянство, притом добровольное.
IvaYan
12.10.2022 12:39+2В Монге ты делаешь итерацию по объекту и данные подтягиваются по мере продвижения по массиву (хотя может я застрял в прошлом веке и mysql так же умеет?).
А разве выборка результат построчно это не станадртная функция всех СУБД?
lehha
12.10.2022 12:57-1Похоже, что это уже слой ORM. Так как если сделать "select * from table" на 100 млн записей то все эти данные сначала летят клиенту (в processlist висит статус Sending data), а там уже разгребай их в foreach, если памяти хватит.
в php есть итераторы, которые в связке с ORM позволят сделать foreach на такой запрос быстрым, добавляя под капотом в запрос limit.
IvaYan
12.10.2022 13:05+9Так как если сделать "select * from table" на 100 млн записей то все эти данные сначала летят клиенту
В нормальной СУБД с нормальным клиентом не летят. И ORM тут не причем, LIMIT тоже. Если у вас летят, то у меня для вас плохие новости. Хотя зависит от того как вы на уровне клиента данные запрашиваете.
lehha
12.10.2022 13:17php 8.1.11
memory_limit = 1024M
mysql 5.7.39
таблица 3гб<?php $mysqlhost = "hostname"; $mysqluser = "user"; $mysqlpass = "password"; $mysqldb = "db"; $mysqli = new mysqli($mysqlhost, $mysqluser, $mysqlpass, $mysqldb); $result = $mysqli->query("SELECT * FROM bigtable"); while ( $row = $result->fetch_assoc() ) { var_dump($row); die(); }
PHP Fatal error: Allowed memory size of 1073741824 bytes exhausted (tried to allocate 276840448 bytes) in /test.php on line 8
из коробки выкачивает все данные на сторону клиента.
Монга с таким подходом 300 гиговую базу спокойно отдает построчно в итерации.
Layan
12.10.2022 14:20-1Скажите, а вы в курсе про \PDO в PHP? Вот так будет построчная выборка:
$object = new PDO($dsn,$user,$password); $sql="select * from student"; $result = $object->query($sql); while($arr=$result->fetch()){ print_r($arr); }
lehha
12.10.2022 15:29+2А вы в курсе про тестирование?
<?php $mysqlhost = "hostname"; $mysqluser = "user"; $mysqlpass = "password"; $mysqldb = "db"; $pdo = new PDO("mysql:host={$mysqlhost};dbname={$mysqldb}", $mysqluser, $mysqlpass); $res = $pdo->query("SELECT * FROM bigtable"); while ( $row = $res->fetch() ) { var_dump($row); die(); }
PHP Fatal error: Allowed memory size of 1073741824 bytes exhausted (tried to allocate 276840448 bytes) in /test2.php on line 8
IvaYan
12.10.2022 14:58+1Я не PHP-шник, я из другого мира, но доки читать умею. Про
fetch_assoc
написаноВыбирает следующую строку из набора результатов
то есть выборка все же построчно. А потом вы через
while
обходите все строки. Внезапно, это означает, что все строки придется выгрузить на клиент.lehha
12.10.2022 15:36Речь про реализацию драйвера. Тот же foreach в монге на 300гб коллекции не получает ее всю на клиента, а бегает по ней по сети:
$res = $mongodb->db->bigtable->find( [ ] ); foreach ( $res AS $d ) { var_dump($d); die(); }
С одной стороны, это удобней (не нужно строить цикл с перебором страниц). Код был чище.
IvaYan
12.10.2022 15:38Ну то есть ваш первоначальный посыл, который, позволю себе напомнить, звучал как
Если в mysql сделать select на таблице в 100млн записей, то они все летят на фронт.
неверен? И, как я уже писал, это проблема клиента, так ведь?
lehha
12.10.2022 15:42Посыл остался прежним - клиент эту проблему не решает. Выше меня пытались переубедить что нужно PDO, но нет.
Решить эту проблему - накрутить ORM? Который этот код превратит через итераторы в пагинацию...
IvaYan
12.10.2022 16:08+2О нет, ваш посыл поменялся. Изначально вы проклинали MySQL, а теперь проклинаете клиент. Это уже большая разница. Потому что это означает что можно найти нормальный клиент.
FanatPHP
12.10.2022 18:21Решает, если почитать доку. В РНР можно делать и так и так — можно забрать все сразу, можно цедить по строчке.
JordanCpp
12.10.2022 16:17+1Если клиенту нужно вернуть 100 млн записей, то это не клиент а аналитик. Аналитик работает с другой бд. Если именно клиенту нужно 100 млн записей, то ему реально не нужно 100 млн записей, ему скорее всего нужно пару цифр посчитать или вывести по критерию.
lehha
13.10.2022 10:25В кейсах работы с данными, например, переложить что-то в другую базу-коллекцию, почистить json или найти вот прям нужную запись и не вешать всю таблицу вместе с сервером. Это один из способов.
mayorovp
13.10.2022 00:44А потом вы через while обходите все строки.
Там вызов
die()
не просто так, он ровно одну строку "обходит". Но драйверу почему-то всё равно нужны всеBugM
13.10.2022 02:18Драйвер делает разумное предположение что раз пользователь вот это запросил наверно оно ему нужно. И максимально оптимально читает и отдает пользователю весь ответ сервера.
Kohelet
12.10.2022 16:34+2Вы его готовить не умеете
www.php.net/manual/en/mysqli.use-result.php
www.php.net/manual/en/mysqlinfo.concepts.buffering.php
MaximBobylev
13.10.2022 10:14select * from ...
А о какой именно ORM речь? Ни разу не видел таких запросов.
Так же, если речь про mysql, то отправлять ли результат целиком на клиент будет определяться с помощью
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY
lehha
14.10.2022 10:25А всё казалось таким простым:
As long as there are pending records waiting to be fetched, the connection line will be busy and all subsequent calls will return error Commands out of sync.
Придется открывать отдельное соединение к базе только ради этого.
HellWalk
12.10.2022 11:04+2Работал с MongoDB на двух проектах:
В первом в MongoDB хранились логи - все ок
Во втором, на MongoDB хранились основные данные, и это пц. Просят доработать функционал сортировки - в процессе оказывается, что на каких-то элементах сортировка не работает. Вникаю в данные - а там в каких-то записях параметр отвечающий за сортировку int, в других string, в третьих вовсе отсутствует. И так по всем данным.
Дело было в 2018-2019 годах. Использовалось в связке с php.
JordanCpp
12.10.2022 11:33+1В первом в MongoDB хранились логи - все ок
Для таких задач key db и предназначены. Если юзать для всего, начинается процесс заката солнца вручную.
blind_oracle
12.10.2022 16:54+5Нет, логи это не то, для чего были сделаны NoSQL БД. Для логов хороши либо спец-хранилища вроде Loki, либо просто колоночная БД вроде Clickhouse/Vertica/etc.
Busla
13.10.2022 12:03колоночные БД хороши для метрик - их структура практически не меняется и поэтому данные метрик в колонки выстраиваются. А логи - это последовательность "документов" разной структуры от разных компонентов, уровней абстракций и т.п. Всякие документоориентированные NoSQL БД по своей идее прямо таки идеально подходят как раз под логи.
blind_oracle
13.10.2022 12:43+2База должна выбираться по принципу того какие запросы она исполняет.
NoSQL неплохи для записи больших объемов (LSM деревья помогают, хотя в колоночных используются они же часто) и запроса единичных ключей, но так себе для линейного чтения (нужно читать много sstables с диска, отбрасывать старые версии если они есть и т.п.) и поиска + вторичные индексы по ним довольно сложно реализуемы.
Какие запросы к логам обычно? Найти какой-то текст в заданном интервале времени с какими-то фильтрами небольшими типа имени хоста. Для этого обычно принято юзать полнотекстовый поиск (привет Elastic и ко), но в последнее время прямое сканирование в современных колоночных БД оказывается часто быстрее т.к. данные расположены линейно, правильно партиционированы (по дате, например, хосту и так далее) и прочее.
В NoSQL такие запросы выполняются не очень эффективно.
JordanCpp
12.10.2022 11:23+6Всякие авито, Яндекс для своих сервисов юзают postgresql. Хранят и перемалывают терабайты информации. Конечно это не MongoDB не стильно не модно и не молодежно, но разработчики справляются и с масштабированием и с нагрузками и с сопутствующими проблемами. И что собственно вам мешало заюзать реляционную бд с поддержкой json, стандартным sql? Уверовали key db и стали его пихать всюду. Проблема даже не в кривых руках, а в неадекватных инструментах для решения вашей задачи.
sanchezzzhak
12.10.2022 11:58+3Я за 8 лет монги наелся достаточно.
Из проблем которые у меня возникли:
Постоянно разваливалась реплика из 3х серверов, то арбитр упадёт, то какаета реплика. Но это не страшно.
Самое страшное, это когда у тебя проподают индексы из коллекции причём ладно это был единичный случай, но нет, 4 раза Карл.
Создавал индексы всегда через бекграунд опцию, данных не много 300гб.
JordanCpp
12.10.2022 22:00А как это непотребство на проде то юзать? Это же отвага и слабоумие. Не в вашу сторону, я о молодых, стильных, молодежных. Ест проблемы с фундаментальными вещами типа надёжность, как юзать то?
Bablozavr
12.10.2022 23:29+1>Я за 8 лет монги наелся достаточно.
8 лет монга основная субд
>Постоянно разваливалась реплика из 3х серверов, то арбитр упадёт, то какаета реплика
кривое железо или нет места на диске.
>когда у тебя проподают индексы из коллекции
бред, как и всё вышенаписанное.
данных терабайты. и реплики и standalone.
mayorovp
13.10.2022 00:47+1Вот-вот, как только заканчивается место на диске — монга вместо того чтобы выдать ошибку начинает терять индексы или данные. Это вообще нормальное поведение для СУБД?
MikeEshva
14.10.2022 00:23-2И толку-то от того, что она выдаст ошибку, когда у вас место закончится? Вы логи постоянно читаете? Успеете среагировать?
Для мониторинга состояния оборудования, ресурсов и софта есть специальные инструменты для мониторинга, а для них дэшборды с красивыми, понятными индикаторами, графиками, алертами.
morijndael
14.10.2022 23:25+1Выкинутую ошибку приложение может как то обработать. Например показать пользователю, что "упс, все упало"
А если ошибку не выкинуть, приложение будет считать, что все прошло нормально, и покажет юзеру образный "ваш заказ принят", хотя это нифига не так
Dgolubetd
12.10.2022 12:48+25Действительно, динамически типизированный ЯП + БД без схемы данных + отсутствие хороших практик - казалось бы, что тут может пойти не так?
kpmy
12.10.2022 13:48+1Как и в любой практике тут важно отделять наработанный опыт от фундаментальных проблем.
Можно говорить про потребление памяти Монги, но для агрегаций есть опция allowDiskUse, если сильно надо.
Можно говорить про json-поля в Постгре, но я в упор не понимаю, почему одно невалидное string::json-поле рушит весь запрос, а не возвращает просто null, как могло бы быть в Монге.Или вот, индексы. Я долго вкуривал, почему индекс не применяется к запросу find({}), пока не понял, что играет роль порядок полей в запросе и в индексе. Понял и теперь это не проблема, а просто trade-off.
И такого много. Возможно мои проекты не самые крупные в мире, зато ушибов и ссадин при работе в обеих БД было достаточно. В итоге получилось так, что для конструирования сложных фильров в postgres был найден sql-билдер mongo-sql для node с синтаксисом mongo-like json, вот такие повороты сюжета :)Вернуть бы времена, когда хватало couchdb и SPA-фронтенда.
FanatPHP
12.10.2022 19:25+2В конце получился отличный ответ на вопрос, для чего нужна монга.
Логи. Старые добрые логи, которые пишут потоком из разных сервисов и читают раз в полгода, если случился инцидент.
Файлы, кликхаус, локи
Данные, по которым вы никогда не будете искать дальше самого верхнего уровня. Максимум: взять какой-нибудь документ по его ID или другому полю (например, user_id, который вы до этого взяли из другой БД).
Непонятно, зачем в этом случае вообще городить отдельную датабазу. Берем документ по его ID из той же базы, где лежит и всё остальное.
Данные, которые вы будете писать большую часть времени разом и одним документом. Редко когда придется менять отдельные поля.
Это что-то из пальца высосанное. А, главное, те же грабли — вид сбоку: "сейчас мы воображаем, что отдельные поля менять не потребуется. А потом пойдем писать на Хабр о том, почему это оказалось недальновидным решением".
Кэш большого объема, для удобства разбитый по отдельным полям. Его быстро можно прочитать, легко инвалидировать. Он в виде структурированного документа может быть сразу использован вашим приложением.
Редис
Сессии (хотя вопрос скорости работы все еще остается). Аналогично кэшу. Создали документ, бросили его в MongoDB и взяли через какое-то время одному-двум полям. Без глубокого поиска. Максимально просто. MongoDB, кстати, удобно шардится и реплицируется — это прям реально ее преимущество.
Редис
Очереди. Точнее, можно использовать MongoDB как БД для хранения очередей данных для последующей обработки. Аналогично кэшу и сессиям MongoDB дает возможность хранить структурированный документ с данными очереди.
Осталось только понять, зачем использовать для очередей не специально предназначенные для этого инструменты, а Монгу.
php7
12.10.2022 21:46-1Статью лайкают те, кто 10 лет назад минусовал подобные комментарии под статьям-антонимами?
Xambey97
12.10.2022 22:43+2Еще одно подтверждение в тему того, что прежде чем использовать какой-то инструмент, нужно сначала понять для чего он был создан и в чем его особенности работы. Выбирать под задачи ТС NO-SQL базу было
как минимум странно, и тут не важно, что он не смог ее приготовить, ошибка была еще до начала использования. Вопрос к компетенции того, кто отвечал за архитектуру. И к тому же у них на сайте есть достаточно подробное описание того, для чего ее стоит использовать. Стоит читать не только стартовую маркетинговую страницуTsimur_S
13.10.2022 11:01+1Еще одно подтверждение в тему того, что прежде чем использовать какой-то инструмент, нужно сначала понять для чего он был создан и в чем его особенности работы.
Так и для чего этот инструмент все же использовать? Пихать разнородные данные в одну коллекцию а потом делать по ним какие-то сложные выборки? Ну выглядит крайне сомнительно.
Вон автор попытался найти достаточно конкретные юзкейсы но облажался лишь в том что для каждого пункта есть альтернатива специально заточенная под это дело.
А вот подход от создателей, по вашей ссылке в самом низу, вида «используйте Mongo для Payment processing» это лажа в квадрате. Типа если у меня платежный шлюз то тут Cassandra и Redis не подойдут, берем Mongo. Весьма авторитетно.
Пока что ничего в голову не идет кроме — а давайте напрямую портянки json сохранять с рестового ендпоинта на ноде, в стиле MEAN образца 2012 года. Ну да, в 2012 так можно было за день запилить стартап. А сейчас зачем?
Menog
12.10.2022 22:48мнда, судить по базе, не используя ее основную функцию агригейтов - это конечно очень сильно ))
Pasha_21
12.10.2022 22:48-1Кто-нибудь, подскажите толковый учебник по NoSql вообще и MongoDB в частности.
моя благодарность.
avrusanov
12.10.2022 22:48+1Если вам кажется что монга плоха - попробуйте кассандру :) - и вы поймете что монга - не так уж и плоха :)
antonvn
13.10.2022 01:29А меня монга 10 лет устраивает всем, кроме кластеризации. Она отлично работает, когда у вас облака, много нод и хорошая сетевая связность. Но я делаю автономное приложение, которое должно в отказоустойчивом режиме работать на двух железных серваках рядом. И если между ними я могу использовать ucarp, haproxy и тп для выбора мастера, то кластер монги в принципе не работает, если отключен один из пары хостов. Аналогичная проблема с распределенным приложением, где узлы могут оказаться изолированными от сети на время.
BugM
13.10.2022 02:21+1Но ведь Монга не работает на паре хостов когда один из них отказал. Это та БД которой надо 3 хоста минимум.
antonvn
13.10.2022 07:49+1Вот именно. В мире полно сценариев, когда железки для отказоустойчивости ставят в пары: роутеры, фаерволлы, контроллеры какие-нибудь. И тут я прихожу со своим софтом, и говорю «дайте мне три сервера, потому что у меня монга и у неё арбитр».
Очень недостает режима, когда черт с ним с кворумом, не надо понижать свою роль праймари до секондари, когда сосед пропал. Пускай приложение получает сервис (в ридонли, или в полном режиме с последующей синхронизацией дельты межды нодами по настраиваемому мной алгоритму), чем «ой у меня лапки» и отказ работать.
Если кто сталкивался и знает, как выкрутиться малой кровью, пишите в ЛС.
MaksMur
13.10.2022 15:17+1В Монге очень плохой оптимизатор, при сложном логическом запросе с И и ИЛИ он использует не тот индекс, если указать его хинтом то он его использует, но подставляет не все поля. Даже с учётом правильного порядка полей в индексе, и одинаково начала у составных индексов, что-то идёт не так :) хотя бороться можно.
После Lookup индексы не работают, то есть если тебе нужна пост сортировка или фильтр, монга будет грузить в память коллекции а не индексы. Если таких запросов много то...
Если есть один массив в коллекции, то жить впринципе можно, допустим с проверкой ACL, но в $text поиск нельзя добавить массив для фильтрации индекс. Так как текстовое поле представляет собой индекс, и место массива в текстовом индексе уже занято по дефолту, а два массива в индексе нельзя.
Ну а если в коллекции нужно делать фильтр по двум массивам, вот тут начинается самое главное :)
Шардинг тоже имеет тучу нюансов к lookup и индексам.
Хорошие моменты тоже есть, разработки на с#, мапинг, дескриминатор, это всё круто и удобно, codeFirst, просто класс, в базу даже не нужно заглядывать создавать какие-то там типы, строго типизировать коллекции, но если нужен поиск и фильтрация что-то тяжелее чем по id_, filed1 && filed2, и есть массив вот тут приходит крах производительности.
JordanCpp
14.10.2022 00:18Codefitst , mapping, orm это все конечно красиво, но до первого сложного запроса. Или проблем с производительностью. Все равно надо будет лезть в базу, мониторить профайлером запросы, создавать индексы и опять профайлить. И ещё потратить много времени, что бы заставить orm генерировать приемлемый запрос sql. И вся красота рушится как карточный домик. Да сурово, хотелось избежать сложности, получить лёгкий перенос между бд, писать на орм и не учить эти ваши старые и сложные sql. Хотелось чик чик и в продакшн. Но даже сервер с Овер ядер не выдержал тяпа ляпа. Что же делать? Пройти тернистый путь, найти горячие места, добавить вручную индексы, оптимизировать запросы на чистом sql. Ещё возможно переделать архитектуру, подумать как уложить данные так, что бы база данных обрабатывала максимально эффективно. Да реальная жизнь намного суровее.
MaksMur
14.10.2022 05:04Тюнинг запросов это естественно нужно, opsManager не плохо помогает мониторить. Но проблема в том что упираешься в сопротивление монги, ну не можешь ты решить проблему, даже изменив сам запрос. Ну вот хоть тресни. Приходится менять коллекции... А это импакт по производительности и иногда очень затратно по времени. Главный бич монги это поддержка индексов и массивов.
писать на орм и не учить эти ваши старые сложны sql
SQL в разы легче, больше инструментов, оптимизаторы которые десятилетия допиливыли. Простой синтекс, поддержка индексов везде. Но в sql есть проблема, это описание структуры данных, когда ты не знаешь что у тебя будет ещё добавлено в систему, какие будут требования. У сильно изменяемой системы. Вот тут решение nosql...
Devoter
13.10.2022 23:24Собственно, про выбор инструмента под задачу уже много раз в комментариях сказали, я же хотел бы отменить один, как мне кажется, важный факт: база относительно легко перенеслась на РСУБД. Отсюда можно сделать предположение, что и построена она была исходя из принципов построения реляционных баз данных, коей MongoDB не является. Для нее нужно совершенно иначе нужно проектировать структуру базы, и тот случай, когда одна сущность у вас разбита на несколько документов и/или коллекций - вызывает большие вопросы. В общем, Mongo - это не про взял и начал писать, сперва неплохо хотя бы базовый курс по ней пройти, раньше бесплатный был, теперь - не знаю. С добавлением транзакций спектр применения значительно расширился, однако, сейчас, с учётом наличия поддержки массивов и JSON-полей с возможностью поиска по ним, Postgres для проекта с неизвестными конечными целями и перспективами роста можно считать наиболее привлекательной СУБД. Мое субъективное мнение.
MaksMur
14.10.2022 05:09с учётом наличия поддержки массивов и JSON-полей с возможностью поиска по ним, Postgres для проекта с неизвестными конечными целями
Есть опыт перехода на постгри от монги? В чем там проблем больше/меньше? Фильтр массивов, join, индексы на все это дело? Драйверы на c# нормально поддерживают мапинг json?
dabrahabra
14.10.2022 01:01.NET в сложном проекте используем MongoDb, никаких проблем, единственное хотелось бы чуть больше свободы в модификации Mongo.Driver
js605451
Как вы там в 2012-ом?
holyx
Тут лучше чем в 2022м)
dopusteam
У вас там всего одна проблема - конец света)
shasoftX
Меня пугает что никто не пишут из 2032 :)
JordanCpp
Есть нюанс:)
rrrav
Ферми это давно знал
nin-jin
Здравствуйте, спокойно, пишу вам из 2032, тут все уже перешли на распределённую крипто базу данных с конвергентной мультимастер синхронизацией написанную на $mol.
shasoftX
Смотрел я в сторону $mol, хотел попробовать прикрутить к своему проекту. Но оказалось что в $mol нет красивых компонентов :(
А так как я сам в CSS ноль, то стал смотреть в сторону quasar, где в наборе уже идут красивые компонентники
nin-jin
Дизайн базовых компонент там намеренно аскетичный, чтобы его легче было кастомизировать под личные представления о прекрасном. Но какое это имеет отношение к базам данных?
shasoftX
В том то и дело, что представление о прекрасном у меня есть, а вот навыков в CSS нет :)
Поэтому пришлось искать там, где уже все украшательства прикручены.
А при чем тут вообще базы данных?
nin-jin
Я бы вам рекомендовал всё же освоить CSS, а не заучивать птичий язык всяких квазаров. Он не сложный.
Вам не кажется странным обсуждать дизайн компонент под статьёй о базах данных?
mayorovp
Как язык CSS и правда несложный. Но это совершенно не означает, что делать дизайн сайта с нуля используя CSS легко.
nin-jin
Не просто легко, а элементарно, справится любой войтивайти за 3 копейки.
shasoftX
В том то и дело, что в quasar стандартный vue с js. Никакой css не нужен.
p.s.я и так что не начну писать, так целую кучу инструментов понапишу, а до того, что изначально планировал так и не добираюсь. :)
Acuna
А есть какое-то решение для 2022?
BugM
PostgreSql или MySql по вкусу (если не можете выбрать киньте монетку. я серьезно). Как БД по умолчанию для любого проекта где вообще непонятно что надо и никаких разумных требований сформулировать не получается. А вот данные хранить надо уже сейчас.
В 2012 те кто поопытнее говорили тоже самое, но кто их в стартапах слушал?
Acuna
А, в смысле в 2022 MongoDB уже никто не пользует? Ясно, она как-то мимо меня прошла (не было необходимости), только через 10 лет узнал что и нужно было)
BugM
Используют. Но только когда понимают зачем она им и почему именно она, а не как вариант по умолчанию.
vendelieu
не читать новости :/
пока только такое