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

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

В статье обсудим семь важных аспектов работы с SQL и NoSQL базами данных.

  1. Журнал опережающей записи WAL.
  2. Трудный выбор базы данных.
  3. Менять базу данных сложнее, чем изначально разрабатывать с ее учетом
  4. NoSQL не заменяет SQL, а дополняет его.
  5. Трудозатратное масштабирование.
  6. Индексы часто подводят.
  7. Транзакции.

Примечание: если не указано обратное, то под термином “база данных” подразумевается реляционная базах данных, такая как PostgreSQL или MySQL, а не база данных NoSQL. Тем не менее нереляционные базы данных также рассматриваются в статье.


1. Журнал опережающей записи WAL

Базы данных, а точнее их внутренняя архитектура  —  не самая легкая для понимания концепция. Поначалу легко подумать, что база данных  —  это парочка файлов и код, управляющий соединениями или модификациями этой парочки файлов. В определенной степени вы окажетесь правы. Но на самом деле, по своей сути, базу данных лучше воспринимать как файл журнала (logs), хранящий список из упорядоченных данных о транзакциях.

Состояние таблиц, строк, схем и всего остального следует из накопленных изменений, записанных в журнале. Разные движки баз данных хранят журнал по-своему, но “псевдожурнал” раздела формулируется примерно так:

...
2021-06-07 12:24:35.044513-4000 INSERT INTO 'People' "jeff" "wilson"
2021-06-07 12:25:33.232098-4000 INSERT INTO 'People' "mike" "lou"
2021-06-07 12:25:37.140013-4000 INSERT INTO 'People' "pat" "cat"
2021-06-07 12:26:11.030002-4000 INSERT INTO 'People' "mark" "wilson"
...

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

В PostgreSQL и некоторых других реляционных базах данных журнал транзакций называется Write-Ahead-Log (WAL), что переводится как “Журнал опережающей записи”. Управление WAL и его функционалом  —  важная часть настройки производительности реляционных баз данных, а также основа того, как PostgreSQL управляет собственной репликацией. Любая записанная в WAL транзакция транслируется репликам, чтобы они, в свою очередь, смогли добавить транзакцию в собственные WAL.

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


2. Трудный выбор базы данных

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

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

Как выбрать базу данных для проекта? Стоит задаться следующими вопросами, хорошенько продумав ответы на них.

  • Какие данные предполагается хранить в базе данных?

Например, будете ли вы хранить логи работы программы, или же хранимые данные  —  это учетные записи пользователей?

  • Насколько сложны данные, которые планируется хранить?

Можно ли быстро и легко нормализовать их?

  • Насколько данные однородны?

Следуют ли данные примерно одной схеме или же разрозненны, с высокой степенью вложенности?

  • Как часто данные считываются или записываются?

Приложение требовательно к чтению, к записи или к тому и другому, или же не требовательно ни к чему из этого?

  • Каковы требования к среде или деловые соображения компании-заказчика?

Есть ли у вас уже ранее заключенные соглашения с поставщиками? Нужна ли вам вообще поддержка поставщика?

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


3. Менять базу данных сложнее, чем изначально разрабатывать с ее учетом

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

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

Вот почему так важен пункт №2: после выбора базы данных, ее уже трудно изменить.


4. NoSQL не заменяет SQL, а дополняет его

Споры о характеристиках технологий SQL (декларативный язык программирования, применяемый для создания, модификации и управления данными в реляционной базе данных) и баз данных NoSQL активно продолжаются уже не первый год. В пылу спора обе стороны упускают тот факт, что базы данных NoSQL не заменяют SQL, а дополняют их, подходя к решению задачи с другой стороны.

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

С другой стороны, по тем же причинам никто не захочет использовать Redis для хранения высокореляционных данных, таких как учетные записи пользователей или финансовые транзакции. Конечно, любую базу данных с помощью программирования можно заставить работать с почти любыми данными. Но зачем добавлять сложности и головную боль, если можно просто использовать подходящий инструмент для конкретной работы?

На рынке программных услуг представлены несколько отличных баз данных, технически являющихся NoSQL, но предназначенных в том числе для хранения реляционных данных (например, база данных Couchbase). Тем не менее стоит обратить внимание на внешние факторы, влияющие на выбор одной из них, такие как поддержка клиентских языков и облачных технологий  —  все это необходимо учитывать при выборе базы данных для проекта.


5. Трудозатратное масштабирование

Масштабирование базы данных  —  уникальная проблема. Все-таки она хранит не только транзакции, но и состояние. Следовательно, она по сути своей  —  приложение с состоянием. Найти способ воспроизвести состояние в нескольких экземплярах базы данных так, чтобы оно было последовательным, безопасным, достаточно быстрым, прозрачным для любого приложения  —  это достаточно сложная задача. Именно поэтому “вертикальное масштабирование”  —наиболее распространенный способ масштабирования баз данных, особенно реляционных.

Поговорим о двух видах масштабирования: вертикальном и горизонтальном. Для формирования правильного понимания каждого метода нужна еще одна статья, так что сейчас обсудим масштабирование упрощенно. Лучше всего подойдет аналогия: допустим, дан кувшин с водой объемом один литр, но в нем есть небольшое отверстие, поэтому необходимо торопясь перелить воду в другую емкость. Однако даны контейнеры меньшего, чем один галлон, объема. Подойдут два разных способа решения задачи.

  1. Применить десять питьевых стаканов, распределив литр воды между ними.
  2. Применить две банки объемом в половину литра, распределив воду между ними.

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

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

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

Базы данных NoSQL можно масштабировать горизонтально. Они, как правило, следуют модели “Согласованность в конечном счете”, где данные в конечном итоге согласуются по всему кластеру, но зачастую для обращающихся к данным приложений репликация непрозрачна. Объясняя проще: до тех пор, пока кластер не синхронизирует данные на всех узлах, приложение, читающее данные с узла A, получит одну версию данных, а приложение, контактирующее с узлом D  —  другую. Необходимость синхронизации множества контейнеров поначалу кажется неприятным моментом. Но если вы проектируете и создаете приложения с учетом данной особенности масштабирования, она не станет для вас проблемой.


6. Индексы часто подводят

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

Если при чтении вы подумали: “Эй, так индексы базы данных очень похожи на таблицу хэшей!”, то вы не ошиблись. Индексы  —  по сути, разновидность хэш-таблицы в пределах определенного столбца. Большинство реляционных баз данных автоматически создают индекс по первичному ключу, но можно и самостоятельно добавить индексы к любым столбцам.

Однако, не добавляйте индексы в столбцы самостоятельно!

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

В отличие от оглавления книги  —  просто списка указателей  —  индекс фактически содержит вторую копию иначе упорядоченных столбцов.

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

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


7. Транзакции

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

В зависимости от уровня изолированности транзакций, производительность и стабильность базы данных  —  особенно стандарта ACID  —  напрямую зависит от порядка отправки транзакций и объема обрабатываемых ими данных. 

Небольшой анекдот про блокировку транзакций:

“Много лет назад меня раздражало, что “база данных работает медленно и с перебоями”. Оказалось, что кто-то из коллег работал из дома в Коста-Рике с очень медленным интернет-соединением. Клиентское приложение, установленное на ноутбуке этого коллеги, выполняло SELECT * из общей таблицы. Поэтому каждый раз, когда приложение запускалось, база данных блокировала всю таблицу на время, необходимое для отправки всех результатов в Коста-Рику, поскольку даже select  —  это транзакция”.

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

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


Выводы

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

Читайте также:

Читайте нас в TelegramVK и Яндекс.Дзен


Перевод статьи Peter Christian Fraedrich: 7 Database Concepts You Should Know About

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