Что мешает современным СУБД работать в полную мощность на современном оборудовании
Статья является продолжением цикла о проблемах современных систем управления базами данных, начатого материалом Проблемы современных СУБД: Нужен ли морю данных океан «железа»? в сентябре 2020 года.
Содержание |
'Основная статья:' СУБД
По состоянию на октябрь 2020 года на сайте db-engines (самый известный мировой рейтинг СУБД) зарегистрировано 358 продуктов для управления базами данных. Среди них можно найти традиционные реляционные и современные NoSQL продукты, решения, использующие для обработки данных оперативную память и даже аппаратные ресурсы видеокарты. Конкуренция по-прежнему высока – растут скорости обработки и объёмы данных.
Однако даже самое современное программное обеспечение далеко не всегда способно эффективно решать возложенные на него задачи. Видный исследователь в области управления данными Майкл Стоунбрейкер в одном из выступлений привёл пример: традиционная СУБД тратит лишь 4% процессорного времени непосредственно на полезную нагрузку. Всё остальное может уходить на поддержание работы системы [1] [2].
Естественно, эта цифра – в значительной степени абстрактна. Но факт остаётся фактом: сегодняшние СУБД не раскрывают полностью всех возможностей многоядерной архитектуры аппаратной части, а лишь вынуждают конечного пользователя наращивать эту самую аппаратную часть. Примеры были приведены в предыдущей статье цикла[3].
Почему так происходит? Что «под капотом» современных СУБД мешает им полностью раскрыться на многоядерной архитектуре?
Классический подход с блокировкой
Для начала вспомним, что базовая архитектура наиболее популярных сегодня классических реляционных дисковых СУБД формировалась в 80-90-е годы XX века, когда компьютеры были большими, процессоры – одноядерными, программы – однопоточными, а данные хранились на жёстких дисках. В это время работа внутренних структур СУБД строилась на принципах взаимного исключения при доступе к общему ресурсу. Если какой-то процесс хочет изменить блок данных – данные нужно временно заблокировать для других процессов, которые также могут попытаться внести свои изменения. Доступ осуществляется с помощью так называемых объектов синхронизации. Простейший пример такого объекта – спинлок.
Предположим, процесс пытается захватить спинлок. Если попытка удалась – процесс получает монопольный доступ к ресурсу и выполняет нужные действия. Если попытка не удалась, процесс ожидает в цикле пока спинлок освободится. Такой способ тратит вычислительный ресурс, не совершая полезной работы, поэтому спинлок используют, предполагая, что ожидание не будет долгим. Более сложный тип – мьютекс (от английского «mutual exclusion» – взаимное исключение) может передать управление планировщику задач (ядру) и освободить CPU для другой задачи или потока. Мьютекс удобнее спинлока, но на его работу тратится больше вычислений и времени.
Механизмы блокировок обеспечивают консистентность данных и изоляцию транзакций, но при этом сильно ограничивают возможности параллельной работы. В стремлении избежать блокировок при работе с данными в 90-х годах появился мультиверсионный контроль конкурентного доступа (MVCC) на основе «снимков» базы – изменения данных не видны другим пользователям, пока они не подтверждены. Сегодня этот механизм есть в большинстве СУБД, даже в современных NoSQL-решениях.Сергей Петренко, МТС RED: ИБ-игроки строят экосистемы, чтобы обеспечить заказчика интегрированными средствами защиты
Между тем, вся внутренняя структура большинства классических СУБД по-прежнему построена на идее блокирующих алгоритмов и взаимного исключения. Примеры таких решений – это Oracle, Microsoft SQL Server, PostgreSQL, MySQL.
Пока процессор был одноядерным – другого решения не требовалось. А вот появление многоядерных чипов и многопоточного программирования быстро показало минусы архитектуры с блокировками.
Во-первых, сама по себе работа механизма блокировки (чтение блокировки, ожидание освобождения, запись «занято», захват, освобождение, запись «свободно») требует значительного (по меркам программы) процессорного времени при наличии конкуренции. А во-вторых, есть общая проблема – всякая параллельная работа невозможна на участке, защищаемом блокировкой.
Для конечного пользователя проблема выражается в том, что в условиях конкуренции на многоядерном оборудовании работа СУБД и приложения, которое его использует, замедляется в десятки раз.
А если не блокировать?
Альтернативой являются неблокирующие алгоритмы, которые как раз и появились с развитием многоядерной архитектуры. Принцип их работы состоит не в том, чтобы запретить конкурирующим потокам доступ к ресурсу, а в том, чтобы обеспечить консистентное существование ресурса для всех потоков, которые в данный момент времени работают с ним параллельно. Преимущество неблокирующих алгоритмов — в лучшей масштабируемости по количеству процессоров.
Главный минус – неблокирующие алгоритмы предназначены для обработки данных только в памяти и подразумевают кооперативную работу конкурентных потоков. Использование таких алгоритмов в классических СУБД затруднено, прежде всего из-за активной работы с диском. Значительные временные задержки для обращения к диску не позволяют осуществить кооперативную работу конкурентных потоков при встраивании долгих операций в неблокирующий алгоритм. Поэтому наилучшим образом неблокирующие алгоритмы работают в in-memory системах.
Для потребителя это означает, что вся система будет работать в разы быстрее классической СУБД. Но база данных будет ограничена объёмом оперативной памяти сервера или кластера серверов. Естественно, если база данных вырастет – придётся докупать оперативную память.
Примеры таких решений – Redis, Tarantool, Hazelcast, SAP HANA. Они приобретают всё большую популярность в самых разных системах, могут использоваться в качестве кэша для ускорения доступа к классической дисковой реляционной базе данных. Но итоговая стоимость владения такими решениями всё ещё достаточно высока.
Как соединить несоединимое
Естественно, что разработчики и классических дисковых, и in-memory СУБД в ходе развития своих продуктов хоть и с разных концов, но приходили к одной и той же проблеме – как совместить преимущества обоих подходов и избавиться от их недостатков. Разработчики in-memory решений хотят убрать из памяти на диск неиспользуемые данные и тем самым повысить эффективность работы системы. А создатели классических СУБД хотят быстрее работать на многоядерном «железе» в случае, когда данные обрабатываются исключительно или преимущественно в памяти.
Но пока что никому из гигантов рынка не удалось создать продукт или хотя бы прототип, удовлетворяющий всем этим требованиям.
А есть ли третий вариант? С точки зрения авторов, на производительность СУБД влияют несколько факторов. Во-первых, именно подходы и методы синхронизации в итоге обеспечивают уровень эффективности использования современных многоядерных процессоров. Во-вторых, в реальных системах не все данные имеют одинаковую частоту обращений, поэтому кэширование данных в памяти позволяет более рационально использовать ресурсы сервера.
Поэтому, по мнению авторов, интересно построить архитектуру СУБД, которая базируется на комбинации неблокирующих алгоритмов при обработке данных в памяти и классических методов синхронизации при ожидании событий с длительными задержками. Такая архитектура позволяет наследовать лучшие свойства от разных классов СУБД: специализированных in-memory и классических систем.
В следующей статье читатели познакомятся с прототипом, разработанным специалистами РЕЛЭКС. Свойства прототипа и тестовые измерения его производительности позволяют утверждать, что разработчикам удалось построить именно такую – принципиально новую архитектуру.