14.9.1.5 Как работает сжатие для таблиц InnoDB
В этом разделе описаны некоторые внутренние детали реализации для таблиц InnoDB. Представленная здесь информация может быть полезна при настройке производительности, но не является необходимой для базового использования сжатия.
Алгоритмы сжатия
Некоторые операционные системы реализуют сжатие на уровне файловой системы. Файлы обычно делятся на блоки фиксированного размера, которые сжимаются в блоки переменного размера, что легко приводит к фрагментации. Каждый раз, когда что-то внутри блока изменяется, весь блок пересжимается перед записью на диск. Эти свойства делают эту технику сжатия непригодной для использования в системе управления базами данных с интенсивными операциями обновления.
MySQL реализует сжатие с помощью известной библиотеки zlib, которая реализует алгоритм сжатия LZ77. Этот алгоритм сжатия является зрелым, надежным и эффективным как в отношении использования процессора, так и в уменьшении размера данных. Алгоритм является «безошибочным», поэтому исходные несжатые данные всегда могут быть восстановлены из сжатой формы. LZ77 сжатие работает, обнаруживая последовательности данных, которые повторяются в сжимаемых данных. Образцы значений в ваших данных определяют, насколько хорошо они сжимаются, но типичные пользовательские данные часто сжимаются на 50% или более.
До MySQL 5.7.24, InnoDB поддерживает библиотеку zlib до версии 1.2.3. В MySQL 5.7.24 и более поздних версиях, InnoDB поддерживает библиотеку zlib до версии 1.2.11.
В отличие от сжатия, выполняемого приложением, или функций сжатия некоторых других систем управления базами данных, сжатие InnoDB применяется как к пользовательским данным, так и к индексам. Во многих случаях индексы могут составлять 40-50% или более от общего размера базы данных, поэтому это различие является существенным. Когда сжатие работает хорошо для набора данных, размер файлов данных InnoDB (пространства таблиц или файлов .ibd) составляет от 25% до 50% от несжатого размера или может быть меньше. В зависимости от , это меньшая база данных, в свою очередь, может привести к снижению ввода-вывода и увеличению пропускной способности при небольших затратах на увеличение использования ЦП. Вы можете отрегулировать баланс между уровнем сжатия и нагрузкой на ЦП, изменив параметр конфигурации innodb_compression_level.
Хранение данных InnoDB и сжатие
Все пользовательские данные в таблицах InnoDB хранятся в страницах, составляющих индекс (the ). В некоторых других системах баз данных этот тип индекса называется «таблицей с индексом». Каждая строка в узле индекса содержит значения (указанные пользователем или сгенерированные системой) и все остальные столбцы таблицы.
в таблицах InnoDB также являются B-деревьями, содержащими пары значений: ключ индекса и указатель на строку в кластеризованном индексе. Указатель фактически является значением первичного ключа таблицы, который используется для доступа к кластеризованному индексу, если требуются столбцы, отличные от ключа индекса и первичного ключа. Записи вторичного индекса всегда должны помещаться на одной странице B-дерева.
Сжатие узлов B-дерева (как кластеризованных, так и вторичных индексов) обрабатывается иначе, чем сжатие используемых для хранения длинных VARCHAR, BLOB или TEXT столбцов, как описано в следующих разделах.
Сжатие страниц B-дерева
Поскольку они часто обновляются, страницы B-дерева требуют специального обращения. Важно свести к минимуму количество раз разделения узлов B-дерева, а также свести к минимуму необходимость разархивирования и перезаписи их содержимого.
Один из методов, используемых MySQL, заключается в хранении некоторой системной информации в узле B-дерева в несжатом виде, что облегчает некоторые обновления на месте. Например, это позволяет отмечать и удалять строки без каких-либо операций сжатия.
Кроме того, MySQL пытается избежать ненужного разархивирования и пересжатия страниц индекса при их изменении. Внутри каждой страницы B-дерева система сохраняет несжатый «журнал изменений», чтобы записывать изменения, внесенные в страницу. Обновления и вставки небольших записей могут быть записаны в этот журнал изменений без необходимости полной реконструкции всей страницы.
Когда пространство для журнала изменений заканчивается, InnoDB разархивирует страницу, применяет изменения и пересжимает страницу. Если пересжатие завершается неудачно (ситуация, известная как ), узлы B-дерева разделяются, и процесс повторяется до тех пор, пока обновление или вставка не завершатся успешно.
Чтобы избежать частых сбоев сжатия в задачах с интенсивной записью, таких как для приложений, MySQL иногда резервирует некоторое пустое место (заполнение) на странице, так что журнал изменений заполняется раньше, и страница пересжимается, пока на ней еще достаточно места, чтобы избежать ее разделения. Количество заполненного места на каждой странице варьируется, поскольку система отслеживает частоту разделения страниц. На загруженном сервере, выполняющем частые записи в сжатые таблицы, вы можете отрегулировать параметры конфигурации innodb_compression_failure_threshold_pct и innodb_compression_pad_pct_max, чтобы настроить эту механику.
Как правило, MySQL требует, чтобы каждая страница B-дерева в таблице InnoDB могла вместить не менее двух записей. Для сжатых таблиц это требование было смягчено. Листовые страницы узлов B-дерева (как первичного ключа, так и вторичных индексов) должны вмещать только одну запись, но эта запись должна помещаться в несжатом виде в журнал изменений на страницу. Если innodb_strict_mode ON, MySQL проверяет максимальный размер строки во время CREATE TABLE или CREATE INDEX. Если строка не помещается, выдается следующее сообщение об ошибке: ERROR
HY000: Too big row.
Если вы создаёте таблицу, когда innodb_strict_mode выключено, и последующее INSERT или UPDATE утверждение пытается создать запись индекса, которая не помещается в размер сжатой страницы, операция завершается неудачно с ERROR 42000: Row size too
large. (В этом сообщении об ошибке не указан индекс, для которого запись слишком велика, или не указана длина записи индекса или максимальный размер записи на конкретной странице индекса.) Для решения этой проблемы перестройте таблицу с помощью ALTER TABLE и выберите больший размер сжатой страницы (KEY_BLOCK_SIZE), укоротите любые префиксные индексы столбцов или отключите сжатие полностью с помощью ROW_FORMAT=DYNAMIC или ROW_FORMAT=COMPACT.
innodb_strict_mode не применяется к общим пространствам таблиц, которые также поддерживают сжатые таблицы. Правила управления пространствами таблиц для общих пространств таблиц строго соблюдаются независимо от innodb_strict_mode. Более подробную информацию см. в Разделе 13.1.19, «CREATE TABLESPACE Statement».
Сжатие столбцов BLOB, VARCHAR и TEXT
В таблице InnoDB, BLOB, VARCHAR и TEXT столбцы, которые не являются частью первичного ключа, могут храниться в отдельном выделенном . Мы называем эти столбцы . Их значения хранятся в односвязных списках страниц переполнения.
Для таблиц, созданных в ROW_FORMAT=DYNAMIC или ROW_FORMAT=COMPRESSED, значения BLOB, TEXT или VARCHAR столбцов могут храниться полностью вне страницы, в зависимости от их длины и длины всей строки. Для столбцов, хранящихся вне страницы, запись кластеризованного индекса содержит только 20-байтовые указатели на страницы переполнения, по одному на столбец. Хранение столбцов вне страницы зависит от размера страницы и общего размера строки. Если строка слишком длинная, чтобы поместиться полностью на странице кластеризованного индекса, MySQL выбирает самые длинные столбцы для хранения вне страницы до тех пор, пока строка не поместится на странице кластеризованного индекса. Как отмечалось выше, если строка не помещается на сжатой странице, возникает ошибка.
Для таблиц, созданных в ROW_FORMAT=DYNAMIC или ROW_FORMAT=COMPRESSED, TEXT и BLOB столбцы, которые меньше или равны 40 байтам, всегда хранятся в строке.
Таблицы, созданные в более ранних версиях MySQL, используют формат файла , который поддерживает только ROW_FORMAT=REDUNDANT и ROW_FORMAT=COMPACT. В этих форматах MySQL сохраняет первые 768 байт BLOB, VARCHAR и TEXT столбцов в записи кластеризованного индекса вместе с первичным ключом. За префиксом длиной 768 байт следует 20-байтовый указатель на страницы переполнения, содержащие остальную часть значения столбца.
Когда таблица находится в формате COMPRESSED, все данные, записанные на страницы переполнения, сжимаются «как есть»; то есть MySQL применяет алгоритм сжатия zlib ко всему элементу данных. Помимо данных, сжатые страницы переполнения содержат несжатый заголовок и хвост, включающий контрольную сумму страницы и ссылку на следующую страницу переполнения, среди прочего. Поэтому существенное сокращение размера хранилища может быть получено для более длинных BLOB, TEXT или VARCHAR столбцов, если данные легко сжимаются, что часто бывает с текстовыми данными. Данные изображений, такие как JPEG, обычно уже сжаты, поэтому не получают большой выгоды от хранения в сжатой таблице; двойное сжатие может тратить циклы процессора практически без экономии места.
Страницы с переполнением имеют тот же размер, что и другие страницы. Строка, содержащая десять столбцов, сохраненных вне страницы, занимает десять страниц переполнения, даже если общая длина столбцов составляет всего 8 КБ. В нескомпрессированной таблице десять нескомпрессированных страниц переполнения занимают 160 КБ. В сжатой таблице с размером страницы 8 КБ они занимают всего 80 КБ. Поэтому часто более эффективно использовать сжатый формат таблицы для таблиц со значениями длинных столбцов.
Для табличных пространств использование сжатого размера страницы 16 КБ может снизить затраты на хранение и ввод-вывод для столбцов BLOB, VARCHAR или TEXT, так как такие данные часто хорошо сжимаются и, следовательно, могут потребовать меньше страниц переполнения, даже если узлы B-дерева занимают столько же страниц, как и в нескомпрессированном формате. Общие табличные пространства не поддерживают сжатый размер страницы 16 КБ (KEY_BLOCK_SIZE). Дополнительную информацию см. в разделе 14.6.3.3 «Общие табличные пространства».
Сжатие и кэш-память InnoDB Buffer Pool
В таблице со сжатыми InnoDB данными каждая сжатая страница (независимо от того, 1К, 2К, 4К или 8К) соответствует нескомпрессированной странице размером 16 КБ (или меньше, если innodb_page_size задано). Для доступа к данным на странице MySQL читает сжатую страницу с диска, если она еще не находится в памяти, а затем распаковывает страницу до исходного формата. В этом разделе описывается, как InnoDB управляет кэш-памятью буфера с точки зрения страниц сжатых таблиц.
Для минимизации операций ввода-вывода и уменьшения необходимости распаковки страницы кэш-память буфера иногда содержит как сжатую, так и нескомпрессированную форму страницы базы данных. Чтобы освободить место для других необходимых страниц базы данных, MySQL может извлечь из кэш-памяти буфера нескомпрессированную страницу, оставив сжатую страницу в памяти. Или, если страница долгое время не использовалась, сжатый вид страницы может быть записан на диск, чтобы освободить место для других данных. Таким образом, в любой момент кэш-память буфера может содержать как сжатую, так и нескомпрессированную форму страницы, или только сжатую форму страницы, или ни одну из них.
MySQL отслеживает, какие страницы хранить в памяти, а какие удалить, используя список наименее недавно используемых (LRU), так что данные, которые часто используются, остаются в памяти. При доступе к сжатым таблицам MySQL использует адаптивный алгоритм LRU для достижения подходящего баланса между сжатыми и нескомпрессированными страницами в памяти. Этот адаптивный алгоритм чувствителен к тому, работает ли система в режиме ожидания ввода-вывода или режиме ожидания ЦП. Цель состоит в том, чтобы избежать слишком длительного времени обработки распаковки страниц, когда ЦП занят, и избежать избыточных операций ввода-вывода, когда ЦП имеет свободные циклы, которые могут быть использованы для распаковки сжатых страниц (которые могут уже находиться в памяти). Когда система ограничена операциями ввода-вывода, алгоритм предпочитает удалять нескомпрессированную копию страницы, а не обе копии, чтобы освободить больше места для других страниц диска, которые должны быть в памяти. Когда система ограничена ЦП, MySQL предпочитает удалить как сжатую, так и нескомпрессированную страницу, чтобы больше памяти можно было использовать для «горячих» страниц и уменьшить необходимость распаковки данных в памяти только в сжатом формате.
Сжатие и журнал InnoDB redo
Перед записью сжатой страницы на диск MySQL записывает копию страницы в журнал redo (если она была пересжата с момента последней записи в базу данных). Это делается для того, чтобы журналы redo были пригодны для восстановления, даже в маловероятном случае, если библиотека zlib будет обновлена, и это изменение приведет к проблеме совместимости со сжатыми данными. Поэтому можно ожидать некоторого увеличения размера журнала или необходимости более частых контрольных точек при использовании сжатия. Величина увеличения размера файла журнала или частоты контрольных точек зависит от того, сколько раз сжатые страницы изменялись таким образом, что потребовалась реорганизация и пересжатие.
Для сжатых таблиц требуется формат файла . Чтобы создать сжатую таблицу в табличном пространстве с файлом на таблицу, необходимо включить innodb_file_per_table и установить innodb_file_format в . При создании сжатой таблицы в общем табличном пространстве нет зависимости от параметра innodb_file_format. Дополнительную информацию см. в разделе 14.6.3.3 «Общие табличные пространства». Продукт поддерживает формат файла .
© 2025 Oracle
Licensed under the GPLv2 License.