MongoDB против MySQL
Недавно я сильно впечатлился возможностями MongoDB. Под Монгу очень приятно программировать, но эта СУБД не подходит для небольших (и даже средних) web-проектов. Я задумался: а что тогда подходит?
Как я уже говорил, основная претензия к MongoDB — прожорливость. Монга скушала все процессорное время, а web-приложение выдавало всего 140 страниц в секунду. Тогда я решил переписать свое приложение, заменив Mongo на MySQL.
Я уже привык к удобствам и переходить обратно на голый SQL не хотелось. Не говорю уже о таких «радостях», как схемы, миграции, нормализация и т.п. Поэтому я принял решение попробовать использовать ORM. Для Ruby это ActiveRecord (используется в Ruby on Rails) и Data Mapper. Первое решение более продвинутое, но сильно тормозит. Отчасти из-за убийственной медлительности ActiveRecord разные пейсатели часто творят на тему «Почему Web-разработчики отказываются от Ruby on Rails?» А рельсы действительно тормозят, поэтому вместо Рельсов я предпочитаю шустрый и неуловимый микрофреймворк Sinatra. Чтобы его не замедлять, оставалось выбрать Data Mapper.
Этот замечательный Data Mapper
Data Mapper мне очень понравился. Да, описывать схему всё равно приходится, но все остальные рутинные задачи с программиста снимаются. Вот как, например, выглядит модель Feed (лента новостей):
class Feed
include DataMapper::Resource
property :fid, Serial
property :name, String, :required => true, :length => 64
property :url, String, :required => true, :length => 256
property :group, String, :default => 'General', :length => 64
property :ts, DateTime
has n, :NewsItem
end
DataMapper.finalize
DataMapper.auto_upgrade!
Волшебство происходит при вызове метода auto_upgrade!. (Это не я восклицаю, а это в Руби method и method! — два разных метода). DataMapper создаст за юзера таблицы, установит связи и будет отслеживать изменение схемы.
Вторая причина выбора ORM — независимость от конкретной СУБД. В последующих статьях я сравню производительность MySQL с PostgreSQL, SQLite3 и так далее. Заменить СУБД можно в одну строчку в конфигурации:
DataMapper.setup(:default, 'mysql://user:password@localhost/database")
Это для MySQL. Чтобы перевести приложение на работу, например, с PostgreSQL требуется только установить адаптер для DM и заменить строку подключения:
DataMapper.setup(:default, 'postgres://user:password@localhost/database')
А теперь представьте, сколько бы потребовалось переписать, если бы приложение общалось с СУБД на голом SQL! Высокий уровень абстракции — это прекрасно!
Ну так вот, было у меня приложение — RSS-читалка. Фиды и новости хранились в Монго. Это приложение я оставил, но переписал ту часть, которая отвечает за сохранение и загрузку данных в СУБД. Итак, чего удалось мне достигнуть заменой Монго на MySQL? Настает момент истины...
Момент истины
RSS-читалка на Монго выдавала 140 страниц в секунду. MySQL через ORM стал давать 48 страниц в секунду. Трехкратное падение производительности.
Причины трехкратного падения производительности
Признаться, я ожидал роста на порядок, а тут падение. Пришлось искать причины и они были найдены: если в случае с Монго тормозила сама СУБД, то в случае с MySQL СУБД на пике потребляла всего 1,5% времени процессора. Что же тогда тормозит? Тормозит сам Ruby и особенно ORM.
Что делать?
Мне надоело, что как только какая-то технология начинает мне нравится, так сразу же выясняется, что она тормозит. Что же, писать web-приложения по старинке, на голом PHP + SQL? Неужели мы никогда не избавимся от этого обрыдшего LAMP?
Я стал выяснять, почему Ruby + Sinatra + Data Mapper = паралич. Как оказалось, Ruby сам по себе — очень медленный. Невероятно медленный. По двум причинам:
- гнилой сборщик мусора;
- высокие расходы на вызов функций.
Что же посоветуют Ruby-гуру? Совета тоже два:
- не использовать Ruby;
- не использовать ORM.
Первый совет насмешил, но на самом деле речь не идет об отказе от самого языка. Спецы советуют использовать вместо классического интерпретатора Ruby его Java-клон JRuby, который компилирует программу на Ruby в байткод Джавы. Совет дельный, надо попробовать.
А вот второй совет не очень понравился. Если не использовать ORM, то зачем тогда вообще использовать объектно-ориентированный по самые помидоры Ruby? Логичнее писать тогда уж в процедурном стиле на PHP и использовать голый SQL для доступа к данным.
А что дальше?
Я еще протестирую Data Mapper с другими СУБД, но уже понятно, что узким местом так и будет оставаться сам Ruby. И от него придется отказываться. Просто потому, что поисковики учитывают время генерации страницы и никогда не дадут на медленный сайт много посетителей. Правильно ли использовать инструменты, которые позволяют программисту наслаждаться своей работой, но в результате получается дерьмовый продукт, который никогда не станет популярным?
Вывод
MySQL уделывает всех на небольших наборах данных (до 10 тыс. записей в таблице). Потом на сцену выходит Postgres. Ну а для гигантских объемов слабоструктурированных данных лучше подходит MongoDB. Только у вас должно быть 2+ выделенных под одну Монгу сервера, иначе профит будет незаметен.
Комментарии
Laestrygon
27 декабря, 2014 - 15:58
Может быть правильнее пересмотреть свое отношение к программированию и поменять мнение о том, что «удобно», а что нет?
pomodor
27 декабря, 2014 - 19:46
По ссылке какая-то демагогия от старого пердуна, который советует учить Яву 10 лет. А чего тогда уж сразу не 100? Родился, учился, учился, помер — просто идеальный программист. ;)
Удобно — это когда средства разработки позволяют управлять сложностью проекта. За счет использования уровней абстракции. ORM — один из таких уровней. И проблема тут не в том, что я все никак не могу "поменять мнение о том, что «удобно»", а в том, что существующие ORM на Ruby дико тормозят.
ЗЫ Почитайте лучше «Совершенный код».
DJ_Baldey
28 декабря, 2014 - 07:30
Спасибо за хорошую литературу! Начал читать, осознаю, что я с автором одного мнения. Но я то на своих шишках набитых, э-э-эхх... мне бы её пораньше...
Комментировать