Почему скачок времени запроса несмотря на одинаковое количество проверенных строк? - PullRequest
7 голосов
/ 04 января 2012

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

SELECT P.id, P.name, P.lat, P.lng, P.price * E.rate AS 'ask' FROM Property P
 INNER JOIN Exchange E ON E.currency = P.currency
 WHERE P.floor_area >= k?
  AND P.closing_date >= CURDATE() // this and key_buffer_size=0 prevents caching
  AND P.type ='c'
  AND P.lat BETWEEN v? AND v?
  AND P.lng BETWEEN v? AND v?
  AND P.price * E.rate BETWEEN k? AND k?
 ORDER BY P.floor_area DESC LIMIT 100;

k? определяются пользователем constant значения;v? - это переменные , которые изменяются при перетаскивании пользователем или масштабировании карты.100 результатов извлекаются из таблицы и сортируются в соответствии с площадью пола в порядке убывания.

ПЕРВИЧНЫЙ ключ на id и ИНДЕКС на floor_area устанавливаются только.Никакой другой индекс не создается, чтобы MySQL последовательно использовал floor_area в качестве единственного ключа.Время запроса и проверенные строки записываются следующим образом:

query number              1    2    3    4    5    6    7    8    9    10
user action on map     start   >    +    +    <    ^    +    >    v    +
time in seconds          138  0.21 0.43 32.3 0.12 0.12 36.3 4.33 0.33 2.00
rows examined ('000)      43    43   43   60   43   43  111  139  133  176

План выполнения запроса следующий:

+----+-------------+-------+--------+---------------+---------+---------+--------------------+---------+-------------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref                | rows    | Extra       |
+----+-------------+-------+--------+---------------+---------+---------+--------------------+---------+-------------+
|  1 | SIMPLE      | P     | range  | id_flA        | id_flA  | 3       | NULL               | 4223660 | Using where |
|  1 | SIMPLE      | E     | eq_ref | PRIMARY       | PRIMARY | 3       | BuySell.P.currency |       1 | Using where |
+----+-------------+-------+--------+---------------+---------+---------+--------------------+---------+-------------+

Тест выполняется несколько раз, и результаты довольнов соответствии с вышеизложенным.Каковы могут быть причина (-ы) для скачка во времени запроса в запросе № 4 и № 7 и как его устранить?

ОБНОВЛЕНИЕ:

Результаты удаления ORDER BY в соответствии с рекомендациями Digital Precision:

query number              1    2    3    4    5    6    7    8    9    10
user action on map     start   >    +    +    <    ^    +    >    v    +
time in seconds          255  3.10 3.16 3.08 3.18 3.21 3.32 3.18 3.17 3.80
rows examined ('000)     131  131  131  131  136  136  136  136  136  157

План выполнения запроса такой же, как и выше, хотя он больше похож на сканирование таблицы.Обратите внимание, что я использую MyISAM engine, версия 5.5.14.

AS запрашивается, ниже приведена схема:

| Property | CREATE TABLE `Property` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `type` char(1) NOT NULL DEFAULT '',
  `lat` decimal(6,4) NOT NULL DEFAULT '0.0000',
  `lng` decimal(7,4) NOT NULL DEFAULT '0.0000',
  `floor_area` mediumint(8) unsigned NOT NULL DEFAULT '0',
  `currency` char(3) NOT NULL DEFAULT '',
  `price` int(10) unsigned NOT NULL DEFAULT '0',
  `closing_date` date NOT NULL DEFAULT '0000-00-00',
  `name` char(25) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `id_flA` (`floor_area`)
) ENGINE=MyISAM AUTO_INCREMENT=5000000 DEFAULT CHARSET=latin1

| Exchange | CREATE TABLE `Exchange` (
  `currency` char(3) NOT NULL,
  `rate` decimal(11,10) NOT NULL DEFAULT '0.0000000000',
  PRIMARY KEY (`currency`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1

2ND UPDATE:

Я подумал, что было бы целесообразно опубликовать нестандартные параметры в файле конфигурации my.cnf, так как два из них упоминают о параметрах:

max_heap_table_size = 1300M
key_buffer_size = 0
read_buffer_size = 1300M
read_rnd_buffer_size = 1024M
sort_buffer_size = 1300M

У меня 2 ГБОперативная память на моем тестовом сервере.

Ответы [ 4 ]

4 голосов
/ 09 января 2012

Полагаю, я выяснил причину всплесков. Вот как это происходит:

Сначала я создал таблицы и загрузил в них несколько случайно сгенерированных данных:

Вот мой запрос:

SELECT SQL_NO_CACHE P.id, P.name, P.lat, P.lng, P.price * E.rate AS 'ask' 
FROM Property P
 INNER JOIN Exchange E ON E.currency = P.currency
 WHERE P.floor_area >= 2000
  AND P.closing_date >= CURDATE()
  AND P.type ='c'
  AND P.lat BETWEEN 12.00 AND 22.00
  AND P.lng BETWEEN 10.00 AND 20.00
  AND P.price BETWEEN 100 / E.rate AND 10000 / E.rate
 ORDER BY P.floor_area DESC LIMIT 100;

А вот и описание:

+----+-------------+-------+-------+---------------+--------+---------+------+---------+----------------------------------------------+
| id | select_type | table | type  | possible_keys | key    | key_len | ref  | rows    | Extra                                        |
+----+-------------+-------+-------+---------------+--------+---------+------+---------+----------------------------------------------+
|  1 | SIMPLE      | P     | range | id_flA        | id_flA | 3       | NULL | 4559537 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | E     | ALL   | PRIMARY       | NULL   | NULL    | NULL |       6 | Using where; Using join buffer               |
+----+-------------+-------+-------+---------------+--------+---------+------+---------+----------------------------------------------+

это занимало от 3,5 ~ 3,9 сек каждый раз, когда я запрашиваю данные (не имеет значения, какие параметры я использую). Это не имело смысла, поэтому я исследовал Использование буфера соединения

Затем я хотел попробовать этот запрос без «буфера соединения», поэтому я вставил еще 1 случайные данные в таблицу Exchange.

INSERT INTO Exchange(currency, rate) VALUES('JJ', 1);

Теперь я использую тот же sql, и для ответа потребовалось 0,3 ~ 0,5 секунд. А вот и описание:

+----+-------------+-------+--------+---------------+---------+---------+-----------------+---------+-------------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref             | rows    | Extra       |
+----+-------------+-------+--------+---------------+---------+---------+-----------------+---------+-------------+
|  1 | SIMPLE      | P     | range  | id_flA        | id_flA  | 3       | NULL            | 4559537 | Using where |
|  1 | SIMPLE      | E     | eq_ref | PRIMARY       | PRIMARY | 3       | test.P.currency |       1 | Using where |
+----+-------------+-------+--------+---------------+---------+---------+-----------------+---------+-------------+

Так что проблема (насколько я вижу) в оптимизаторе пытается использовать «буфер соединения». Оптимальным решением этой проблемы было бы заставить оптимизатор не использовать «буфер соединения». (что я не мог найти, как) или изменить значение "join_buffer_size". Я решаю это, добавляя «фиктивные» значения в таблицу Exchange (чтобы оптимизатор не использовал буфер соединения), но это не точное решение, это просто глупый прием, чтобы обмануть mysql.

Редактировать: Я исследовал на форумах / ошибках mysql об этом поведении "буфера соединения"; затем спросил об этом в официальных форумах . Я собираюсь заполнить отчет об ошибке в этом нерациональном поведении оптимизатора.

3 голосов
/ 07 января 2012

Попробуйте индекс по типу и floor_area (и, возможно, также по дате закрытия).

Измените свои константы по курсу вместо столбца цены:

P.price between ( k? / E.rate ) and ( k? / E.rate )

, затем попробуйте индекс поцена.

3 голосов
/ 04 января 2012

Пара вещей:

  1. Почему вы вычисляете произведение P.price и E.rate в SELECT, а затем задаете псевдонимы как 'ask', а затем снова выполняете вычисление в предложении where? Должно быть в состоянии сделать AND ask BETWEEN k? and k? - Редактировать : Это не будет работать из-за того, как работает MySQL. Очевидно, MySQL оценивает предложение WHERE перед любыми псевдонимами ( sourced ).

  2. Какой у вас индекс для Exchange.currency и Property.currency? Если exchange - это таблица поиска, возможно, вам лучше добавить сводную (связывающую) таблицу с помощью Property.Id и Exchange.Id

  3. Порядок, заданный floor_area, заставляет MySQL создать временную таблицу для правильной сортировки, есть ли шанс, что вы сможете выполнить сортировку на уровне приложения?

  4. Также поможет добавление индекса по столбцу типа.

- Редактировать

Не уверен, что вы подразумеваете под комментарием // this and key_buffer_size=0 prevents caching к CURDATE, где условно, вы можете принудительно отключить SQL-кеширование, используя флаг SQL_NO_CACHE в вашем select утверждении.

Теперь, когда вы удалили ORDER BY, я бы порекомендовал обновить оператор запроса следующим образом (добавлен псевдоним P для столбцов, чтобы избежать путаницы):

WHERE P.type ='condominium'
    AND P.floor_area >= k?
    AND P.closing_date >= CURDATE() // No longer necessary with SQL_NO_CACHE
    AND P.lat BETWEEN v? AND v?
    AND P.lng BETWEEN v? AND v?
    AND P.price * E.rate BETWEEN k? AND k?

Затем добавьте индекс в столбец «тип» и составной индекс в столбцы «тип» и «floor_area». Как вы сказали, столбец типа является столбцом с низким количеством элементов, но таблица большая и должна помочь. И даже несмотря на то, что floor_area является столбцом с большим количеством элементов, составной индекс поможет ускорить время запроса.

Возможно, вы также захотите выяснить, есть ли штраф, использующий МЕЖДУ, а не операторы диапазона (>, <, <= и т. Д.) </p>

2 голосов
/ 09 января 2012

Я стал немного одержим этим вопросом;Спайк трудно объяснить.

Вот что я сделал:

Я заново создал вашу схему и заполнил таблицу свойств 4,5 миллионами записей со случайными значениями для числовых столбцов и столбцов даты.Это почти наверняка не совпадает с вашими данными - я предполагаю, что широта / долгота имеют тенденцию кластеризоваться в населенных пунктах, цены примерно в 10К кратны, а площадь будет смещена к более низким значениям.

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

Всегда стоит запускать ANALYZE TABLE, когдаИмея дело с такого рода странностями.

Я получил немного другие "объяснения" результатов: в частности, выбор таблицы свойств дал 'Using where;Используя временные;Использование файловой сортировки.Это говорит о том, что индекс используется только для предложения where, а не для упорядочения результатов.

Это подтверждает, что наиболее вероятное объяснение пиков производительности связано не столько с механизмом запросов, сколько с тем, как обрабатывается временная таблица, и с требованием выполнить сортировку файлов.Пытаясь воспроизвести эту проблему, я заметил, что время отклика резко возросло, так как количество записей, возвращаемых из условия «где», увеличилось - хотя я не видел всплески, которые вы заметили.

Я пробовал множество разных индексов;использование всех ключей в предложении where ускоряет время для извлечения записей, соответствующих предложению where, но ничего не делает для последующего заказа по.

Это еще раз говорит о том, что это производительность временной таблицыэто причина шипов.read_rnd_buffer_size будет очевидной вещью.

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