SQL — гибок или почему я боюсь NoSQL

Original author: Armin Ronacher
  • Translation
От переводчика: Недавно презентовал на Хабре один проект, в котором использовал MySQL. Многие пользователи удивлялись, почему я не использую NoSQL для моих задач, и настоятельно порекомендовали переходить на нереляционные базы данных. Сегодня я наткнулся на эту статью, которая отлично объясняет, почему я “боюсь” NoSQL.

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

Последние две недели, однако, заставили меня понять, что я больше никогда не начну проект на основе MongoDB или любой другой нереляционной базы данных (НРБД) в качестве первичного хранилища данных. Обратите внимание – я сказал “начну”. Я не говорю, что больше никогда не буду использовать MongoDB как таковую.

Прежде чем я начну объясняться, сделаю отступление, чтобы убедиться, что мы с вами на одной волне. Во-первых, мой основной опыт работы с нереляционными хранилищами данных сводится к трем довольно разным сторонам этой технологии: MongoDB, HBase и Redis. Очень вероятно, что то, что я отношу к неотъемлемым преимуществам SQL, могут отлично делать другие НРБД. Причина написания этого поста в том, что я встречаю множество людей утверждающих, что NoSQL намного проще для создания прототипов, чем SQL. И с этим я несколько не согласен.

Схемы – великолепны


Похоже, что многие люди страшно боятся понятия схемы. Схема == явная типизация == убийца продуктивности труда. Проблема с этим утверждением в том, что оно основано на идее, что от схемы можно полностью избавиться. К сожалению, эта идея является несбыточной мечтой. В какой-то момент вы должны иметь понимание того, с чем вы имеете дело: вы должны знать, какого типа текущее значение. Если мы не хотим описывать типы в программировании, у нас есть несколько вариантов. Например, мы можем проанализировать код, чтобы понять какого типа будет переменная в определённый момент времени, как это делается в языке программирования Rust. Или мы можем прибегнуть к тому, что делает Python или JavaScript: переключиться на динамическую типизацию и решить проблему на этапе выполнения.

Эта проблема значительно существеннее в базах данных, потому что мы говорим не об отдельных переменных, а о целых коллекциях. Вы рассматриваете вашу НРБД как сущность, в которую вы кидаете JSON-документы. Но даже не объявив схему вы держите её в уме если работаете с коллекциями объектов. Предполагая, что вы найдёте общее количество комментариев в вашем блоге, подсчётом количества комментариев каждого поста – вы используете схему. Вы ожидаете, что число комментариев каждого поста – целое число, сумму которых необходимо сложить, чтобы операция имела смысл.

Схема в любом случае живёт в вашей голове. Проблема в том, что она не применяется должным образом и есть вероятность допустить ошибки. Если вы храните строку там, где в другой коллекции вы сохранили целое число, никаких проблем не будет, пока вы размышляете в рамках одного документа. В противном случае ваши коллекции внезапно оказываются несогласованными.

Оказывается, что даже если вы явно не используете схему, ваши НРБД всё равно используют некоторые её остатки для индексации. Несмотря на то, что люди могут вам сказать, НРБД – это не магия. Они работают под такими же ограничениями, как и любые другие хранилища данных, и нуждаются в явной индексации, чтобы быть быстрыми.

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

Знаете ли вы чего хотите?


Отсутствие схемы не должно быть причиной, чтобы не использовать MongoDB или любую другую НРБД. Легко добавить схему как надстройку, и такие вещи как MongoEngine для Python уже делают это за вас. Поэтому я не столько обеспокоен проблемой недостатка схем, сколько всеохватывающей идеей денормализованных данных.

Денормализованные данные – это клёво, без всяких сомнений. Их можно быстро и легко понять, и денормализация в нескольких нереляционных хранилищах намного проще, чем в SQL, потому что вы можете легко поместить данные куда необходимо. Это будет работать пока вы знаете, что делаете. К сожалению, иногда происходит непредвиденное и тогда вы понимаете, что у вас есть данные, к которым у вас нет доступа. Это то, что случилось со мной несколько раз на этой неделе.

По причинам, слишком сложным, чтобы объяснить здесь, я должен был извлечь информацию, которая была представлена не в том формате, в котором мне было необходимо. Все было так удивительно хорошо денормализованно, что попытки получить данные оказались кровавым месивом. С SQL-ориентированной БД это можно было бы сделать с помощью нескольких JOIN’ов и GROUP BY. А всё потому, что это писалось без догадки, что мне понадобятся данные в совершенно ином формате.

Убит “Слишком сложным”


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

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

MongoDB и другие НРБД удивительны, если вы уже всё знаете. Нет никаких сюрпризов. Все, что вам нужно – это масштабировать успешный продукт. Если же ваш продукт новый, большая ценность в том, чтобы быть гибким и иметь возможность получить информацию из данных быстро, даже если изначально вы такого не предполагали.

SQL базы данных, делают это относительно простым занятием и не заставляют вас страдать за отсутствие надлежащего планирования. Если запросы становятся медленными, вы можете просто добавить индексы в тех местах, в которых это необходимо. В некоторых НРБД это не сработает. В Redis вы формируете структуры данных вручную, и если изначально они неверны у вас появляется проблема. В MongoDB наличие или отсутствие индексации меняет результаты выборки иногда, и может сделать добавление индексов нетривиальной задачей.

Ещё хуже то, что иногда вы просто не можете добавить индексацию. Если у вас есть вложенные объекты, сложно просто запросить некоторые из них, т.к. они содержатся в других объектах. Поскольку БД не предоставляет запросов для таких нужд, не сложно оказаться в ситуации, когда вы вынуждены извлечь все данные для их автономной обработки через MapReduce.

Это даже не проблема, это просто часть архитектуры базы данных. Тем не менее, мы столкнулись с этим как с проблемой. К сожалению, иногда мир ведёт себя не так как должен. Вы сталкиваетесь с непредвиденными ситуациями, и нет ничего более утомительного, чем сидеть на ваших данных и не быть в состоянии получить информацию, когда вы знаете, что она там.

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

И я всё ещё люблю тебя MongoDB.
Share post

Comments 48

    –76
    Почему когда я читаю подобные статьи мне кажется что это писал старый пердун?
      +4
      Дословный перевод с любого языка всегда оставляет примерно такое ощущение =) Надо просто переводить вкладывая «искру», очеловечивая текст. Это как раз то, что пока не умеют делать машины.
      +8
      Насчет индексации — есть доля правды. Если в SQL — все более-менее понятно, то в MongoDB — для меня это все ещё частично магия в стиле — а что получится если… :)

      Второй недостаток — отсутствие классических транзакций.

      Если это не проблема, то для себя особых поводов для возврата на MySQL я не вижу, по крайней мере на том уровне на котором сейчас есть проекты.
        +1
        bknr.datastore — объектная база. Есть транзакции, схемы и совершенно логичные индексы. Ещё снапшоты.

        У автора каша в голове ИМХО. Он так пишет, как будто все НРБД одинаковы.
          +1
          А вы знакомы с bknr.datastore? Может быть написали бы статью/обзор?
          0
          Замечание по поводу индексов в mongo (когда разные индексы дают разные результаты) — очень однобокое. Такой же эффект можно получить даже в оракле. Как говорит Том Кайт — просто что бы добиться качественного результата нужно хорошо знать инструмент который ты используешь. Все субд имеют свои особенности, в том числе и реляционные.
          +11
          Интересно, а Armin читал docs.mongodb.org/manual/core/indexes/#index-sub-document?

          Утверждение про отсутствие схем полубредовое. Везде, где авторы монго говорят о проектировании БД, они говорят о схемах и о том, что их нужно сперва продумать, прежде чем что-то делать. Разница с SQL только в том, что это добровольное дело. Но если человек дурак, он и в MySQL всё в строку запихает.

          Совсем же недавно был курс m101 где все описанные здесь проблемы были разжеваны, даже в картинках.
            +3
            Специально в MySQL в строку запихиваю данные, по которым не нужен поиск (а агрегацию по ним в отдельную таблицу). Оказывается, я — дурак :(
              +5
              Только если в контексте «аа, я все данные скинул в бессвязную кучу, а теперь не ищется». У вас-то иначе.
            +8
            opening a can of worms = разворошить осиное гнездо
              +1
              спасибо!
              +5
              Ваш заголовок не соответствует заголовку оригинала и меняет смысл всего опуса. Автор не боится, он просто рассуждает о преувеличено гибкости некоторых постулатов nosql.
              Бояться ничего не надо, нужно просто думать что делаешь, а не слепо верить в серебряную пулю.
                –1
                JOIN'ы можно делать на клиенте, GROUP BY — с помощью агрегаций или map/reduce, нормализация — это хорошо пока не начнет тормозить.
                Поскольку БД не предоставляет запросов для таких нужд, не сложно оказаться в ситуации, когда вы вынуждены извлечь все данные для их автономной обработки через MapReduce.
                Зачем и куда извлекать данные, если map/reduce сам их извлекает?
                Если запросы становятся медленными, вы можете просто добавить индексы в тех местах, в которых это необходимо.
                До некоторого предела. А потом придется переписывать без всех этих join'ов и транзакций.
                  +6
                  Да, схема и типизация — бог с ней, а вот ориентация на денормализованные данные, на выборку всех нужных данных по одному ключу может сыграть неприятную шутку, когда понадобится сделать запрос, который на предыдущих этапах никому в голову не приходил.

                  Сейчас в текущем приложении в тестовой ветке используется аж четыре способа хранения:
                  — SQL (MySQL) с жесткой схемой, которая, утрируя, может изменяться пользователями через веб-морду (добавление новых атрибутов и типов)
                  — SQL с сериализованными и частично денормализованными средствами приложения полями
                  — NoSQL (CouchDB) с сильной денормализацией и активным использованием map/reduce
                  — Нормализованный NoSQL, где «джойны» и агрегация осуществляются средствами приложения

                  У всех способов свои недостатки и преимущества. Склоняемся к третьему, но далеко не однозначно.
                    +11
                    И такое ощущение, что половина заметок создает культ из какой-то технологии, другая — пытается объяснить, что не стоит этого делать.

                    Неужели не ясно сразу, что любая технология это всего лишь средство, уместное в определенном контексте.
                      +6
                      Ну, как известно, по большому счету NoSQL = NoACID. Этим многое объясняется. Не для всех это подходит.
                        0
                        Ну на самом деле ничто не мешает делать NoSQL решения которые соответствуют ACID. Возьмите MySQL, храните все данные в одной большой таблице — практически получите недо-NoSQL который соответствует ACID. Фишка не в ACID, фишка в том, что NoSQL дает удобство работы з определенным типом моделей данных, тогда как реализовать подобное с помощью SQL можно, но сложно и не всегда быстро.
                        +7
                        Странным показалось про индексы: такое ощущение, что люди брались за NoSQL, не читая про основные плюсы/минусы этого подхода, а теперь удивляются «оказывается тут с индексами как-то сложно».
                          +18
                          Наконецто первая толковая статья, которая трезво определяет место NoSQL, без лишнего хайпа. Вам жирнейший плюс за перевод.

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

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

                          Аналогично с БД, только уже на уровне данных. Таблицы — это ваша «файловая системв», а язык SQL — ваши sed, awk, ls, cp итд. NoSQL — это «специальная программа под конкретную задачу». Поэтому каждое NoSQL решение полезно вам в тех случаях, которые подходят под определенный круг задач. Шаг влево, шаг вправо — вам нужна еще одна специальная утилита для каждой новой конкретной задачи, в то время как SQL и логичная схема дают вам универсальный инструмент.

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

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

                          Если кому то интересны еще «неправославные» аналогии, — такое же место у XML vs JSON. XML — универсальный расширяемый текстовый формат, находящий применение буквально везде и применимый буквально ко всему. JSON — специализированное решение, упрощенный формат под конкретные задачи представления обьектов, где не требуется строгая валидация.
                            +2
                            Согласен со всем, кроме сравнения XML с JSON'ом. Валидация JSON'а уже давно есть, json-schema.org, причём она достаточно строгая. Или я не понимаю, о чём Вы говорите. И существует множество способов тождественного преобразования из XML в JSON и обратно. Единственное, на мой взгляд, чего не хватает в JSON — стандартизированных аналогов XPATH и XSLT.
                              +2
                              У NOSQL технологий есть преимущества, в некоторых случаях делающие выбор в их пользу однозначным. Например, горизонтальная масштабируемость.
                              +1
                              Вся идея в том, что NoSQL — это не «Not SQL», как думают многие, а «Not only SQL », то есть SQL используется только там, где реально нужен ACID, например в финансовых расчетах.
                              • UFO just landed and posted this here
                                  +2
                                  Кто говорит об аналитике? Простой пример — блог типа хабра. Очевидное решение на документных БД — пост и вложенная коллекция комментов с полем «автор коммента». И вот кто-то хочет посмотреть все комменты одного автора. Никакая не аналитика, а просто другой срез тех же данных.
                                  • UFO just landed and posted this here
                                      +5
                                      Зачем так делать? То что монго поддерживает такие структуры — не означает, что надо везде их применять. Стандартный подход с двумя коллекциями никто не отбирал. В документации всё расписано, когда стоит это применять, а когда нет.
                                        0
                                        На клиенте можно сделать на раз-два. Ему нужно — пусть на себе и считает :)
                                        Сейчас как раз допиливаю считатор для работы с произвольным JSON-ом.
                                        +2
                                        Да, отсутствие транзакций расстраивает, однако это большой минус только если продолжать думать в рамках РСУБД.

                                        А если не думать в рамках РСУБД, то можно не обращать внимания на частично выполненную операцию с разрушенными данными? Транзакции обеспечивают атомарность набора изменений, как это связано с РСУБД?
                                        +5
                                        Для некоторых задач NoSQL действительно хороши, но я бы позиционировал их не как «без схемы», а с «гибкой схемой».
                                        Как правильно заметил автор, схема есть всегда, но она гибкая и находится в уме.

                                        Лично мне не хватает в MongoDB утилитки для проверки логической связности базы по заданной гибкой схеме. Иногда хочется проверить что мои джуниоры ничего не начудили в тестовой базе. Интересно, а как другие пользующие MongoDB проверяют консистентность данных, или просто верят в то, что все окей?
                                        Пока же в планах в конце весны заняться этой темой, если никто раньше не напишет конечно.
                                          +1
                                          Молодец, Автор очень последователен. И хорошо отлавливает ОБратную связь ))
                                            +1
                                            В основе SQL лежит реляционная алгебра, потому-то SQL серверы разогнали все предки NoSQL баз данных в 70-х.
                                            Какая алгебра лежит в основе NoSQL баз данных?
                                              +1
                                              видимо — самая базовая, a = b (key-value)
                                                +2
                                                Контр-пример: есть замечательный Haskell (ну и другие fp-языки), основанный на математических формализмах. Но что-то он не в мейнстриме, в мейнстриме — сплошная императивщина.
                                                  +1
                                                  Тут узнал недавно, что в мейнстримном C++ недавно появились анонимные функции. 8-( ]
                                                    0
                                                    И C++ от этого перестал быть императивным?
                                                    Хотя конечно появление лямбд, дает вторую жизнь алгоритмам из STL.
                                                      0
                                                      Я к тому, что функциональщина рвется в мейнстрим ;)
                                                +15
                                                Отличная статья, но я бы добавил бы ещё один пункт — всё это многообразие NoSQL-решений появилось по большей части на волне массовой истерии крупных highload-стартапов. То тут, то там в блогах читаешь, что вот мол Facebook разработали Cassandra специально под свои нужды, начали использовать, и сразу все стало хорошо! Или же вот Foursquare — активно используют MongoDB, и тоже все у них круто, все масштабируется на 5+, одолели highload и т.п. Ну блин, ребята, вы же не Facebook и не Twitter. 90% проектов в сети прекрасно работают банально на одном сервере с использованием самой банальной РСУБД аля MySQL или PostgreSQL и даже в ус не дуют. Просто РСУБД — это проверенные временем средства, многие из которых активно разрабатываются и используются чуть ли не два десятилетия. В интернетах десятки обмусоленных тем по поводу различных подводных камней, кучи туториалов, миллионы утилит для разного рода диагностики и логирования, патчи от сторонних разработчиков и т.п. Где все это в NoSQL? РСУБД — это своего рода «швейцарский нож», при помощи которого можно сделать что угодно и как угодно. Про NoSQL же ИМХО имеет смысл задумываться, когда проект уже сформирован, прекрасно работает, и встает вопрос об оптимизации, и уж никак не заместо РСУБД, а вкупе с ними.
                                                P.S. MongoDB, кстати, довольно неплохая штука, подводных камней конечно куча, и распиаренный шардинг с репликацией на деле получаются далеко не такими простыми, как в документации. Но, на мой взгляд, через пару лет MongoDB скорее всего займет свою определенную нишу
                                                  0
                                                  Переведённый автор предполагает, что SQL всегда означает строгую типизацию.

                                                  На практике существует библиотека SQLite, служащая живейшим исключением из этого правила, и одновременно несказáнно популярная.
                                                    +6
                                                    А насколько ее популярность обусловлена именно динамической системой типов?..
                                                      +5
                                                      Лично мне динамические типы там только мешают. Весело начинается, когда выборка WHERE x=1 не работает только потому, что надо было писать x='1'. Это реальный кошмар, серьезно говорю.
                                                      +2
                                                      Каждый раз когда читаю подобные статьи, не покидает ощущение, что люди пытаются сравнивать отвертку с молотком. А потом яростно спорят, что из них лучше для ремонта в квартире.
                                                        +9
                                                        На прошлом Хайлоаде (2011) один из докладчиков высказывал похожие тезисы,
                                                        одним из его тезисов был: «новомодные NoSQL системы еще не проверены временем и есть риск наткнуться на подводные камни, решение которых потребует не запланированных ресурсов»

                                                        я использовал и МонгоДв, редис и Тарантул в своих проектах. Да, с каждым из них каждым из них натыкался на какие-то нерешенные проблемы, которые со временем решались. Со старым добрым проверенным SQL, тоже бывают свои проблемы. Только они, в силу более развитого коммунити выевляются и решаются гораздо быстрее.

                                                        новые, альтернативные системы хранения информации нужны, и у них есть и будет своя ниша использования. SQL нужен там, где он нужен… одно не должно подменять другое. Биллинг я ни когда не буду писать на МонгоДБ, а для простой и быстрой отдачи контента, не буду использовать MySQL.
                                                          +1
                                                          Подумал что писал Мицгол, когда увидел режущее слух НРБД. Ну почему бы не писать везде NoSQL?
                                                            +7
                                                            Имхо народ просто не врубается в основной тезис статьи, И он отнюдь не в том что SQL хуже или лучше NoSQL а в том что для начала проекта, когда многие сущности в процессе изменений SQL гибче. Успех NoSQL прошел на волне рефакторинга сформировавшихся проектов и если схема продумана и проверена, то сделать для нее NoSQL решение с оглядкой на HL очень продуктивно. В тоже время те же самые проекты, которые сделали рекламу NoSQL начинались на простых и известных решениях, в том числе и MySQL в качестве DB.

                                                            SQL позволяет как-то формализовывать схему данных, когда она непонятна до конца и решение можно поменять, если не кидать все в кучу. А уже после, когда функционал более менее сложился, когда данные естественно ложатся в схему, можно думать об оптимизации и подыскивать подходящее решение для масштабирования. Если проект выстрелил и вообще имеет смысл его масштабировать :)
                                                              0
                                                              В MongoDB наличие или отсутствие индексации меняет результаты выборки иногда

                                                              Пример бы посмотреть.
                                                                0
                                                                Ниже ответил про sparse индексы.
                                                                0
                                                                Такое может быть при использовании sparse индексов:

                                                                > db.test.insert({a:1, b:2, c:3})
                                                                > db.test.insert({a:2, b:3, c:4})
                                                                > db.test.insert({a:3, b:4})
                                                                
                                                                > db.test.find({}).sort({c: -1})
                                                                { "_id" : ObjectId("50e4205ce133536eb72229de"), "a" : 2, "b" : 3, "c" : 4 }
                                                                { "_id" : ObjectId("50e42055e133536eb72229dd"), "a" : 1, "b" : 2, "c" : 3 }
                                                                { "_id" : ObjectId("50e42062e133536eb72229df"), "a" : 3, "b" : 4 }
                                                                
                                                                > db.test.ensureIndex({c:1}, {sparse : 1})
                                                                > db.test.find({}).sort({c: -1})
                                                                { "_id" : ObjectId("50e4205ce133536eb72229de"), "a" : 2, "b" : 3, "c" : 4 }
                                                                { "_id" : ObjectId("50e42055e133536eb72229dd"), "a" : 1, "b" : 2, "c" : 3 }
                                                                
                                                                  0
                                                                  А, ну это логично.

                                                                Only users with full accounts can post comments. Log in, please.