Как стать автором
Обновить

Комментарии 93

Вместо того, что бы выгребать актуальные данные, Вы выгребаете «какие есть», а потом по «каким есть» выгребаете актуальные и проверяете на целостность? По сути пляшете от «кэша».
Крайне любопытно будет почитать про поиск по параметрам вида «товар с присутствием красного и зеленого цветов, но отсутствием синего и желтого, остальные по фиг», а так же про вывод списков с сортировкой по любому из непрямых свойств товара (например по цветам в алфавитном порядке).
MongoDB не любит сортировать более чем 1000 записей без индекса, так что для больших категорий сортировку по произвольным параметрам мы запретили. Но для фильтрации секрет я раскрою:
Категория с 2445 товарами
Вообще я бы для фильтрации (как и для поиска) использовал люсен под эластикой, интересно как вы решили
НЛО прилетело и опубликовало эту надпись здесь
>Как организовать полнотекстовый поиск с релевантностью в mongodb?

Я для себя ответил таким образом: «Взять сфинкс». :)
вот кстати хорошее решение. сразу убивает несколько зайцев
Ага, и не только в mongodb.
Еще можно ElastcSearch. К нему есть плагин под MongoDB. Профит по сравнению со сфинксом в real-time индексации
у сфинска тоже есть RT-индексы, но работа через mysql-интерфейс онли.
К сожалению у сфинкса для многих есть несколько минусов: у него плохая интеграция с пхп (то апи, что есть — ужасно) и его, судя по хабру, либо сложно готовить (либо он всегда выдает такой нерелевантный поиск). Люсен ему проигрывает в итоге только в прожорливости
Для RT-индексов есть доступ через MySQL-синтакс, я класс-прокладку на коленке написал и все работает (pdo на данный момент нельзя толком использовать из-за несовместимости).

Мне в принципе большая релевантность не нужна, поэтому по-второму пункту ничего сказать не могу.
Ну использовать sql, как говорят разработчики, стоит только для разработки, а само приложение все-таки должно работать через нормальное апи, но в пхп все это апи реализовано одним классом с минимальными возможностями
Через API с RT-индексами работать нельзя как я понимаю, вообще. Они только через MySQL.
> Ну использовать sql, как говорят разработчики, стоит только для разработки, а само приложение все-таки должно работать через нормальное апи,

???
Хм, кажется я перепутал сфинкс с чем то:) то ли у эластики тоже есть sql-интерфейс, толи у кауч(или и вовсе тарантул), вот там только для «побаловаться» sql можно использовать
Ага, понятно, "… а потом уже и анекдотов насочиняли" ;)
Довольно обидно, что Хабр как рупор Российской ИТ индустрии так компрометирует Sphinx. На самом деле Sphinx способен искать гораздо качественнее Solr/ES/etc, особенно это касается русского языка. Вдобавок у к отсутствию прожорливости Sphinx гораздо быстрее индексирует документы, а бинарный протокол сильно экономит на пересвлке данных. Вот с RealTime у него действительно беда :( Приходится держать до 3-х дельта-индексов, при этом мёрджинг оказывается накладнее полного переиндексирования.

Те же программисты, что пишут Хабр реализовали поиск на сайте darudar.org гораздо качественнее. Там правда в 10-ки меньше нагрузка, но зато в 10-ки больше данных. Видимо просто сейчас на Хабре много более приоритетных задач, чем оптимизация поиска.
Кстати, а что с RT?

Пока что вроде все ок, но в хотелось бы быть готовым к проблемам :)
Я вот тоже давно задумываюсь о связке NoSQL + Поисковый движок (Sphinx/Solr/etc.). По сути RDBMS — это тот же key-value, плюс плохой (по сравнению с Sphinx/Solr) поисковый движок, плюс реляции с обеспечением целостности данных, плюс триггеры/хранимые процедуры, целесообразность использования которых в вэбе крайне сомнительна.

Целостность вполне можно обеспечить на стороне приложения. Sphinx/Solr решают все проблемы со сложными запросами. А вот что касается альтернатив реляциям, особенно M2M — вопрос для меня пока неисследованный. К примеру M2M связь пост → хаб довольно очевидна. Пост может находится в небольшом числе хабов, можно просто хранить их в документе поста как массив, выборку по хабу разрулит SE…

А если число связей велико с обоих сторон? А если у связи есть дополнительные данные (например пользователь → хаб, где есть роль, дата вступления и т.п.)?
Да, я конечно забыл о транзакциях. Это тоже больной вопрос…
Основной вывод по монго — жаль что я его не сформулировал в самой статье — что структура базы намного ближе к коду, с ней проще работать, чем sql. Хочешь реляции? Пожалуйста. хочешь словари — пожалуйста, хочешь массив с идексом — да без проблем.

Особенно удобно в связке с основнанной на json библиотекой knockout, которая обеспечивает взаимодействие с пользователем.
Как хранить в MongoDb дерево с произвольным уровнем вложенности, с возможностью прикрепления в качестве узла ссылки на другой узел, где каждый узел это объект со своим набором свойств, атрибутов и внешних связей? Дерево отдельно, объекты отдельно или как-то еще?
В каждом конкретном случае ответ свой — слишком мало информации.

Задаем себе вопрос — какие данные мне нужно будет читать/изменять атомарно? Ну и вот опираясь на это решаем, все хранить в документе, либо выносить полезную нагрузку в отдельную коллекцию.
Система документооборота, для сравнения можно взять файловую структуру, но более продвинутую: есть множество папок и файлов с произвольной вложенностью. Файлы имеют произвольный набор атрибутов, разный тип, по разному отображаются и редактируются. Папки больше походят на умные контейнеры, могут ограничивать типы входящих файлов, позволяют выполнять групповые операции над внутренними объектами, обеспечивают необходимое представление содержимого. Можно сделать ссылку как на папку, так и на файл. Изменения происходят в 2 этапа: добавляем новый узел (папку/файл) в дерево, изменяем свойства/атрибуты выбранного узла. Файлы могут быть также связаны с другими файлами (через атрибуты), например: описание файла, проекции файла (преобразование в pdf, jpg и т.д.). И дерево большое (более 100 000 узлов) и атрибутов много (более 10 000 000) То есть явно требуются реляционные связи, как это решается в MongoDB?
Как только появляется «много произвольных атрибутов» то монго резко становится намного удобнее MS SQL.

1. Сходу, напрашивается сохранять всех предков в монго.

2. Насколько часто происходит перемещение объектов между папками? По сути — это единственная проблема, которую я сейчас вижу. Но мне кажется для ms sql это тоже была бы проблема.

3. 100 тысяч узлов для монго это не много. Также, за счет того что можно использовать композицию — несколько объектов в одну запись, вероятно количество объектов может быть меньшем чем в реляционном варианте.

Если хотите — могу проконсультировать в индивидуальном порядке.
id, parent_id, data — структура мало чем отличается от классики
И как выбрать всех предков данного узла (а-ля breadcrumbs)? А как удалить всех детей данного узла вне зависимости от уровня (при удалении самого узла)? Классическая структура подходит только для простейших применений.
Materialized path?
Я задавал этот вопрос на конференции девелоперу Монги. Если вложенность планируется большая и вообще никак не ограничивается, то все-таки лучше для этого посмотреть в сторону графоориентированных БД: neo4j например.
У всего есть предел, в том числе в том, сколько технологий можно освоить профессионально одновременно.

Судя по сайту, neo4j не предоставляет api для .net, так что надо будет искать что-то еще [более сырое]. Вполне возможно, лучше уж забивать графы монгой.
neo4j не предоставляет api для .net

Там есть REST api.
>> За одно единственное обращение к базе данных — поиск по id категории и SeoFreindlyUrl, которое занимает 0,0012s (!) я получаю:

С этим понятно, вопрос сколько времени займет выборка:
— выборка по тегу, платья
— красных платьев с ценой более 200, но менее 570 (или какие там характеристики не уникальные)
Можно глянуть большую категорию с 2445 товарами

Тесты показали, что выборки по случайным свойствам товаров данной категории занимают порядка 0.2-0.4 секунды, что слишком долго для веб приложений. Для фильтров мы строим что-то похоже на индексы самостоятельно без средств монго, и укладываемся в 0.01.

Сортировать данные более чем в 1000 строк монго не любит — так что в больших категориях сортировки по полям запрещены.
Если уж сильно нужно, можно считать за 1й заход id + необходимую характеристику, отсортировать в C#, а потом уже считать порцию нужных данных по id
информация о платежах у вас тоже в Mongo?
Платежей пока нет.

Если вас беспокоит потеря данных — не верьте слухам о том, что монго не стабильна. Сама по себе она как камень. Единственное, чего она боится, это незапланированной перезагрузки питанием без корректного завершения процесса.
и еще забываем про теугольник CAP
Недавно был такой на тестовом сервере. Удаление mongod.lock и перезапуск с параметром --repair помог, потерь я не заметил.
Сама по себе она как камень.

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

Пока все разборы полетов на тему «у нас пропали картинки у колясок» заканчивались выявлением неправильной работы пользователей.
Ну, я даже скорее о транзакциях.

Но и насчет потери данных тоже не стоит забывать: в Mongo журналирование появилось в 1.7.5 (~1.5 года назад). В 32-битной версии по умолчанию вообще отключено. Без журнала её хоть сколько-то надежной считать просто нельзя.
В то же время, журнал невозможно настроить на полностью синхронную запись (по умолчанию — интервал 100мс). Конечно, в большинстве случаев это приемлемо, но уже компромисс.
32-битная версия имеет много ограничений, легче вообще её не рассматривать.

В то же время, журнал невозможно настроить на полностью синхронную запись (по умолчанию — интервал 100мс). Конечно, в большинстве случаев это приемлемо, но уже компромисс.


Всё наоборот. Для каждого запроса можно синхронизировать журнал на диск, базу данных на диск, минимальное количество реплик, минимально количество датацентров и т.д. Никаких компромиссов.
и каждая синхронизация — это тормоз…
бесплатный сыр… сами знаете где,
компромисс на компромисе
Да блин, там куча вариантов. Можно отправить запрос не дожидаясь подтверждения — быстрее некуда. Можно дождаться подтверждения, что сервер принял запрос и записал в память не синхронизируя ни журнал, ни базу — запишется само через определенный интервал.

Дело в том, что можно регулировать durability очень гибко в широких пределах и для каждого запроса. Так чтобы одновременно и быстро и надежно физически не может быть.
у меня проект реализован в Монго, а вот платежи старый добрый Мускуль
Проверками целостности данных должна заниматься СУБД, а не клиентское приложение.
БД должна исправлять ошибки программиста, которые приводят к потере целостности?
Если мы работаем с EC, то предоставление приложению возможности разрешения конфликтов (в соответствии с предметной областью или моделью данных) является единственным правильным подходом.
Как только понадобится делать апдейты нескольких документов одновременно или хранить хоть сколь-нибудь объемные связанные данные, mongodb будет «жать».
«Работать с нецелостными данными в приложении» только звучит просто; на деле же оказывается, что это приносит немало проблем.
Не совсем ясны ваши опасения.
1) Всего размер базы у нас 18 гиг и маленькой ее никак не назовешь. Добиться аналогичной скорости работы проекта на ms sql было бы намного сложнее
2) Ну и зачем-то ж нам дан мозг, было интересно его напрячь для не тривиальных решений.
Не совсем понятно, почему вы полностью от SQL баз отказались в своем проекте. Мы сейчас используем SQL базу в интернет-магазине и собираемся mongo прикрутить для ряда задач. Очевидно, что хранить характеристики со значениями, отдельно для каждого товара в ней значительно удобнее, равно как и выборки товаров по значениям характеристик делать. Но древовидные структуры типа каталога товаров и критически важные данные — заказы, пользователи, цены, я бы оставил в SQL.
Мы наоборот, используем ms sql для ряда исключительных задач.

Основной аргумент против двойной базы — сложность восстановление информации. Бакапы не могут быть сделаны в один и тот же момент, и после восстановления системы базы не будут совпадать.

Я тоже переживал за категории в Монго — оказалось очень удобно, к примеру вот такую страницу в случае использования sql придется, наверно, кэшировать. Я не написал, но этот проект не использует OutputCaching вообще — все страницы, запросов бывает до 15 тысяч в час, рендерятся на лету.

Мы же в каждой категории сохраняем id лучших 10 товара, которые ее представляют, и потом вычитываем товары, которые ее показывают.

Запрос 1 — найти категорию
запрос 2 — найти 44 товара

Не помню сколько в сумме, где-то на уровне 1/20 секунды.

Один из прошлых проектов с деревом в 100000 узлов на ms sql безбожно тормозил.
НЛО прилетело и опубликовало эту надпись здесь
Нет, изображения храняться как файлы, в базе хранится тоьлко путь к файлу. Основной размер дают:
— описания товаров (статья)
— массив ключевых слов после стэмминга для поиска
— свойства
А индексы сколько занимают?
0.5 GB
Индексов довольно много:
— id
— категории
— внешнему id (товары синхронизируются с 1C)
— цене
— ключевым словам для поиска
— SeoFriendlyUrl
— приоритет показа
— название
0.5Гб индексов — это на 4Гб или на 18Гб данных.
Это на таблицу товаров, 4GB
>Как организовать полнотекстовый поиск с релевантностью в mongodb?
через Сфинкс, а как же еще?
>Есть ли жизнь без group by?
несомненно!
А как вы обошли проблему отсутвия блокировок/транзакций?
Такой же вопрос. Отсутствие транзакций делает монгу очень плохим решением для некоторых приложений. Да, можно вручную как-то их напрограммировать, но в резульате из этих костылей ничего хорошего не выйдет.
Причём непонятно, почему они не сделают транзакции (пусть и жутко медленные, делающие лок всей БД). Пусть лучше будет официальный путь, чем куча самописных велосипедов.
Это вечный вопрос. Вообще, в работе с NoSQL бывает очень не хватает именно транзакций. Все круто, но нет транзакций, а нужно гарантированно обновить несколько документов. Этим пожертвовали в угоду производительности.

Многие NoSQL-решения (включая Mongo) гарантируют Eventual Consistency (целостность «в конечном итоге»), что требует особого подхода к архитектуре проекта, но иногда просто не хватает обычных классических транзакций, когда нужно «чтоб прям здесь, сейчас и наверняка». Один из камней преткновения NoSQL, заставляющих идти на чрезмерные хитрости.

Для мира .NET есть решения NoSQL с транзакциями вроде RavenDB (очень хорошее решение, к слову), но MongoDB побыстрее будет и не привязано к Windows и .NET, что является преимуществом при масштабировании.
Причём решение достаточно странное, у меня есть задачи, в которых транзакции жизненно необходимы, но использоваться они будут достаточно редко, чтобы это не сказывалось на производительности, даже в случае блокировки всей БД.
Давайте разберем пример операции, где дествительно, важно чтобы изменения нескольких объектов были сохранены именно одновременно, может что и придумаем :)
Легко:
Два человека обмениваются деньгами
user1.balance+=amount user2.balance-=amount user1.save user2.save
Извиняюсь, тег code почему-то не учёл переносы строк :(

P.S. по сабжу — вообще любые операции с деньгами обычно требуют подобное
При горизонтальном масштабировании транзакции перестают нормально работать, так же как и джойны. Поэтому от них лучше сразу отказаться.

user1.balance+=amount user2.balance-=amount user1.save user2.save


Тут как раз описан этот пример.
Ключевая таблица — Transactions — туда пишем 1 запись с двумя (в монго можно и больше, главное чтобы сумма была 0) объектами:
{ Changes: [ { User: «User1», Amount: +X}, {User: «User2», Amount: +X } ] }

Когда нам нужно достоверно знать количество денег на счету — находим все changes по user = myid и считаем сумму.

Осталось только придумать (на вкус и цвет) стратэгию кэширования остатков, чтобы не делать полный пересчет каждый раз.
Раз в сутки пересчитывать остатки более чем достаточно.
И хранить по датам.
Пересчет делается только за текущий день.

Это же, кстати, помогает нивелировать ошибки округления до почти ничтожных.
несомненно за скорость надо платить,
но как только кончается оперативка — начинаются тормоза ( на set, относительно мускуля конечно на insert)
очень уж много данные занимают место (у меня больше 60Gb на 0.5М Пользователей)
очень долго делаются бэкапы… делать бэкап перед выкладкой — это смертеподобно :)
Тормозов от количества оперативки не заметил. Сейчас mongod занимает в памяти 10G, при основной базе 14 + staging где-то столько же.

А вот скорость выполнения как бакапов, так и восстановления базы дейстивтельно не радует и поднимает новые вопросы.
Монго всегда будет занимать всю свободную память, потому что это просто файловый кэш ОС. Главное чтобы индексы вмещались в память.

По поводу бекапов, мне кажется, что вы используете mongodump/mongorestore. Они подходят для работы с отдельными коллекциями, и, например, индексы не сохраняют. Восстановление из правильного бекапа — это просто копирование директории data. Бекап должен выглядеть так: заходим на вторичную реплику, переводим процесс в режим только для чтения, копируем файлы данных, снимаем блокировку. Тут главное чтобы не кончился oplog. Если делать снапшот файловой системы, то разблокировать можно сразу же.
а что делать — если индексов куча и все не вмещаются???
как на счет БД в 60 Гб?
Так и сколько ж там индексов?
Выделить сервер с 8GB (лучше, конечно 16GB) памяти не такая космическая задача на сегодня.
я разработчик, а не инвестор
а инвестор говорит, что проект пока денег не заработал
вот и удивляемся, почему тормозит на 1 или 2 Gb (виртуалка)
виртуалка — это как временное решение,
но нет ни чего более постоянного чем временное.
Тогда вам один путь — искать возможность уменьшить объем данных. Когда проект станет зарабатывать данных, скорее всего, станет еще больше.

У меня тоже был один мини стартап, который я свернул после беты, так как понял, что стоимость хранения информации первышает возможности на этом заработать.

Ну и на всякий случай
— убедиться, что количество запросов при каждой загрузке страницы o(1)
— Можно посмотреть, здесь описываются детальная оптимизация запросов: derickrethans.nl/indexing-free-tags.html

>убедиться, что количество запросов при каждой загрузке страницы o(1)
кол-во запросов: 1 get & 1 set на каждой стр, за исключением одной: стр 2 get & 1 set
> у меня запросы в основном по первичному индекс: получить данные игрока, есть запросы (меньшая часть) по вторичному индексу: выбор списка друзей.
Более подробно про архитектуру у меня написана статья в моем болге (сервер пока не работает, ссылку дам позже)
Например, столько
mysql> SHOW TABLE STATUS like 'table_ra%';
+------------------+--------+---------+------------+-----------+----------------+-------------+-----------------+--------------+
| Name | Engine | Version | Row_format | Rows | Avg_row_length | Data_length | Max_data_length | Index_length |
+------------------+--------+---------+------------+-----------+----------------+-------------+-----------------+--------------+
| table_raw | InnoDB | 10 | Compact | 257569966 | 146 | 37714132992 | 0 | 61952704512 |
Так это ж mysql, статья то про монго, которую, как минимум, легко раскидать по разным серверам.

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

Но такое есть и живет.

И я не сильно уверен, что горизонтальный шардинг и репликация в монго лучше чем в мускуле. Вот когда доведется попробовать, тогда я смогу что-то сказать более однозначно.
а что делать — если индексов куча и все не вмещаются???


Ну конечно sharding. Это же основная идея почти всех NoSQL хранилищ — легкое горизонтальное масштабирования. У монго оно еще и автоматическое.

как на счет БД в 60 Гб?

Например, тут размер базы 10TB.
>У монго оно еще и автоматическое.
на HiLoad 2011 в докладе «Почему не надо использовать MongoDB» говорили, что оно работает не совсем так, как ожидается.
Спасибо за идею, попробуем
Такое пафосное начало про то что выборка даже по primary key это мол время, а потом и страницы не проходит как идет FindOneById(id), которые по сути работы отличается от выборки по первичному ключу примерно как Санта Клаус отличается от Дед Мороза.

Дальше читать уже как-то и не так захватывающе.
В обычной не реляционной базе это была бы пачка запросов, а не 1:
Запросить товар
запросить картинки
запросить видео
запросить характеристики

Особенно резко все ухудшается при необходимости показать список объектов, у каждого их которых есть подобъекты — lazy loading быстро портит сокрость выполнения
Монго здесь по сути используется только как кэш. Можно по желанию использовать для этого всё что угодно, хоть даже memcached, который умеет сохранять данные на диск.
Блин, как раз в обычной реляционной.

PS: Тот абзац чуть поправил.
а вы не задумывались над возможностью использовать хранимые процедуры в качестве транзакций?
В Монго есть аналог: встраиваемые функции
db.system.js.save( { _id: «foo», value: function( x, y ){ return x + y; } } );

я тут погуглил cookbook.mongodb.org/patterns/perform-two-phase-commits/
Задумывался, но для нас вопрос транзакций вообще не актуально.

Функция это хорошо, но ведь админ по ошибке может вырубить свет как раз в момент ее выполнения. Я бы скорее завел таблицу операций, писал бы туда всю необходимую информацию в одну запись.
Асинхронный процесс выгребает операции и обновляет связанные объекты — habrahabr.ru/post/149047/#comment_5040625
ссылка классная, так тоже можно работать.
db.system.js.save( { _id: «foo», value: function( x, y ){ return x + y; } } );

Сама хранимая процедура ничего не даст. Если транзакция как в этом примере, в пределах одного документа, то монго и так это поддерживает. Есть куча интересных функций для атомарного изменения документа. Но уже в пределах коллекции гарантий нет, так как она может быть распределена на много серверов.
согл, если что-то распределено, то…
Не думаю, что небольшой интернет магазин займет несколько серверов данных
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории