Хранилище данных Falcon
Хранилище данных Falcon
Автор: Павел Пушкарев, paulus (at) sqlinfo (dot) ru
В сервере MySQL версии 5.2 появился новый вид хранилища данных — Falcon. Эта статья является обзором нового вида хранилища. В ней будут обсуждены его достоинства, недостатки и возможности.
Хранилища MySQL
База данных MySQL работает с несколькими видами хранилищ данных. Хранилища отличаются способом хранения данных, набором возможностей.
Хранилище InnoDB поддерживается сервером MySQL начиная с третьей версии. По своей сути оно организовано чрезвычайно просто, а потому с его помощью можно добиться очень высокой производительности, особенно при выборе данных из таблиц. С другой стороны, добавление данных в таблицу влечет за собой блокировку всей таблицы, поэтому одновременное добавление и выбор данных из одной и той же таблицы ведет к задержке выбора данных.
Вторым по популярности хранилищем MySQL является InnoDB. Это хранилище устроено гораздо более сложным образом. Производительность при выборе данных из таблиц в нем ниже, чем в InnoDB, зато таблицы не блокируются при изменении данных и одновременные добавления в таблицу не блокируют операции чтения из нее. Более того, InnoDB полностью поддерживает транзакции со всеми четыремя уровнями изоляции и ссылочную целостность.
Хранилище Falcon
Новое хранилище Falcon было добавлено в сервер в версии 5.2. Оно разрабатывалось исходя из мысли, что оборудование за последние несколько лет претерпело большие изменения, и, если использовать его возможности, реально создать хранилище с поддержкой большой функциональности и при этом с сохранением высокой производительности.
В настоящее время (5.2.0-alpha), хранилище находится в состоянии alpha, т.е. в нем нет всей запланированной функциональности и возможны ошибки кода. Тем не менее, эта версия позволяет оценить будущее хранилище и понять, насколько оно хорошо будет подходить к использованию с вашими приложениями.
Запланированная функциональность Falcon
При разработке Falcon планировалось создать следующую функциональность:
- полная поддержка транзакций (full ACID)
- поддержка ссылочной целостности таблиц
- хранение всех типов данных MySQL
- оптимизации для работы с большим количеством оперативной памяти
- полная мультиверсионность, позволяющая уменьшить, а иногда полностью устранить необходимость в блокировках
- сжатие данных
К сожалению, в alpha-версии воплощена не вся задуманная функциональность. Так в текущей версии не работает ссылочная целостность и нету поддержки уровня изоляции транзакций SERIALIZABLE. Тем не менее, первые три уровня изоляции работают так, как и ожидается.
Falcon в файловой системе
В файловой системе Falcon создает автоматически по три файла для каждой базы данных, использующей таблицы Falcon. В данной версии никакие характеристики этих файлов не могут быть настроены. В дальнейшем планируется возможность настройки двух файлов. В отличие от InnoDB, в котором Вы должны настроить пространство данных, Falcon создает файлы по мере необходимости.
Пусть мы создаем Falcon-таблицу в базе данных test:
mysql> CREATE TABLE tbl (a INT) ENGINE=Falcon; Query OK, 0 rows affected (1.24 sec)
При этом сервер автоматически создает следующие файлы в каталоге данных:
- test/tbl.frm — файл структуры таблицы, такой же, как и для других типов хранилищ
- test.fts — файл данных Falcon (содержит данные, индексы и прочую информацию о всех таблицах Falcon в базе test)
- test.fl1, test.fl2 — файлы журналов Falcon (содержат журналы транзакций, которые используются для отката и применения транзакций)
Очевидно, что отсутствие возможности настройки названий этих файлов приводит к ограничению на переносимость таблиц и баз данных на другие диски (в InnoDB, например, можно переносить отдельные таблицы на другие физические диски, увеличивая производительность, а в InnoDB — отдельные файлы данных). Вы все еще можете воспользоваться методом переноса файлов в UNIX-системах с помощью символических ссылок, однако для Windows рабочего метода пока еще нет (и не работает старый метод переноса всех баз данных, т.к. файлы данных Falcon хранятся вне основного каталога базы).
Оптимизации Falcon
Несмотря на многие указанные ограничения, хранилище Falcon создавалось как хранилище более быстрое, чем InnoDB. Скорость работы Falcon во многом зависит от того, какое количество памяти вы выделяете для его работы. Вы можете настроить ресурсы хранилища (так же, как и для любого другого хранилища), установив значения серверных переменных.
mysql> SHOW VARIABLES LIKE 'falcon%'; +--------------------------+----------+ | Variable_name | Value | +--------------------------+----------+ | falcon_log_dir | | | falcon_max_record_memory | 20971520 | | falcon_min_record_memory | 0 | | falcon_page_cache_size | 4194304 | | falcon_log_mask | 0 | | falcon_debug_server | OFF | | falcon_page_size | 4096 | +--------------------------+----------+ 7 rows in set (0.01 sec)
Помимо различных переменных, предназначенных для разработчиков, в этом списке есть переменные, которые непосредственно влияют на производительность хранилища. Это переменные falcon_max_record_memory, falcon_min_record_memory и falcon_page_cache_size.
Первые две переменные ограничивают размер памяти, которое хранилище использует для кэширования данных, индексов и прочей информации из базы данных для быстрого обращения к ним. В отличие от InnoDB, в этот кэш попадают только реально запрашиваемые данные. Те столбцы, которые Вы не используете, в кэш не попадут.
Минимальный размер памяти записей используется тогда, когда кэш следует освободить. Освобождение происходит до этого порога.
falcon_page_cache_size устанавливает размер кэша страниц при чтении данных с диска. В этот кэш попадают страницы данных из файла данных. Обратите внимание, что данные типа BLOB не попадают в record_cache (ввиду их возможно значительного размера), однако они могут попасть в page_cache. Тем не менее, оптимизация происходит при работе с record_cache, поэтому если вы используете небольшое количество BLOB-данных, вам следует выделить больше памяти под record_cache, иначе — сбалансировать кэши по объему.
Работа Falcon с диском
При работе с диском, Falcon использует огромное количество оптимизаций. Самое большое время в работе БД с диском занимает поиск нужной дорожки. Falcon оптимизирован таким образом, чтобы уменьшить количество таких поисков. Например, если несколько транзакций работают одновременно, то запись журнала их работы будет произведена только при завершении одной из транзакций (ну или, разумеется, если закончится память в кэше записей).
Кэш записей содержит в себе несколько версий строк (если над ними работают разные транзакции) и индексы, связанные с этими строками. Индексы перестраиваются также в памяти и записываются вместе с завершением транзакции для уменьшения количества дисковых поисков.
При чтении и записи данных на диск, Falcon использует два потока. Один из потоков («gopher») переносит данные из кэша на диск. Он же объединяет новые индексы с индексами, хранящимися на диске. Второй поток в фоновом режиме освобождает место на диске и обновляет кэш страниц данных.
При записи данных на диск они архивируются, что позволяет уменьшить занимаемоеданными на диске место. В оперативной памяти данные хранятся в распакованномсостоянии, поэтому уменьшения производительности практически нет.
Заключение
К сожалению, не вся функциональность Falcon доступна. Более того, в текущей версии сервера, это хранилише не имеет многих оптимизаций для сравнения скорости с другими хранилищами. Тем не менее, Falcon показывает очень хорошие результаты при работе с кэшами. Он отлично работает с тремя уровнями изоляции и впоследствии вполне сможет стать хорошей заменой для InnoDB.
Итак, некоторые особенности Falcon (далее вольный перевод):
- Отсутствие spinlock'ов. Falcon не использует spinlock'и. Он использует свой собственный механизм блокировок, заставляющий операционную систему переходить в режим ожидания, если ресурс недоступен. Посмотрим, к чему это приведёт, но я полагаю, что в условиях ОС с поддержкой нескольких процессоров при некоторых типах блокировок, когда может быть несколько конфликтов, будет необходимо использовать spinlock'и. Сейчас уже даже в ноутбуки устанавливается несколько процессоров, поэтому можно забыть о ситуации, когда spinlock в пустую тратит процессорное время единственного CPU без практической выгоды. В тоже время spinlock'и нужно использовать с умом.
- Кэширование строк.Не может не радовать факт, что Falcon позволяет кэшировать отдельные строки таблиц, т.к. это означает, что в одинаковом объёме оперативной памяти можно сохранить большее количество данных. Вам может понадобиться сохранить в кэш только одну строку из страницы объёмом 8-16 КБ, однако кэширование на уровне страниц вынуждает сохранять всю страницу целиком; то есть если Вам потребуется поместить в кэщ 1000 100-байтовых строк, находящихся каждая на новой странице, в кэш, то с Falcon'ом Вам потребуется выделить всего лишь 100 КБ вместо 8-16 МБ. На практике же существуют специальные методики, позволяющие оптимизировать постраничное кэширование, поэтому в реальности выигрыш будет несколько меньше.
- Вместо фиксированного объёма кэша — диапазон. Незначительное изменение, к тому же неоднозначное. Врят ли Вы сможете увидеть, что Falcon использует минимальный или максимальный пределы объёма памяти, выделяемые под кэш, а не какое-то фиксированное значение. Если у меня есть 8 GB оперативной памяти, и я решил выделить под кэш 4 GB, то какое мне дело как mySQL будет управлять этими гигабайтами? И какой диапазон в этом случае нужно задать, чтобы кэширование было максимально эффективным — 3 ГБ или может быть 3.8 ГБ? В чём разница? Не будет ли чистка памяти запускаться слишком часто, если я задам близкие друг к другу минимальное и максимальное значения? Документация на этот счёт ничего конкретного не говорит.
- Небольшое количество настроек. Возможно, это несколько противоречит предыдущему пункту, но давайте посмотрим на это с точки зрения экспериментатора, а не обычного пользователя. Я бы хотел иметь большее количество настроек, чтобы настроить поведения базы данных под свои нужды. Я люблю принцип «не требует администрирования», и я бы хотел, чтобы база данных делала всё за меня, но для начала она должна доказать, что может это сделать лучше меня, а достичь этого можно только дав сравнить производительность системы на двух конфигурациях: автоматической и той, что я сделал сам собственными руками. До сих пор я не видел автоматических конфигураций, которые могли бы подойти под все режимы нагрузки на сервер — они могут прекрасно справляться с «типичными» режимами, но быть неэффективными при работе в нестандартных режимах, что в случае mySQL происходит довольно часто.
- Небольшие индексы. Индексы, занимающие мало места — это здорово. С другой стороны огромные индексы — это залог высокой производительности InnoDB. Поэтому на самом деле нужно будет посмотреть, насколько «небольшими» получатся индексы и насколько от это пострадает (или насколько выиграет) производительность системы. В релизе, на котором мы тестировали Falcon, команда «SHOW TABLE STATUS» не показывает размер индекса, поэтому оценить это нововведение затруднительно.
- Отсутствие команды «USING INDEX». Falcon вынужден всегда просматривать содержимое строки, даже если выборка извлекает только данные, содержащиеся в индексе. Я считаю это огромной проблемой, т.к. «покрывающие индексы» (covering index) позволяют существенно повысить производительность многих запросов.
- Упорядоченное чтение данных. В отличает от других движков, которые читают данные в соответствии с проходов по индексу, Falcon может оптимизировать этот процесс, пройдя сначала весь индекс (а может быть и по объединению индексов), и только после этого считывая уже упорядоченные данные. Это может сильно помочь, если Вам приходятся выбирать большое количество данных из таблицы. Однако я считаю, что такая возможность должна быть вынесена выше уровня движков mySQL (Storage Engine level) — чтение строк в порядке их физического расположение может быть реализовано для большинства типов движков, и уж точно — для InnoDB и InnoDB. Помню, что подобные планы звучали некоторое время назад, но делается ли что-либо в этом направлении мне не известно.
- Отсутствие поддержки кластеризации индекса. В отличие от InnoDB Falcon не группирует данные ни по Primary-индексу, ни по каким-либо другим индексам. В каких-то случаях это хорошо, в каких-то — плохо. Многие уже существующие приложения опираются на данную особенность InnoDB, поэтому данную особенность скорее можно к отрицательной стороне нового движка. По-моему данную возможность следовало бы сделать необязательной (например так, как это уже сделано в InnoDB — Вы можете создать скрытый Primary-ключ), однако такая возможность была бы плохо портируемой, т.к. Вам пришлось бы удалять Primary-ключ и т.д. В некоторых презентациях утверждается, что Falcon не нуждается в кластеризации индексов, т.к. использует специальную оптимизацию при их чтении. Однако это не тот случай. Если Вам необходимо прочитать небольшую часть Primary-ключа в InnoDB, то mySQL просмотрит всего несколько страниц, в то время как Falcon'у (в случае, если строки были добавлены в разное время и «раскиданы» по индексу) понадобится прочитать значительно больше страниц. К примеру, подумайте о типичных случаях применения кластеризации в InnoDB — почтовые ящики пользователей, в таблице с кластеризованным ключом (user_id,message_id).
- Сжатие строк. В Falcon реализован механизм быстрого сжатия данных в строках; к примеру, он использует ровно столько байт для значения типа integer, сколько требуется (а не сколько отведено для максимума), не сохраняет значения в колонке, если оно равно значению по умолчанию и т.д. Это замечательное нововведение, хотя и может вызвать некоторые трудностни: например, если Вы захотите поменять значение по умолчанию у какого-либо столбца, то Falcon должен будет перестроить всю таблицу, а не просто изменить её мета-данные (впрочем, эта возможность тоже не реализована в mySQL). Будет очень интересно посмотреть на соотношение «производительность — степень сжатия» у Falcon относительно прозрачного gzip-сжатия, реализованного в InnoDB.
- Табличная область уровня базы данных. Как Вы уже могли заметить в текущем релизе, Falcon создаёт отдельную табличную область и собственные логи для каждой базы данных. При стандартном mySQL подходе с созданием отдельной директории для каждой таблицы это может вызвать определённые трудности: если транзакция порождает несколько баз данных, то при её фиксации (т.е. при commit'е) Вам нужно будет обновить несколько логов. К тому же для синхронизации фиксаций транзакции в нескольких базах данных нужно будет либо использовать XA-транзакции (что весьма ресурсоёмко), либо может возникнуть ситуация, когда транзакция была зафиксирована в одной базе, но не в другой. Ещё одна проблема заключается в том, что последовательные «снимки» сервера будут последовательными только в пределах базы данных, поэтому если Вы начнёте транзакцию, обращающуюся к базе A, а несколько позже к базе B, то в базе данных B Вы увидите строки, которые были добавлены/обновлены уже после того, как началась транзакция.
- Режимы изоляции. В настоящее время Falcon поддерживает очень ограниченный набор изоляций запросов (в том числе не поддерживает запросы вида SELECT FOR UPDATE). Движок Falcon использует Оптимистичную модель блокировок при конкурентном доступ, что может повлечь за собой проблемы в условиях работы сервера с большим количеством UPDATE-запросов, которые вынуждены будут ждать освобождения блокировки на уровне строки. К тому же сейчас возникает забавная ситуация, когда движок ожидает окончания транзакции с UPDATE-запросом, после выполнения которой всё равно аварийно останавливается с ошибкой «ERROR 1020 (HY000): Record has changed since last read in table ‘t’» («Запись была изменена с момента последнего чтения таблицы»). Нельзя сказать, что такое поведение движка хуже, чем в случае с InnoDB, оно — другое, и это нужно учитывать при разработке приложений на основе Falcon. Документации по данному вопросу также маловато.
- Нет защиты от частичной записи в страницу. Это означает, что если запись в данную страницу была не атомарной, т.е. при изменении какой-то части страницы другая её часть осталась старой, то таблица может быть неисправимо повреждена. Именно поэтому InnoDB использует буфер двойной записи (прим.: InnoDB перед записью страниц в файл сначала записывает их в смежный участок табличной области, поэтому после сбоя при восстановлении InnoDB сможет найти копию данных в смежном участке). Джим не считает это существенной проблемой, хотя я не один раз сталкивался с ней при использовании движка InnoDB до введения в него буфера двойной записи. Разговаривая с разработчиками PostgreSQL и Oracle я пришёл к выводу, что все они в курсе данной проблемы и имеют собственные методы борьбы с ней, поэтому я не слишком понимаю, почему Джим в неё не слишком верит.
- Данные на диске меняются только после фиксации транзакции. Движок InnoDB меняет данные в файлах сразу после того, как была отдана такая команда, Falcon же — только после commit'а. У этого момента есть как положительные стороны (например, отсутствие долгих откатов при больших транзакциях), так и отрицательные (повышенные требования к объёму оперативной памяти). Я согласен, что большинство транзакций — небольшие, однако в случае, например, пакетной обработки чего-либо Вам может понадобится либо увеличить объём памяти, либо переписать запросы так, чтобы фиксация транзакций происходила чаще.
- Оптимизация работы с данными типа BLOB. Джим любит BLOB'ы как своих собственных детей, поэтому Falcon оптимизирован для работы с ними, например, реализована прямая запись BLOB в базу данных. Будет интересно посмотреть на результаты этой оптимизации в реальности.
Ещё один интересный момент, о котором я не нашёл никакой информации в документации — обработка фрагментации: что происходит при обновлении строк — обновлённые строки хранятся в том же месте или в новом? Разделяются ли строки на несколько частей, как это происходит в InnoDB, или всегда хранятся компактно? Это весьма важные вопросы относительно I/O производительности движка.
PS от переводчика: к настоящему времи движок находится в разработке уже более 4 лет, а «бета» ещё настолько сырая, что Falcon проигрывает практически по всем тестам не только своему «прародителю» InnoDB, но и InnoDB. В то же время движок Maria развивается куда быстрее (этот движок является развитием InnoDB, а не InnoDB, хотя и является полностью транзакционным и обладает всеми свойствами ACID), поэтому вовсе не понятно, зачем mySQL разрабатывает столько движков одновременно, которые фактически равны друг другу по возможностям.