Как я могу ускорить «показ столбцов» в MySQL?

7

Мое приложение зависит от запуска "Показать столбцы" для определенных таблиц. Выполнение занимает около 60 мс, в то время как все остальные наши запросы занимают менее 1 мс. Запросы information_schemaнапрямую еще медленнее.

База данных содержит около 250 баз данных, от 100 до 200 таблиц на базу данных (всего около 20 тыс. Таблиц).

  • Как я могу узнать, почему эти операции такие медленные?
  • Возможно, есть какие-то настройки, которые я могу изменить, чтобы они работали быстрее или для кэширования на стороне SQL?

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

mpen
источник
1
из интереса, в каком сценарии 60 мс будут слишком медленными, чтобы исследовать столбцы таблицы? Это не то, что вы должны делать каждый запрос
1
Что вы имеете в виду показать столбцы? Убирать имена столбцов из таблицы или печатать весь столбец? Если это имена ... почему бы вам просто не взять его один раз и не сохранить в приложении ... если это невозможно, почему бы вам просто не создать еще одну таблицу, которая содержит все столбцы на основе этой таблицы?
@Jaitsu: Нет, это не то, что мы должны делать, но так оно и есть. Устаревший код. Пока у меня не будет времени, чтобы привести его в порядок и сделать это правильно, я хочу посмотреть, смогу ли я ускорить его. У меня есть около 14 из них, которые запускают каждую страницу загрузки.
@FlorinStingaciu: Да, имена столбцов. Помещение их в другую таблицу может ускорить процесс, но это не будет синхронизировано, что сводит на нет всю цель запроса таблицы напрямую.
1
@ Мат: Неплохая идея. Проголосовал за миграцию в дба.

Ответы:

12

MySQL пересчитывает статистику таблиц для определенных операций, которые обращаются к INFORMATION_SCHEMAтаблицам ( SHOW COLUMNSэто просто удобный псевдоним для запросов INFORMATION_SCHEMA.COLUMNS). Задайте для свойства innodb_stats_on_metadata значение false, что предотвратит повторный расчет при запросе метаданных из таблицы.

SET GLOBAL innodb_stats_on_metadata=0;

и добавьте следующее к my.cnf

[mysqld]
innodb_stats_on_metadata = 0
Аарон Браун
источник
Я должен был упомянуть, что я на самом деле использую MyISAM. В любом случае попытался установить это, но это не принесло никакой пользы.
mpen
Вы рассматривали ALTER TABLE foo ENGINE = InnoDB? :) Есть ли веская причина для использования MyISAM?
Аарон Браун
Я думаю, что в основном это наследие. Я боюсь, что может случиться, если я попробую это; Не уверен, что все ФК выстроятся в линию. Я еще подумаю над этим.
mpen
@AaronBrown +1 за этот ответ, потому что любой, кто сталкивается с такой ситуацией с базой данных all-InnoDB, нуждается в этой информации.
RolandoMySQLDBA
1
+1 за размещение [mysqld]там. Для многих может быть очевидным, что этот параметр подходит для mysqld, но может не быть очевидным для тех, кто задает этот вопрос. Кстати, это ускорило SELECT COUNT(*)на одном из моих information_schemaстолов до 6 секунд за минуту. Все еще медленно, но огромное улучшение.
Баттл Буткус
3

Я предлагаю вам создать базу данных, в которой есть INFORMATION_SCHEMAтаблицы (или только те, которые вам нужны) в качестве копий. Индексируйте их соответствующим образом, и вы получите прирост производительности.

Проблема синхронизации между этой базой данных и INFORMATION_SCHEMAхитрая, хотя.

У вас может быть процедура, которая синхронизирует эти таблицы каждый час или каждые 5 минут (как часто меняется структура таблиц?).

Другая идея заключается в том, чтобы использовать MySQL Proxy для перехвата любых ALTER TABLEоператоров (и, CREATEи, DROPи, CREATE INDEXи так далее, для любых других операторов, которые изменяют необходимую информацию), а затем синхронизировать реплицированную информационную схему после успешного выполнения этих операторов.


Если вам нужны только имена столбцов, а не какая-либо другая информация, такая как тип данных, длина или доступные индексы, вы, возможно, могли бы заменить использование SHOW COLUMNSс (быстрыми) запросами, которые возвращают только 1 строку, LIMIT 1вообще или без них, либо либо, LIMIT 0либо:

SELECT * FROM TableName WHERE FALSE ;

Несмотря на общий совет против использования SELECT *, это может быть законным случаем, где ничто другое не полезно. (все остальное, кроме *, может привести к ошибке!)

ypercubeᵀᴹ
источник
2

В данном конкретном случае, я думаю, INFORMATION_SCHEMAэто красная сельдь. Судя по моим собственным тестам SHOW COLUMNSпроизводительности, innodb_stats_on_metadataпеременная, похоже, не имеет никакого значения для таблиц MyISAM или InnoDB.

Тем не менее, из руководства MySQL 5.0 ...

Некоторые условия не позволяют использовать временную таблицу в памяти, и в этом случае сервер использует вместо нее таблицу на диске:

[...]

  • Операторы SHOW COLUMNSand DESCRIBEиспользуются BLOBв качестве типа для некоторых столбцов, поэтому временная таблица, используемая для результатов, представляет собой таблицу на диске.

Похоже, что это было удалено из руководства, начиная с MySQL 5.5, но все еще применимо в этой версии ...

mysql> SHOW VARIABLES LIKE 'version';
+---------------+-------------------------+
| Variable_name | Value                   |
+---------------+-------------------------+
| version       | 5.5.41-0ubuntu0.14.04.1 |
+---------------+-------------------------+
1 row in set (0.00 sec)

mysql> SHOW STATUS LIKE 'Created_tmp_disk_tables';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| Created_tmp_disk_tables | 0     |
+-------------------------+-------+
1 row in set (0.00 sec)

mysql> SHOW COLUMNS FROM mysql.user;
[...snip...]
42 rows in set (0.00 sec)

mysql> SHOW STATUS LIKE 'Created_tmp_disk_tables';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| Created_tmp_disk_tables | 1     |
+-------------------------+-------+
1 row in set (0.00 sec)

Информация о поле, возвращаемая с набором результатов запроса, содержит ту же информацию, что и возвращаемая SHOW COLUMNS, поэтому a SELECT * FROM my_table LIMIT 0должно достигать того же самого, не создавая временную таблицу на диске для каждого запроса.

Быстрый пример, чтобы просто получить имена полей в PHP ...

$mysql = new mysqli('localhost', 'root', '', 'my_database');
$field_names = array();
$result = $mysql->query("SELECT * FROM my_table LIMIT 0");
$fields = $result->fetch_fields();
foreach ($fields as $fields)
{
    $field_names[] = $field->name;
}
var_dump($field_names);

Получение информации о поле таким способом немного более неудобно для декодирования. Вам придется обратиться к описанию базовой MYSQL_FIELDструктуры, чтобы извлечь типы данных и флаги, но в моей системе она работает примерно в 7 раз быстрее.

Ая
источник
1

Мне нравится первое предложение в ответе @ yerpcube (+1), но я хотел бы предложить кое-что

  • создайте другой экземпляр базы данных на порту 3307
  • mysqldump производственной базы данных в текстовый файл SQL, используя следующие параметры:
    • --no-data
    • --routines
    • --triggers
    • --all-databasesили --databasesзатем список баз данных, которые вы хотите
  • Загрузите текстовый файл SQL в порт 3307 MySQL Instance

Таким образом, ваш mysqldump должен выглядеть следующим образом:

mysqldump --no-data --routines --triggers --all-databases > ImportFile.sql

Вот и все. В дальнейшем все, что вам нужно сделать, это подключиться к этому экземпляру базы данных порта 3307 и выполнить любой запрос, связанный со схемой, к вашему сердцу. Если вам известна какая-либо таблица в производственной базе данных, которая изменяется, просто mysqldump схема из производства и снова загрузите ее в экземпляр порта 3307.

ВНИМАНИЕ: Если вы устанавливаете экземпляр mysql на той же машине, что и производственная, убедитесь, что вы подключаетесь к этому экземпляру, используя

mysql -u... -p... -h127.0.0.1 -P3307 < ImportFile.sql

Если вы выполните

mysql -u... -p... -P3307 < ImportFile.sql

Это будет производство шлангов. Так что будь осторожен !!!!

Альтернативой было бы просто использовать отдельный сервер БД.

RolandoMySQLDBA
источник