Чужой опыт касается решения чужих задач. Нету такого понятия, как некий абстрактный высоконагруженный проект в вакууме. Все зависит от самого проекта, от характера запросов, от характера нагрузок, от связи между данными, соотношения между количеством обращений на чтение и запись данных, сложности запросов, и многого-многого другого.
По поводу «а». Все базы данных и так имеют свой кэш в оперативной памяти. Чтение с жесткого диска это наихудший случай, но и в кэш вы тоже все данные не сможете поместить. Вообще не имеет смысла дублировать кэш базы данных, так вы только усложняете систему, увеличиваете латентность и нерационально расходуете память.
На счет «б», учтите, что ваше «отправить данные в хранилище» не редко оказывается даже более узким местом, чем из считывание. Считывание данных как правило всегда можно так или иначе масштабировать, той же примитивной мастер-слейв репликацией, а вот с записью все обстоит гораздо сложнее.
а) Кто вам сказал, что хранилище данных должно быть непременно медленным?
б) Кто вам сказал, что данные обязательно класть сперва в хранилище, потом из него в кэш?
Вы плаваете в плену каких-то непонятных представлений, вместо того, чтобы сесть и продумать логику работы от начала до конца. Так вы сами заведомо выдумываете себе не существующие еще проблемы, и пытаетесь дать им решение.
Все объектные, и тем более, более примитивные key-value базы — прекрасно горизонтально масштабируются практически без потери своих возможностей. Это свойство просто фундаментальное и следует непосредственно из теории. И MongoDB не исключение, прямо на главной странице www.mongodb.org/ — одним из ключевых свойств выделено:
# Auto-Sharding »
Scale horizontally without compromising functionality.
и это не считая:
# Replication & High Availability »
Mirror across LANs and WANs for scale and peace of mind.
ну и само-собой partitioning вы можете использовать всегда, если он архитектурно вам доступен.
«Web-сервер, способный отдавать динамический контент»
А nginx не способен? Способен. Но тут вопрос даже в другом, зачем вам городить нагромождение веб-серверов, друг за другом, хотя именно http вам нужен только в самой конечной точке, непосредственно в браузер пользователя. Учтите, что сам http-протокол не является шибко эффективным и производительным, не стоит им злоупотреблять.
Базы данных на графах существуют. И я допускаю возможность их применение в том числе и в веб, если это очень-очень обосновано и удобно. Но вы говорите о горизонтальном масштабировании, при этом называете базу данных со сложностью структуры еще большей, чем у реляционных баз данных. Реляционные базы данных то не очень хорошо масштабируются, чаще всего утрачивая при этом ряд важных свойств, а как можно масштабировать еще более сложный граф, я так прямо с ходу даже и придумать не могу. Вероятно, что это и вовсе не возможно, но тут надо читать.
Про MongoDB не упомянул легкий шардинг, GridFS, replica sets где, в случае выхода из строя мастера, слейв автоматически подхватывает на себя его функции, тем самым и обеспечивается multi-server durability. У CouchDB также есть возможность шардинга (Lounge), причем в виде отдельного модуля к nginx + twisted демона. Файлы в CouchDB можно хранить в виде простых файлов в файловой системе, но прикрепленных к объектам.
Наверняка еще что-то интересное и занимательное по каждой из этих трех баз упустил, в общем, читайте документацию, или хотя бы части Introduction, What it is и FAQ, на сайтах каждого проекта.
Я не очень понимаю, что значит «база для хранения связей», я конечно догадываюсь, но такое разделение опять же довольно прикладного уровня, вы должны четко понимать, зачем оно вам нужно.
Любая база хранит связи. Даже примитивные key-value, как минимум хранят связь между ключом и его значением. Объектные базы хранят связи в виде объектов, а иногда еще дополнительно в виде чего-то обобщенного (коллекции например в MongoDB, а в RIAK вообще есть такие штуки, как ссылки). Если вы хотите хранить связи еще как-то отдельно, то для этого нужно иметь очень веский повод, как минимум в этом случае вам нужно дополнительно какой-то механизм городить, который обеспечивал бы непротиворечивость между этакой «базой связей» и «базой объектов».
NoSQL решения есть очень разные. Пожалуй тут всегда можно подобрать конкретное под конкретные требования. Я более менее знаком, только с MongoDB, CouchDB и RIAK. Каждое из этих решений стабильно, имеет будущее и на сайтах этих проектов вы можете найти документацию, а также увидеть кто их уже применяет на практике. Не надо боятся NoSQL, но и не надо забывать, что это звучит как Not only SQL. Если вам безумно нужны традиционные транзакции, то скорее всего, стоит все же посмотреть в сторону той же PostgresQL.
Если очень кратко, по особенностям то:
— все трое получают и возвращают в итоге JSON
MongoDB:
— динамические запросы, очень похоже на SQL-подход
— отсутствуют какие-либо гарантии single-server durability, сразу готовьтесь ставить сервера пачками
— очень быстро развивается, релиз-цикл чуть ли не 3 месяца, постоянно появляются новые вкусные фишки… те кто пробовали ее хотя бы полгода назад — уже не в теме =)
— можно гибко настраивать поведение при изменение\добавлении данных в каждом запросе: начиная от когда функция возвращает управление в процесс сразу, как только запишет данные в сокет, не дожидаясь, обработки, в плоть до ожидания пока данные попадут на n серверов и fsync случится на каждом… между этими двумя вариантами, куча промежуточных
— учтите что количество индексов и коллекций на базу ограничено определенным, хотя и довольно большим числом
CouchDB:
— совершенно уникальный подход к запросам: вы определяете все запросы заранее во view map/reduce-функциях и все объекты сразу пропускаются через них, а результат сохраняется в b-tree. Когда добавляются новые данные, то только эти данные обрабатываются. Таким образом все запросы на чтение это всегда очень быстрый поиск по уже готовым b-tree индексам, а все запросы на изменение\добавление\удаление — простое обновление этих индексов
— сплошной ACID, вплоть до crash-only, т.е. убийство процесса — является естественным (!) способом остановки, данные при этом не пострадают, таким образом у вас максимальная single-server durability
— уникальная мастер-мастер репликация с контролем ревизий, можно строить какие угодно схемы и реплицировать хоть в датацентр на другом конце света, хоть на мобильный телефон
— везде сплошной REST и доступ по простому http… можете общаться с базой прямо напрямую из джава-скрипта на клиенте, и не только, есть гибкий механизм разграничения правд доступа и валидации данных, специальные validation-функции
— design-документы, кроме того, позволяют определять шаблоны, которые будут отдавать полноценный html или rss, вообще чего вам вздумается. CouchDB — вообще одна большая мега-стейтфул машина и некий application-сервер, многие сайты и RIA можно было бы делать только на ее основе, без какого-либо middleend-а
RIAK:
— как и CouchDB сплошной RESTful HTTP
— уникальная система ссылок, формально это key-value база, но не простые эти key-value… можно объединять с помощью них, получая возможность обращения с ними, как с некими графо-подобными структурами
— мега-гибко настраиваемая система масштабирования, вы запускаете большое число серверов и данные размазываются между ними, таким способом, чтобы одновременно обеспечить высокую скорость и надежность: вы сами минимальное количество реплик необходимое для записи, и для считывания, при этом наиболее востребованные данные будут находится на большем количестве серверов и считываться быстрее, единая точка отказа — отсутствует, сервер может умереть, испортиться — ничего страшного
— система, опять же, рассчитана и в первую очередь хорошо себя проявит на большом количестве серверов, ввиду этого никто даже не побеспокоился о контроле доступа к каждой отдельной машине, т. е. весь этот парк серверов на RIAK предполагается ставить в отдельную изолированную подсеть
— при использовании платной (энтерпрайз) подписки вам также доступен прямо из коробки механизм репликации на несколько датацентров
Все это мои личные, очень краткие, исключительно впечатления от этих систем. За более подробной информацией обращайтесь, в первую очередь, к документации на сайте разработчиков. Как видите, все эти три несмотря на сходства в некоторых вещах, в то же время имеют совершенно кардинальные различия. Каждая будет наиболее предпочтительна при решении какого-то вполне определенного круга задач. То же касается, кстати, и если рядом поставить обычную RDBMS… у них тоже есть свой круг задач. Так сложилось, что с помощью RDBMS многие привыкли решать любые задачи, даже когда с виду это довольно часто выглядит как завинчивание шурупа — молотком, а иногда, как забивание гвоздей микроскопом.
Производительность и прочий satisfaction гарантированы только при правильно подборе и использовании того или иного инструмента.
Я так упрощенно все описал, а в целом, архитектура проекта — это сложная комплексная задача, требующая соответствующего подхода. Нужно уметь выявлять потенциально узкие места, как производительности, так и по возможностям наращивания функционала, трудоемкости поддержки и реализации. И как несколько раз повторил, любая архитектура — это один большой компромисс, между кучей факторов. Если вдруг, в чем-то компромисса вы не видите, то вы стоите на опасном пути, вы просто чего-то не учли.
Я и не спрашиваю решения конкретно для «реляционных БД». Если кто-нибудь скажет, о, слушай, да с твоей задачей прекрасно и быстро справится такая-то БД, но она на графах, смотри… <cut> — вот как оно с этим справляется. Я скажу — о, круто, спасибо, попробую! На RDBMS свет клином не сошелся.
Смотрите, b-tree из 100000 наборов «значение_поля — id_товара» с большой вероятностью вполне неплохо будет помещаться целиком в ОЗУ. И выборка среза по нему это очень быстрая операция. Результаты выборок по одному из полей, довольно часто будут помещаться даже целиком в процессорный кэш, после поочередного нахождения пересечений между подобными выборками, количество оставшихся id будет стремительно уменьшаться. Все эти операции весьма быстрые, и не представляют существенных проблем для современных серверов, да и легко параллелятся, не вижу что бы мешало делать их тысячами в секунду на десятке, может быть нескольку десятков среднестатистических машинок.
Но не писать же это все самому? Наверняка некоторые существующие решения именно так и работают, только я этого не знаю.
Индекс нужен, по каждому добавляемому полю, которое может участвовать в запросе. Возможность материализации заранее представляется весьма туманно, при произвольном наборе параметров на выборку, возможных вариантов запроса может быть очень много.
Алгоритмически все действо я представляю себе так. У нас есть b-tree «значение — id-товара» на каждое поле доступное для выборки (причем их можно разнести по разным серверам и вообще неплохо масштабировать), из них легко брать срезы. Поступает запрос, по каждому параметру присутствующему в запросе берется срез, так, что в итоге у нас получается набор сортированных массивов содержащих id-товаров. Остается найти между ними пересечение, начать можно с самого маленького массива, не такая уж затратная операция. В итоге, получаем набор id-товаров, по котором уже надо выбрать набор соответствующих объектов из базы.
В общем, с помощью чего это можно провернуть быстро, масштабируемый и чтобы не писать своих велосипедов?
Хм, думаете EAV в RDBMS наилучший способ решения данной задачи? Мне вообще всегда казалось, что EAV это костыль для запихивания объектов в реляционную модель, когда нет других вариантов, или я ошибаюсь? и это весьма производительное решение?
Я вообще не считаю, что «каждый товар может содержать произвольный набор данных» как-то хорошо вписывается в RDBMS. Я нигде не писал, что речь идет об SQL.
«noSQL базы с map/reduce»
Какую? CouchDB — отсутствуют динамические запросы, map/reduce нужно продумывать заранее, и все запросы характерны выборкам по b-tree, т. е. максимум срез от сих до сих, а пересечения уже самим искать. Либо, если только Lucene прикручивать, либо я что-то про нее не знаю еще.
MongoDB — ограничения на количества индексов и коллекций, как-то пугают. А индексов нужно судя по всему великое множество.
RIAK — пока не очень понимаю, как оно применимо для моей задачи.
Задача похожая на яндекс маркет. Будет база товаров (несколько сотен тысяч и расти), каждый товар может содержать произвольный набор данных, нужно делать выборку на запросы с произвольным набором параметров. Таких запросов будет сыпаться по нескольку тысяч в секунду. Главное выборка, запросов на изменение будет значительно меньше и их время выполнения не очень критично.
По поводу «а». Все базы данных и так имеют свой кэш в оперативной памяти. Чтение с жесткого диска это наихудший случай, но и в кэш вы тоже все данные не сможете поместить. Вообще не имеет смысла дублировать кэш базы данных, так вы только усложняете систему, увеличиваете латентность и нерационально расходуете память.
На счет «б», учтите, что ваше «отправить данные в хранилище» не редко оказывается даже более узким местом, чем из считывание. Считывание данных как правило всегда можно так или иначе масштабировать, той же примитивной мастер-слейв репликацией, а вот с записью все обстоит гораздо сложнее.