Райское наслаждение от MongoDB

На выходных сортировал свою библиотечку и надолго задержался на книге «MongoDB в действии». Кидать ее в раздел SQL было бы неправильно, так как Mongo — это как раз NoSQL, а создавать новый раздел из-за одной книги не хотелось.

Я открыл книгу и стал листать. В конце предисловия была фраза, которая меня сначала насмешила, а потом заинтриговала:

«Многие разработчики признавались, что при работе с ней [с Mongo] испытывают ощущения чуда и даже счастья».

Я подумал, что и мне тоже чудо не помешает и уж тем более дополнительная порция счастья. И начал изучать, что же такое Mongo.

Что же такое Mongo?

Оказывается, это СУБД, которая в отличии от MySQL/SQLite/PostgreSQL и прочих SQL ориентирована на работу не с табличным представлением данных, а на работу с документами. Чтобы было понятнее, давайте сначала рассмотрим работу табличной СУБД на примере Либератума.

Как хранятся данные в MySQL на примере работы Drupal

Либератум работает на движке Drupal, который весьма интенсивно работает с MySQL. Для того, чтобы вы имели возможность увидеть произвольную web-страницу, Drupal проделывает следующее:
1. Из адреса страницы вычленяется виртуальный адрес и создается запрос к таблице url_alias, чтобы получить внутренний идентификатор материала (номер узла в терминологии Drupal).

Например, пользователь запрашивает страницу http://liberatum.ru/exclusive/windows-10-chto-novogo. Тут же из адреса вычленятся необходимая для идентификации документа часть и делается запрос:

SELECT src FROM url_alias WHERE dst='exclusive/windows-10-chto-novogo'

Ответ будет таким:

node/26254

2. Теперь по идентификатору узла (26254) можно извлечь заголовок статьи и идентификатор автора. Эти данные хранятся в таблице node:

select title,uid from node where nid=26254

Ответ:

title uid
Что нового в Windows 10 1

В самой таблице node тело документа... отсутствует. Дело в том, что Drupal позволяет работать с ревизиями. То есть, каждая статья может иметь несколько версий. Поэтому для ревизий выделена отдельная таблица node_revision. В ней по идентификатору узла (nid) можно найти все версии данного документа и выбрать актуальную. Именно в этой таблице содержится тело документа и его анонс. Делаем еще один запрос:

select body,teaser,vid from node_revision where nid=26254

Если получаем несколько копий, то выбираем последнюю. Думаете все, можно собрать и показать готовую страницу? Нет.

Потребуется определить имя пользователя по его идентификатору (uid). Это запрос к таблице users. Потом нам потребуется найти все теги к статье. Это вообще отдельная история. Теги хуже всего вписываются в табличную модель представления данных, поэтому их хранение организовано через хитро закрученную жопу. Сейчас Билл Гейтс покажет вам как именно:

Теги в Drupal

Спасибо, Билл!

Достаточно сказать, что для хранения тегов используется сразу 7 разных таблиц, а для их получения делаются запросы с объединением. Минус в том, что объединение таблиц — одна из самых медленных операций. Вот эти таблицы:

  • term_data;
  • term_hierarchy;
  • term_node;
  • term_relation;
  • term_synonym;
  • vocabulary;
  • vocabulary_node_types.

Хорошо, получили теги, но теперь-то всё? Опять нет. Нам нужно получить количество просмотров документа. Берется оно из таблицы node_counter (обновление этого значения пока не рассматриваем, но его нужно увеличивать каждый раз, когда пользователь запрашивает этот узел, а это еще один запрос). Потом нам надо из таблицы node_access проверить, а имеет ли вообще пользователь право просматривать этот документ. Всё? Опять нет!

Комментарии. Для них есть отдельная таблица comments. Работать с комментариями на первый взгляд просто и тут видны достоинства табличного представления данных. Мы можем получить одним запросом сразу все комментарии к документу:

select cid,uid,comment from comments where nid=26254

Получаем сами комментарии, идентификатор комментария (он нужен для ссылки) и идентификатор пользователя. Ой, постойте, как идентификатор пользователя? Неужели опять запросы к таблице users? А как же иначе получить не номер, а юзернэйм?

А ведь помимо страницы с текстом есть еще боковые блоки с:

  • новыми материалами;
  • с самым читаемым;
  • с последними комментариями;
  • со статьями на похожие темы;
  • и т.п.

Даже переводы интерфейса хранятся в отдельной таблице. Нет, сразу в двух отдельных таблицах: locales_source и locales_target.

Почему Drupal мертв

Так как вы думаете, сколько всего MySQL приходится обработать запросов, чтобы предоставить данные для сборки всего одной страницы? От 300 до 1000 и больше. Да, с помощью хаков можно и нужно снизить количество запросов до минимума, но для этого нужно обладать глубокими знаниями Drupal и эту хакнутую копию движка потом будет очень трудно обновлять.

Кто-то может сказать, что ничего страшного не происходит. Ведь СУБД на то и СУБД, чтобы трудиться. Какая разница, 5 запросов на страницу или 500? Для программиста это совершенно неважно. Для пользователя тоже — ну подождет на секунду или две дольше... И такая точка зрения имела право на существование, но только до одного очень важного момента — пока поисковики не стали считать время отдачи страницы одним из важнейших факторов ранжирования. Можно перефразировать это так: поисковик никогда не даст на сайт больше посетителей, чем сайт сможет обработать.

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

В Drupal 8 нам обещают переход на ООП и следует полагать, что появится и ORM, который еще сильнее усугубит ситуацию.

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

Mongo — один запрос на страницу

Разработчики Mongo выбрали другой подход. Вместо хранения кучи строк, раскиданных по разным таблицам, пользователь может сохранять, искать и извлекать документы целиком. Под документами подразумевается не ODF и даже не DOCX, а некие сущности на языке JSON. Например, на JSON можно описать документ, состоящий из заголовка, тела статьи, имени автора, туда же можно поместить теги и комментарии. Одна web-страница — один документ — один запрос. Конечно, так делать не рекомендуется, но теоретическая возможность есть. Другие приятные преимущества Mongo:

  • нет таблиц — нет необходимости описывать схему базы данных;
  • нет схемы базы данных — можно менять структуру документов как угодно и не предупреждать об этом СУБД;
  • эта СУБД появилась относительно недавно и избавлена от старческих болезней MySQL/PostgreSQL и т.п. Например, Mongo изначально рассчитана на облачное применение и легко масштабируется как вертикально, так и горизонтально;
  • и т.д.

Работать с Mongo приятно. Вот примеры на Ruby:
Подключение к базе:

client = MongoClient.new

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

Выбор базы данных:

db = client.db("dbname")

Выбор коллекции (совокупность документов одного типа):

coll = db.collection("collectionName")

Создание документа и помещение его в базу:

doc = Hash.new
doc["pid"] = 1
doc["title"] = "Заголовок статьи"
doc["body"] = "Тело статьи"
doc["path"] = "/path/to/page"
coll.insert(doc)

Извлечение документа по номеру статьи (pid):

coll.find("pid" => 1)

Следует отметить, что разработчики Mongo постарались сделать работу с базой максимально приятной для программистов. Например, зачем заставлять программиста создавать базу данных, если ее можно создать автоматически, когда появится запрос на подключение к ней. Или зачем заставлять программиста создавать коллекцию, если ее можно создать за него автоматически, сразу же, как только придет запрос на вставку новых данных в несуществующую ранее коллекцию. Программисту остается написать всего несколько строчек кода и можно отправляться пинать балду. В это время его коллеги, работающие с SQL-базами данных, будут вынуждены придумывать схемы размещения данных и постоянно актуализировать их с помощью миграций. А еще любители SQL должны насиловать свой мозг, изобретая хитрые многоэтажные объединения нескольких таблиц с помощью вложенных запросов и тормозных операций типа JOIN. Зачем все это? Программист, использующий Mongo напишет всего пару строк кода и пойдет в бухгалтерию за зарплатой.

Все ли так прекрасно и где вообще тесты?

В следующей части статьи о райском наслаждении от Mongo попытаемся сконвертировать базу данных Либератума из MySQL в MongoDB, запилим тестовый Либератум и сравним время генерации страниц. Чтобы было интереснее, для сравнения возьмем еще и SQLite.

Оценка: 
5
Средняя: 5 (4 оценки)

Комментарии

Спасибо за статью. Нашел ее через гугл, зашел на сайт, прочитал, хотел уже искать продолжение, но посмотрел на дату — статья опубликована 20 минут назад. Жду продолжения.

Оценка: 
Пока без оценки

Месяц и 20 минут назад. Продолжение будет, уже есть что рассказать по теме.

Оценка: 
Пока без оценки

Комментировать

Filtered HTML

  • Use [fn]...[/fn] (or <fn>...</fn>) to insert automatically numbered footnotes.
  • Доступны HTML теги: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <blockquote> <strike> <code> <h2> <h3> <h4> <h5> <del> <img>
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Строки и параграфы переносятся автоматически.

Plain text

  • HTML-теги не обрабатываются и показываются как обычный текст
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Строки и параграфы переносятся автоматически.