Кажется, у вашего 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)
.