MySQL Performance - PullRequest
       15

MySQL Performance

0 голосов
/ 14 февраля 2009

В последнее время мои запросы до кэширования в memcache занимали целую вечность! В этом примере это заняло 10 секунд. Все, что я пытаюсь сделать, это получить 10 самых последних хитов в этом случае.

У меня такое ощущение, что он загружает все 125 592 строки, а затем возвращает только 10, я прав?

# User@Host: root[root] @ localhost []
# Query_time: 10  Lock_time: 0  Rows_sent: 10  Rows_examined: 125592
SELECT * FROM hits WHERE campaign_id = 30 ORDER BY id DESC LIMIT 10;

Вот еще один медленный запрос:

# Time: 090214  5:00:40
# User@Host: root[root] @ localhost []
# Query_time: 3  Lock_time: 0  Rows_sent: 1  Rows_examined: 128879
SELECT count(DISTINCT(ip_address)) AS count_distinct_ip_address FROM `hits` WHERE (campaign_id = 30);

При запуске запроса phpMyAdmin это занимает 1,33395 секунды. Хотя выполнение SELECT * FROM hits занимает всего 0,0001 секунды. Я нахожу очень странным, что для возврата всех попаданий требуется меньше, чем для их сортировки, или просто я их сортирую?

Для тех, кто хочет посмотреть мой стол:

CREATE TABLE `hits` (
  `id` int(11) unsigned NOT NULL auto_increment,
  `hostname` varchar(255) NOT NULL,
  `url` tinytext NOT NULL,
  `user_agent` tinytext NOT NULL,
  `created_at` timestamp NOT NULL default CURRENT_TIMESTAMP,
  `ip_address` varchar(15) NOT NULL,
  `campaign_id` int(11) NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `campaign_id` (`campaign_id`),
  KEY `ip_address` (`ip_address`)
);

Ответы [ 5 ]

5 голосов
/ 14 февраля 2009

Кажется, у вашего campaign_id индекса низкая селективность, т.е. е. Есть много записей с этим значением.

Заказ такого количества записей занимает много времени.

Попробуйте использовать INDEX SCAN на PRIMARY KEY для заказа:

/* Edited, as MySQL does not use live feed from the derived source with ORDER BY */
SELECT *
FROM hits
WHERE IFNULL(campaign_id, campaing_id) = 30
ORDER BY id DESC
LIMIT 10;

Что касается вашего второго запроса, мало что можно сделать, так как в любом случае вам необходимо выполнить полное сканирование campaign_id = 30, будь то TABLE SCAN или INDEX SCAN.

На самом деле, TABLE SCAN может быть еще быстрее:

SELECT count(DISTINCT(ip_address)) AS count_distinct_ip_address
FROM `hits`
WHERE IFNULL(campaign_id, campaign_id)  = 30;

Если это не так, вы можете создать индекс для (campaign_id, ip_address) и использовать трюк для имитации INDEX GROUP BY для этого индекса:

CREATE INDEX ix_hits_campaign_ip ON hits(campaign_id, ip_address)

SELECT SUM(cnt)
FROM (
SELECT CASE WHEN @r = ip_address THEN 0 ELSE 1 END AS cnt,
  @r := ip_address
FROM
  (SELECT @r:='') r,
  (
  SELECT ip_address
  FROM hits
  WHERE campaign_id = 30
  ORDER BY ip_address
  ) i
) o

Хитрость здесь проста: нам не нужен результат, просто счетчик, поэтому нет необходимости сканировать фактические значения. Индексного сканирования будет достаточно.

К сожалению, несмотря на то, что в документации MySQL написано здесь при сканировании свободных индексов, они фактически не работают с составными индексами. Вот почему нам нужно подражать INDEX SCAN WITH GROUP BY.

Мы делаем это, заставляя MySQL использовать INDEX RANGE SCAN, который извлекает все записи с campaign_id = 30, отсортированными по ip_address. Затем мы подсчитываем DISTINCT ip_address с использованием переменной сеанса @r, инициализированной пустой строкой в ​​первом подзапросе.

В первом поле мы устанавливаем переменную на 0, когда предыдущий ip_address (сохраненный в переменной) равен текущему; в противном случае мы устанавливаем 1. Во втором поле мы присваиваем переменную текущее значение ip_address.

Наконец, мы получаем SUM в первом поле, которое, конечно, даст нам COUNT (DISTINCT ip_address).

2 голосов
/ 14 февраля 2009

Индекс на (campaign_id,id) должен позаботиться о первом достаточно хорошо. Но другой немного сложнее ...

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

1 голос
/ 15 февраля 2009

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

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

Если вы уверены, что добавление индексов - это то, что нужно, вы можете свернуть эти изменения в выпуск программного обеспечения, как обычно. Остерегайтесь ALTER TABLE для больших таблиц, хотя, это может занять некоторое время и заблокировать записи в таблицу (однако 120k строк, вероятно, не большая таблица). Прежде чем внедрять изменения, убедитесь, что вы знаете, сколько времени это займет и какое влияние это окажет на производство.

1 голос
/ 14 февраля 2009

Если запрос обрабатывается слишком долго, это обычно из-за отсутствующих индексов, плохого дискового ввода-вывода или других узких мест. Таблица с 120 000 строк - это не чертовски много данных, и запрос на самом деле не должен занимать так много времени. Я действительно проверю диск io.

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

Вы также должны запустить «объяснение» для вашего запроса и посмотреть, какие индексы он использует, если таковые имеются. Какое хранилище enigne вы используете для MySQL? Это также может оказать влияние. Если вы используете механизм хранения MYISAM и одновременно выполняете операции вставки и чтения, это может сильно повлиять на производительность.

Убедитесь, что статистика вашей таблицы обновляется, регулярно выполняя «Анализ» для больших таблиц. Это помогает обработчику запросов выбрать оптимальный план запроса.

0 голосов
/ 15 февраля 2009

Просто предположение.

SELECT * FROM hits WHERE (campaign_id = 30 AND id > 0) ORDER BY id DESC LIMIT 10;

Надеюсь, MySQL объединит индексы. Удачи.

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