Всем привет, меня зовут Артём и я алкоголик с давних пор и не разбирался в базе данных.
Ну то есть я понимал концепцию и то, как с ними работать, но всегда воспринимал их как черный ящик с понятным интерфейсом, который умеет сохранять и отправлять данные, если знать, как их спросить.
Механизмы, позволяющие происходить волшебству, были совершенно неясны.
И, честно говоря, мне было все равно.
Бизнесу нужно, чтобы вы придумали функции, а не все это.
Однако недавно я понял, что хватит этого терпеть и пора разобраться, что происходит под капотом, как это происходит и почему.
Статья подойдет тем, кто работает с базами данных каждый день и не особо вникает в детали, людям, которые, как и я, интересуются, как все работает и не знают с чего начать копать, или просто тем, кто хочет освежить свои знания.
немного разбираюсь в базах данных.
Все началось с того, что я задался вопросом, как, черт возьми, работает откат, когда в результате работы моего воспаленного ума получается код, который генерирует кривые запросы в базу данных и вынужден что-то делать, чтобы все не сломать полностью? Прежде чем начать копаться в механизме отката изменений, необходимо понять, что это такое и зачем оно вообще нужно.
И тут в дело вступает такая волшебная аббревиатура, как КИСЛОТА.
Так что же это значит КИСЛОТА? КИСЛОТА это набор характеристик, которым должна соответствовать наша программа, чтобы считаться кошерным местом хранения данных.
Попробую кратко описать каждую букву аббревиатуры (ведь речь идет не о кислоте в берлинских клубах): А Означает атомарность — транзакция работает как одна команда и либо выполняется целиком, либо не выполняется вообще С Означает Последовательность — по завершении транзакции данные не должны быть повреждены (такая неприятная вещь, как порча данных) или утеряны я Означает Изоляция - по сути означает, что транзакции не должны пересекаться друг с другом.
То есть, если одна транзакция уже начала работать с данными, то следующая транзакция должна дождаться завершения предыдущей, чтобы начать выполнять операции с этими данными.
Д Означает Долговечность — гарантировать, что после завершения транзакции данные будут сохранены и не будут подвержены риску утраты.
Тут нужно понимать, что речь идет не только об ошибках программного обеспечения, но и о наводнениях, цунами, отключениях электроэнергии и других ЧП.
Однако стоит отметить, что не каждая база данных обладает всеми свойствами ACID и зачастую это осознанный выбор (а иногда и нет), направленный на то, чтобы пожертвовать одними свойствами системы ради других.
Чтобы значительно упростить задачу, при проектировании хранилища мы должны балансировать между скоростью и надежностью.
Если мы хотим пойти по высокоскоростному маршруту, нам нужно хранить наши данные в самом быстром хранилище.
Конечно, быстрее всего было бы хранить данные непосредственно в кэше L1 процессора, но емкость хранилища нам не позволит расширить.
Следующее, что приходит на ум, — это оперативная память.
Хранение данных в оперативной памяти, несомненно, дает нам хорошую скорость, но о надежности можно забыть, ведь любой скачок напряжения в дата-центре будет означать неизбежную потерю данных.
Примером такой базы данных может быть memcached. Если мы хотим получить высокую надежность и нам не нужен частый и быстрый доступ к данным, то есть смысл записывать все данные сразу на ssd или на hdd для хардкорных ребят с конкретными задачами, например, хранение логов и редко запрашиваемая аналитика.
Однако в этом случае придется пожертвовать скоростью (которая, кстати, может измениться с появлением быстрых nvm-накопителей).
Популярные реляционные решения, такие как Postgres и MySql, используют гибридную схему, при которой данные записываются на жесткий диск, но также имеют уровень, отвечающий за работу с данными, кэшированными в памяти для быстрого чтения.
В этой статье мы поговорим именно об этом виде хранения.
Но прежде чем мы углубимся в техническую реализацию, давайте определим проблему с самого начала.
Допустим, у нас есть заказчик Петя и фрилансер Вася.
Вася сделал Пете классный лендинг (одностраничный сайт) на Тильде (конструктор сайтов) и ожидает, что Петя отправит ему за это деньги.
Петя не возражает и делает перевод через мобильный банк.
Чтобы никто не сел, нам нужно правильно выполнить несколько действий, а именно:
- Найдите таблицу с балансом Пети.
- Спишите необходимую сумму с его баланса
- Найти аккаунт Васи
- Зачислите сумму, списанную с Пети, на счет Васи.
Однако потом выясняется, что Петя ввел данные неправильно и мы не можем найти аккаунт Васи в нашей системе.
Сейчас мы имеем ситуацию, когда Петя стал беднее, а Вася не стал богаче.
Таким образом, мы нарушили правило согласованности, поскольку данные повреждены.
Это не очень крутая ситуация, ведь деньги не должны просто взять и пропасть.
Это справедливое утверждение, но тогда возникает вопрос – как этого не допустить? Чтобы ответить на этот вопрос, нам нужно понять, что на самом деле представляет собой база данных и как в ней хранятся записи.
Здесь нет однозначного ответа, потому что черт знает что, но если попытаться обобщить, то база данных — это программа (да, кто бы мог подумать), которая состоит из нескольких слоев: Транспортный уровень — отвечает за связь базы с внешним миром (наш замечательный сервер), а также за связь между узлами кластера (если мы, скажем, используем шардинг) Обработчик запросов — отвечает за прием запроса, его парсинг, оптимизацию и превращение в список операций (план выполнения), который должна выполнить база для успешного завершения транзакции Механизм выполнения — делает запрос на выполнение операций и агрегирует результаты Механизм хранения - ответственность за выполнение операций Хорошо, с этим разобрались, но теперь нам нужно нырнуть еще глубже в кроличью нору.
Дело в том, что механизм хранения тоже не так прост и состоит из нескольких частей, каждая из которых отвечает за соответствие нашей базы данных злополучному ACID. Здесь также нет единого мнения, но чаще всего выделяют следующие пласты: Менеджер транзакций — отвечает за очередь транзакций и проверяет, что транзакция полностью завершена, прежде чем покинуть очередь.
Отвечает за буквы A и D. Менеджер блокировок — блокирует объекты, участвующие в транзакции.
Ответственный за букву I Структуры хранения — Отвечает за доступ и организацию данных в хранилище.
За любые письма не отвечаю, но без него никуда не пойдешь.
Менеджер буферов — кэширует данные в памяти.
Мы хотим, чтобы наша база данных работала быстро, верно? Менеджер восстановления — отвечает за откат данных к исходному состоянию в случае, если что-то пойдет не так.
Вот! Recovery Manager – это та волшебная штука, которая может помочь нам сделать откат и вернуть Пете его честно заработанные деньги, а также избежать судебных исков и жалоб в тысячи различных инстанций.
Теперь вернемся к сценарию, когда нам нужно провести несколько операций в рамках одной транзакции, чтобы Петя заплатил Васе за красивый лендинг.
Чтобы изменить хранилище, используется семейство шаблонов под названием журналирование с упреждающей записью (WAL) .
В оперативной памяти создается специальный файл журнала, который содержит информацию о том, записи каких таблиц необходимо изменить в рамках операции, а также старые и обновленные значения полей.
Обновленный файл журнала затем сохраняется на диск.
Далее следует изменение записей в таблицах с данными, хранящимися в памяти.
После чего измененные значения таблицы сохраняются на диск.
Здесь важно понимать, что цепочка событий работает не на уровне всей транзакции, а на уровне отдельных операций, которые с определенной периодичностью сохраняются на диск (вызов ядра fsync()).
Но что произойдет, если операция не увенчается успехом и нам по какой-то причине потребуется откатить изменения и вернуть нашу базу данных в исходное состояние? Для восстановления исходного состояния используется семейство алгоритмов под названием ОВЕН (Алгоритмы восстановления и изоляции, использующие семантику).
Логику алгоритма можно описать следующими шагами:
- Анализ — этап анализа и выявления таблиц, которые были изменены в ходе транзакции, а также анализ операций, проведенных в рамках транзакции и их статуса (проверяем, где сломалось).
- ПОВТОРИТЬ — на этом этапе мы уже точно знаем, где сорвалась транзакция и какие таблицы были изменены.
Однако мы пока не можем гарантировать, что наше хранилище находится в согласованном состоянии, так как не понимаем, какие данные уже сохранены на диске, а какие нет. Поскольку ребята, написавшие ОВЕН, были большими поклонниками простоты, они решили не заморачиваться, а просто пройти все необходимые для транзакции операции заново, с самого начала и до момента ошибки.
Примените их еще раз и сохраните на свой жесткий диск.
Это необходимо для того, чтобы гарантировать целостность нашего хранилища и исключить возможность повреждения данных.
- ОТМЕНИТЬ - здесь все просто.
На последнем этапе делаем еще один проход по лог-файлу и переписываем значения в измененных таблицах.
Но в отличие от предыдущего этапа, на этот раз мы сохраняем значения таблицы, которые были в ней до того, как мы начали выполнять транзакцию.
В результате хранилище возвращается в исходное состояние и все довольны.
Вот и все.
Я надеюсь, что эта статья помогла вам немного лучше понять, что происходит под капотом популярных баз данных, каковы принципы ACID и как они поддерживаются, а также какие шаги предпринимает репозиторий для следования этим принципам.
Книги, которые стоит прочитать, если вдруг захочется копнуть глубже: Внутреннее устройство базы данных Алекса Петрова Проектирование приложений с интенсивным использованием данных: большие идеи, лежащие в основе надежных, масштабируемых и ремонтопригодных систем Интервью по проектированию системы – Руководство для инсайдеров, второе издание Теги: #postgresql #NoSQL #sql #база данных #разработка баз данных
-
Об Электронных И Бумажных Книгах
19 Oct, 24 -
Сколько Стоит Использование Стилей На Сайте?
19 Oct, 24 -
Бизнес-Кирпичики
19 Oct, 24 -
Пикник С Инвесторами: Итоги
19 Oct, 24