Как найти и исправить фрагментированные таблицы MySQL

27

Я использовал MySQLTuner, который указал, что некоторые таблицы были фрагментированы. я использовал

mysqlcheck --optimize -A

оптимизировать все таблицы. Исправлены некоторые таблицы, но MySQLTuner все еще находит фрагментированные 19 таблиц. Как узнать, какие таблицы нуждаются в дефрагментации? Возможно, OPTIMIZE TABLE будет работать там, где mysqlcheck не работал? Или что еще мне попробовать?

curiouscat
источник
1
У меня похожая проблема. Я устанавливаю новую БД с MySQL 5.5, и некоторые таблицы InnoDB никогда не фрагментируются. Мне интересно, если проверка Data_free (показанная в ответе KayakJim) некорректна с таблицами InnoDB.
Что

Ответы:

38

краткий ответ:

select  ENGINE, TABLE_NAME,Round( DATA_LENGTH/1024/1024) as data_length , round(INDEX_LENGTH/1024/1024) as index_length, round(DATA_FREE/ 1024/1024) as data_free from information_schema.tables  where  DATA_FREE > 0;

Ответ "Вы должны знать"

Прежде всего вы должны понимать, что таблицы Mysql фрагментируются при обновлении строки, так что это нормальная ситуация. Когда таблица создана, скажем, импортирована с использованием дампа с данными, все строки сохраняются без фрагментации на многих страницах с фиксированным размером. Когда вы обновляете строку переменной длины, страница, содержащая эту строку, делится на две или более страниц для хранения изменений, и эти две новые (или более) страницы содержат пустые места, заполняющие неиспользуемое пространство.

Это не влияет на производительность, если, конечно, фрагментация не растет слишком сильно. Что слишком много фрагментации, давайте посмотрим на запрос, который вы ищете:

  select  ENGINE, TABLE_NAME,Round( DATA_LENGTH/1024/1024) as data_length , round(INDEX_LENGTH/1024/1024) as index_length, round(DATA_FREE/ 1024/1024) as data_free from information_schema.tables  where  DATA_FREE > 0;

DATA_LENGTH и INDEX_LENGTH - это пространство, которое используют ваши данные и индексы, а DATA_FREE - это общее количество байтов, неиспользуемых на всех страницах таблицы (фрагментация).

Вот пример реальной производственной таблицы

| ENGINE | TABLE_NAME               | data_length | index_length | data_free |
| InnoDB | comments                 |         896 |          316 |         5 |

В этом случае у нас есть Таблица, использующая (896 + 316) = 1212 МБ, и у нас есть свободное пространство 5 МБ. Это означает «коэффициент фрагментации»:

5/1212 = 0.0041

... Что является действительно низким "коэффициентом фрагментации".

Я работал с таблицами с коэффициентом около 0,2 (что означает 20% пробелов) и никогда не замечал замедления запросов, даже если я оптимизирую таблицу, производительность остается той же. Но применение таблицы оптимизации на столе размером 800 МБ занимает много времени и блокирует таблицу на несколько минут, что нецелесообразно для производства.

Итак, если вы считаете, что вы выиграли в производительности и потратили время на оптимизацию таблицы, я предпочитаю НЕ ОПТИМИЗИРОВАТЬ.

Если вы считаете, что для хранения лучше, посмотрите соотношение и посмотрите, сколько места вы сможете сэкономить при оптимизации. Обычно это не так уж много, поэтому я предпочитаю НЕ ОПТИМИЗИРОВАТЬ.

И если вы оптимизируете, следующее обновление создаст пробелы, разделив страницу на две или более. Но быстрее обновить фрагментированную таблицу, чем не фрагментированную, потому что, если таблица фрагментирована, обновление строки не обязательно разделит страницу.

Я надеюсь, это поможет вам.

Фелипе Рохас
источник
1
Хотя это ответ от нескольких лет назад, я подумал, что должен указать, что data_free - это статистика для всего табличного пространства, а не для соответствующей таблицы. Если вы храните несколько таблиц вместе в одном табличном пространстве, data_free может ввести вас в заблуждение, полагая, что таблица нуждается в дефрагментации, когда это просто означает, что в табличном пространстве есть свободные экстенты. Запуск таблицы оптимизации не уменьшит количество свободных экстентов. Дефрагментация таблицы может даже увеличить свободные экстенты.
Билл Карвин
14

Просто, чтобы добавить к ответу Фелипе-Рохаса, вы можете рассчитать соотношение фрагментов как часть запроса:

select ENGINE,
  concat(TABLE_SCHEMA, '.', TABLE_NAME) as table_name,
  round(DATA_LENGTH/1024/1024, 2) as data_length,
  round(INDEX_LENGTH/1024/1024, 2) as index_length,
  round(DATA_FREE/1024/1024, 2) as data_free,
  (data_free/(index_length+data_length)) as frag_ratio
FROM information_schema.tables
WHERE DATA_FREE > 0
ORDER BY frag_ratio DESC;

Если таблица фрагментирована небольшим процентом (менее 5%?), То вы, вероятно, можете оставить ее в покое.

Что-нибудь большее, и вам нужно будет оценить, основываясь на вашем использовании базы данных, блокировках таблиц и т. Д., Насколько важно дефрагментировать таблицу.

sysadmiral
источник
2

Оптимизация таблицы действительно решит проблему, с которой вы столкнулись.

Если у вас есть только несколько баз данных, вы можете использовать PHPMyAdmin для просмотра всех ваших баз данных. Выберите таблицы с накладными расходами, а затем выберите для оптимизации.

Если у вас много баз данных, тогда, вероятно, предпочтительнее другой метод.

Я использую следующую настройку PHP-скрипта в cron для запуска каждый час.

$DB = new mysqli ('localhost', 'DbUser', 'DbPassword');
$results = $DB->query('show databases');
$allDbs = array();
while ($row = $results->fetch_array(MYSQLI_NUM))
{
    $allDbs[] = $row[0];
}
$results->close();
foreach ($allDbs as $dbName)
{
    if ($dbName != 'information_schema' && $dbName != 'mysql')
    {
        $DB->select_db($dbName);
        $results = $DB->query('SHOW TABLE STATUS WHERE Data_free > 0');
        if ($results->num_rows > 0)
        {
            while ($row = $results->fetch_assoc())
            {
                $DB->query('optimize table ' . $row['Name']);
            }
        }
        $results->close();
    }
}
$DB->close();
Демон Хаоса
источник
3
Я почти уверен, что mysqlcheck --optimize -Aэто то же самое, что и SQLOPTIMIZE TABLE <tablename>;
docwhat
2

Я наткнулся на эту страницу и нашел, что запросы Фелипе-Рохаса и сисадмирала очень полезны. Но в моем случае я выполнял запрос в phpMyAdmin WHM, и получение только TABLE_NAME было не таким полезным, поскольку база данных не была указана, и несколько баз данных имеют одинаковые имена таблиц. Таким образом, простое добавление TABLE_SCHEMAтакже предоставит этот столбец.

select  ENGINE, TABLE_SCHEMA, TABLE_NAME, Round( DATA_LENGTH/1024/1024) as data_length , round(INDEX_LENGTH/1024/1024) as index_length, round(DATA_FREE/ 1024/1024) as data_free, (data_free/(index_length+data_length)) as frag_ratio from information_schema.tables  where  DATA_FREE > 0 order by frag_ratio desc

Показывает БД

ENGINE  | TABLE_SCHEMA  | TABLE_NAME    | data_length   | index_length  | data_free | frag_ratio

InnoDB  | db_name       | db_table      | 0             | 0             | 8         | 170.6667

Чтобы «исправить», я использовал ссылку таблицы дефрагментации в phpMyAdmin для каждой из таблиц, что привело к значительному «frag_ratio», для которого выполняется phpMyAdmin:

ALTER TABLE `table_name` ENGINE = InnoDB;
Крис
источник
0

Таблицы, использующие InnoDB Engine MySQL, по сути, никогда не должны быть OPTIMIZEd.

Значение Data_freeиз одного information_schema.tablesили SHOW TABLE STATUSочень часто ненулевое, даже если вы думаете, что сделали все, что можете, дефрагментируя свои таблицы. Кроме того, эта метрика - только одна из нескольких фрагментаций, которые могут и происходят. (Кроме того, потраченное впустую пространство в блоках, списки отмены, индекс BTrees против данных BTrees и т. Д. И т. Д.

И innodb_file_per_tableусложняет использование Data_free. Если таблица находится в ibdata1, то Data_freeотносится ко всему табличному пространству; довольно бесполезный номер. Если таблица находится в своем собственном .ibdфайле, она может составлять несколько МБ или несколько процентов от размера таблицы, в зависимости от того, что больше.

Только если вы удалили много строк и не намерены пополнить таблицу, может это будет стоить бег OPTIMIZE TABLE.

PARTITIONsтакже показывает тревожное количество Data_free, так как каждый раздел обычно показывает 4-7 МБ "бесплатно". И это не пройдет.

Зачем дефрагментировать?

  • Вернуть пространство в ОС? Ну, вы могли бы достичь этого кратко, если бы у вас было innodb_file_per_table=1. Но когда вы добавляете строки, вы забираете их из ОС.
  • Чтобы ускорить доступ? Забудь это. Расположение блоков на диске является относительно случайным, и было в течение последних нескольких десятилетий. Полвека назад было несколько важно переставить блоки.
  • Чтобы сбалансировать BTrees? Так? Они быстро снова станут неуравновешенными. Стационарное состояние для BTrees, в которые вставляются случайным образом, составляет 69%. И это даже не учитывается Data_free.
  • MySQLTuner говорит? Этот продукт должен охлаждаться.

Историческая справка. Когда я помогал администраторам баз данных в основном с таблицами MyISAM, я обнаружил, возможно, 2 из 1000 таблиц, которым помогал ежемесячно OPTIMIZE . С тех пор я работал с тысячами таблиц InnoDB, но пока не нашел проблему с производительностью, которая могла бы помочь OPTIMIZE. (Конечно, были проблемы с дисковым пространством, которые OPTIMIZEмогли бы помочь, но это сложно, обычно у администратора базы данных недостаточно места для работы OPTIMIZE!)

Рик Джеймс
источник