Обнаружение таблиц
В MariaDB не всегда требуется выполнять явное CREATE TABLE заявление для отображения таблицы. Иногда таблица может уже существовать в хранилище, но сервер не знает об этом, потому что нет файла .frm для этой таблицы. Это может произойти по разным причинам; например, для кластерного движка таблица могла быть создана в кластере другим узлом сервера MariaDB. Или для движка, поддерживающего передачу таблиц, файл таблицы мог быть просто скопирован в каталог данных MariaDB. Но какова бы ни была причина, существует механизм, позволяющий движку сообщить серверу, что таблица существует. Этот механизм называется обнаружением таблиц, и если движок хочет, чтобы сервер обнаружил свои таблицы, он должен поддерживать API обнаружения таблиц.
Существует два разных вида обнаружения таблиц — полностью автоматическое и с участием пользователя. В первом случае движок может автоматически обнаруживать таблицу всякий раз, когда требуется SQL-запрос. В MariaDB движки Archive и Sequence поддерживают этот тип обнаружения. Например, можно скопировать файл t1.ARZ в каталог базы данных и сразу начать его использование — соответствующий файл .frm будет создан автоматически. Или можно выбрать данные из таблицы, например, из таблицы seq_1_to_10 без явного CREATE TABLE заявления.
Во втором, управляемом пользователем, случае движок не имеет достаточной информации для самостоятельного обнаружения таблицы. Но он может обнаружить структуру таблицы, если пользователь предоставит определённую дополнительную информацию. В этом случае всё ещё необходимо явное CREATE TABLE заявление, но оно должно содержать только имя таблицы и атрибуты таблицы, а не структуру таблицы. В MariaDB движок хранилища FederatedX поддерживает это. При создании таблицы необходимо только указать атрибут CONNECTION , а структуру таблицы — поля и индексы — движок предоставит автоматически.
Автоматическое обнаружение
Что касается автоматического обнаружения таблиц, то с точки зрения сервера таблицы могут появляться, исчезать или изменять структуру в любое время. Таким образом, серверу необходимо иметь возможность запрашивать, существует ли заданная таблица и какова её структура. Необходимо уведомлять сервер о внесении изменений в структуру таблицы вне сервера. И необходимо иметь возможность получать список всех (неизвестных серверу) таблиц для запросов, таких как SHOW TABLES. Сервер выполняет всё это, вызывая определённые методы handlerton.
const char **tablefile_extensions;
int (*discover_table_names)(handlerton *hton, LEX_STRING *db, MY_DIR *dir,
discovered_list *result);
int (*discover_table_existence)(handlerton *hton, const char *db,
const char *table_name);
int (*discover_table)(handlerton *hton, THD* thd, TABLE_SHARE *share);
handlerton::tablefile_extensions
Движки, хранящие таблицы в отдельных файлах (одна таблица может занимать несколько файлов с различными расширениями, но с одинаковым базовым именем файла), должны хранить список возможных расширений в члене tablefile_extensions handlerton (ранее этот список возвращался методом handler::bas_ext()). Это значительно упростит реализацию обнаружения для этих движков, как вы увидите ниже.
handlerton::discover_table_names()
Когда пользователь запрашивает список таблиц в определённой базе данных — например, используя SHOW TABLES или выбирая из INFORMATION_SCHEMA.TABLES — сервер вызывает метод discover_table_names() handlerton. Для удобства этот метод, помимо имени базы данных, получает список всех файлов в каталоге этой базы данных, чтобы движок мог искать файлы таблиц, не выполняя операций ввода-вывода файловой системы. Все обнаруженные таблицы должны быть добавлены в объект-коллектор result. Он определён как
class discovered_list
{
public:
bool add_table(const char *tname, size_t tlen);
bool add_file(const char *fname);
};
и движок должен вызвать result->add_table() или result->add_file() для каждой обнаруженной таблицы (используйте add_file(), если имя для добавления находится в кодировке имён файлов MariaDB, и add_table(), если это истинное имя таблицы, как показано в SHOW TABLES).
Если движок основан на файлах, то есть у него есть непустой список в tablefile_extensions, этот метод является необязательным. Для любого файлового движка, не реализующего discover_table_names(), MariaDB автоматически обнаружит список всех таблиц этого движка, найдя файлы с расширением tablefile_extensions[0].
handlerton::discover_table_existence()
В некоторых редких случаях MariaDB необходимо знать, существует ли заданная таблица, но не особенно интересует её структура (например, при выполнении DROP TABLE заявления). В этих случаях сервер использует метод discover_table_existence(), чтобы узнать, существует ли в движке таблица с заданным именем.
Этот метод является необязательным. Для движка, который его не реализует, MariaDB попытается найти файлы с расширением tablefile_extensions[0], если это возможно. Но если движок не основан на файлах, MariaDB будет использовать метод discover_table() для выполнения полного обнаружения таблицы. Хотя это позволит правильно определить, существует ли таблица, полное обнаружение обычно медленнее, чем проверка существования. Другими словами, движки, не основанные на файлах, могут захотеть поддерживать метод discover_table_existence() в качестве полезной оптимизации.
handlerton::discover_table()
Это основной метод обнаружения таблиц, его сердцевина. Сервер вызывает его, когда хочет использовать таблицу. Метод discover_table() получает структуру TABLE_SHARE, которая не полностью инициализирована — заполнены только имя таблицы и базы данных (и путь к файлу таблицы). Он должен инициализировать эту структуру TABLE_SHARE с желаемой структурой таблицы.
MariaDB предоставляет удобные и простые в использовании вспомогательные функции, которые позволяют движку инициализировать структуру TABLE_SHARE с минимальными усилиями. Это методы TABLE_SHARE init_from_binary_frm_image() и init_from_sql_statement_string().
TABLE_SHARE::init_from_binary_frm_image()
Этот метод используется движками, использующими "frm передачу" — такими как Archive или NDB Cluster в MySQL. Движок frm передачи считывает файл frm для данной таблицы точно так, как он был сгенерирован сервером, и хранит его в памяти. Позже он может обнаружить структуру таблицы, используя этот самый образ frm. В этом смысле отдельный файл frm в каталоге базы данных становится избыточным, потому что его копия хранится в движке.
TABLE_SHARE::init_from_sql_statement_string()
Этот метод позволяет инициализировать TABLE_SHARE с использованием стандартного синтаксиса SQL CREATE TABLE.
TABLE_SHARE::read_frm_image()
Движки, использующие передачу frm, должны получить образ frm, соответствующий определённой таблице (обычно в методе handler::create()). Они делают это с помощью метода read_frm_image(). Он возвращает выделенный буфер с двоичным изображением frm, которое движок может использовать как нужно.
TABLE_SHARE::free_frm_image()
Образ frm, возвращённый методом read_frm_image(), должен быть освобождён с помощью free_frm_image().
HA_ERR_TABLE_DEF_CHANGED
Одним из последствий автоматического обнаружения является то, что определение таблицы может измениться, когда сервер этого не ожидает. Например, между двумя SELECT запросами. Если это произойдёт, если движок обнаружит, что сервер использует устаревшую версию определения таблицы, он должен вернуть ошибку обработчика HA_ERR_TABLE_DEF_CHANGED. В зависимости от того, когда во время обработки запроса произошла эта ошибка, MariaDB либо повторно обнаружит таблицу и выполнит запрос с правильной структурой таблицы, либо прервёт запрос и вернёт пользователю сообщение об ошибке.
TABLE_SHARE::tabledef_version
Предыдущий абзац не раскрывает одного важного вопроса — как движок может узнать, что сервер использует устаревшее определение таблицы? Ответ — проверяя версию определения таблицы tabledef_version. Каждая таблица получает уникальное значение tabledef_version. Обычно оно генерируется автоматически при создании таблицы. При обнаружении таблицы движок может принудительно задать определённое значение tabledef_version (просто установив его в TABLE_SHARE перед вызовом методов init_from_binary_frm_image() или init_from_sql_statement_string()).
Теперь движок может сравнить версию определения таблицы, которую использует сервер (к которой можно получить доступ из любого метода обработчика как this->table->s->tabledef_version), с версией фактического определения таблицы. Если они отличаются — это HA_ERR_TABLE_DEF_CHANGED.
Обнаружение с помощью пользователя
Обнаружение с участием пользователя значительно проще с точки зрения сервера, более контролируемо. Таблица не может появляться или исчезать по желанию, для её изменения всё ещё требуются явные DDL-запросы. Существует только один новый метод handlerton, который сервер использует для обнаружения структуры таблицы, когда пользователь выполняет явное CREATE TABLE заявление без объявления столбцов или индексов.
int (*discover_table_structure)(handlerton *hton, THD* thd,
TABLE_SHARE *share, HA_CREATE_INFO *info);
API обнаружения с помощью пользователя практически независим от API автоматического обнаружения. Движок может реализовать любой из них или оба (или ни один); нет требования поддерживать автоматическое обнаружение, если необходимо только обнаружение с помощью пользователя.
handlerton::discover_table_structure()
Подобно методу discover_table(), метод обработчика discover_table_structure() получает частично инициализированную структуру TABLE_SHARE с заполненными именем таблицы, именем базы данных и путём к файлам таблицы, но без структуры таблицы. В отличие от discover_table(), здесь структура TABLE_SHARE содержит все атрибуты таблицы, определённые движком в структуре TABLE_SHARE::option_struct. Основываясь на значениях этих атрибутов, метод discover_table_structure() должен инициализировать структуру TABLE_SHARE с желаемым набором полей и ключей. Для этого он может использовать вспомогательные методы TABLE_SHARE init_from_binary_frm_image() и init_from_sql_statement_string().
Роль файлов .frm
До того, как была реализована обнаружение таблиц, MariaDB использовала файлы .frm для хранения определения таблицы. Но теперь движок может хранить определение таблицы (если, конечно, движок поддерживает автоматическое обнаружение), и файлы .frm становятся излишними. Тем не менее, сервер может использовать файлы .frm для такого движка — но они больше не являются единственным источником определения таблицы. Теперь файлы .frm являются просто кэшем определения таблицы, в то время как оригинальное и авторитетное определение таблицы хранится в движке. Как и любой кэш, его цель — уменьшить попытки обнаружения таблицы. Движок решает, имеет ли смысл кэшировать определение таблицы в файле .frm или нет (см. второй аргумент для TABLE_SHARE::init_from_binary_frm_image()). Например, движок Archive использует кэш .frm, а движок Sequence — нет. Другими словами, MariaDB создаёт файлы .frm для таблиц Archive, но не для таблиц Sequence.
Кэш полностью прозрачен для пользователя; MariaDB гарантирует, что всегда хранит фактическое определение таблицы и автоматически делает файл .frm недействительным, когда он становится устаревшим. Это может произойти, например, если пользователь скопирует новую таблицу Archive в директорию данных и забудет удалить файл .frm старой таблицы с таким же именем.
© 2023 MariaDB
Licensed under the Creative Commons Attribution 3.0 Unported License and the GNU Free Documentation License.
https://mariadb.com/kb/en/table-discovery/