Проблема:
INET_ATON(sessions.ip) between ip_from and ip_to
, что эквивалентно
INET_ATON(sessions.ip) >= ip_from
AND
INET_ATON(sessions.ip) <= ip_to
Это условие не может использовать индекс из таблицы sessions
, потому что sessions.ip
переносится в вызове finction.
Он может использовать индекс из таблицы местоположений - но только первую ключевую часть.Невозможно сделать два сканирования диапазона (>=
и <=
) для двух разных ключевых частей.Движок может использовать индекс на (ip_from, ip_to)
, но ему нужно будет прочитать половину всех строк в индексе (в среднем 1,45 млн. Строк) для каждой строки в таблице sessions
.Двигатель может даже решить вообще не использовать индекс.Таким образом, вы получите полное объединение двух таблиц.
Первая оптимизация, которую вы можете сделать, - это уменьшить количество строк в таблице sessions
, заключив запрос GROUP BY вподзапрос:
select s.ip,
l.region_name,
l.city_name,
l.latitude,
l.longitude,
s.count
from (
select ip, INET_ATON(s.ip) ip_bin, count(*) as count
from sessions
group by ip
) s
join ip2location l on s.ip_bin between ip_from and ip_to
Если это все еще слишком медленно, вы можете попытаться сохранить результат подзапроса во индексированной временной таблице:
create temporary table tmp_session_ips(
ip_bin int unsigned primary key,
ip varchar(15) not null,
`count` int unsigned
)
select ip, INET_ATON(s.ip) ip_bin, count(*) as count
from sessions
group by ip
order by ip_bin
;
select s.ip,
l.region_name,
l.city_name,
l.latitude,
l.longitude,
s.count
from tmp_session_ips s
join ip2location l on s.ip_bin between ip_from and ip_to
Таким образом, PK (ip_bin
)из временной таблицы может быть использовано для объединения.Однако - это теория.По моему опыту, MySQL плохо справился с оптимизацией условий диапазона для объединения.Новые версии могут быть лучше сейчас.