MySQL замедляет работу базы данных - PullRequest
1 голос
/ 14 октября 2019

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

Поведението же самое, выполняется ли запрос, пытаясь получить фактический метар, или выполняется простой select count(*).

Синтаксис создания таблицы следующий:

CREATE TABLE `metars` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `tstamp` timestamp NULL DEFAULT NULL,
  `metar` varchar(255) DEFAULT NULL,
  `icao` char(7) DEFAULT NULL,
  `qnh` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `timestamp` (`tstamp`),
  KEY `icao` (`icao`),
  KEY `qnh` (`qnh`),
  KEY `metar` (`metar`)
) ENGINE=InnoDB AUTO_INCREMENT=812803050 DEFAULT CHARSET=latin1;

Примерно до8 миллионов записей, select count(*) возвращается примерно за 500 мс. Затем он постепенно увеличивается, в настоящее время снова на 14 миллионов записей, отсчет занимает от 3 до 30 секунд. Я был удивлен, увидев, что при объяснении запроса количества он использует временную метку в качестве индекса, а не первичный ключ. При использовании первичного ключа это может занять всего несколько мс, чтобы вернуть количество записей:

mysql> explain select count(*) from metars;
+----+-------------+--------+-------+---------------+-----------+---------+------+----------+-------------+
| id | select_type | table  | type  | possible_keys | key       | key_len | ref  | rows     | Extra       |
+----+-------------+--------+-------+---------------+-----------+---------+------+----------+-------------+
|  1 | SIMPLE      | metars | index | NULL          | timestamp | 5       | NULL | 14693048 | Using index |
+----+-------------+--------+-------+---------------+-----------+---------+------+----------+-------------+
1 row in set (0.00 sec)

Заставить его использовать первичный индекс еще медленнее:

mysql> select count(*) from metars use index(PRIMARY);
+----------+
| count(*) |
+----------+
| 14572329 |
+----------+
1 row in set (37.87 sec)

Как ни странно, типичный запрос варианта использования состоит в том, чтобы получить погоду для аэропорта, ближайшего к определенному моменту времени, который продолжает работать очень хорошо, несмотря на то, что он является более сложным, чем простой подсчет:

mysql> SELECT qnh, metar from metars WHERE icao like 'KLAX' ORDER BY ABS(TIMEDIFF(tstamp, STR_TO_DATE('2019-10-10 00:00:00', '%Y-%m-%d %H:%i:%s'))) LIMIT 0,1;
+------+-----------------------------------------------------------------------------------------+
| qnh  | metar                                                                                   |
+------+-----------------------------------------------------------------------------------------+
| 2980 | KLAX 092353Z 25012KT 10SM FEW015 20/14 A2980 RMK AO2 SLP091 T02000139 10228 20200 56007 |
+------+-----------------------------------------------------------------------------------------+
1 row in set (0.01 sec)

Что яздесь делаешь неправильно?

1 Ответ

0 голосов
/ 18 октября 2019

InnoDB выполняет простое COUNT(*) путем обхода некоторого индекса. Он предпочитает наименьший индекс, потому что для этого потребуется прикоснуться к наименьшему количеству блоков.

PRIMARY KEY кластеризован с данными, так что индекс на самом деле самый большой.

Какая у вас версияс помощью? TIMESTAMP изменилось в какой-то момент. Возможно, это объясняет, почему вместо qnh используется tstamp.

Если вы удаляете старые данные с помощью DELETE, см. http://mysql.rjweb.org/doc.php/partitionmaint для более быстрого способа.

Я предполагаю, что данные статичны;то есть это никогда не UPDATEd? Подумайте о создании и ведении сводной таблицы, возможно, проиндексированной по дате. Это может иметь различные значения для каждого дня. Тогда выборка из этой таблицы будет намного быстрее, чем получение необработанных данных. Подробнее: http://mysql.rjweb.org/doc.php/summarytables

Сколько строк для KLAX? Этот запрос должен извлечь их все, чтобы преобразовать метку времени перед выполнением LIMIT. Если бы у вас было INDEX(icao, tstamp), вы могли бы найти следующий до или после определенного времени еще быстрее.

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