Медленное соединение с таблицей строк 3M - PullRequest
0 голосов
/ 03 марта 2019

Я выполняю следующий запрос в MySQL, чтобы найти широту / долготу для IP-адресов сеансов, но после 5 или 6 сеансов этот запрос занимает несколько минут (если вообще когда-либо).

Таблица ip2loc2.9M строк.

select sessions.ip, 
       ip2loc.region_name, ip2loc.city_name,
       ip2loc.latitude, 
       ip2loc.longitude,
       count(sessions.ip) as count 
from ip2location.ip2loc, sessions 
where INET_ATON(sessions.ip) between ip_from and ip_to 
group by ip

Таблица ip2loc имеет следующие индексы:

 KEY `idx_ip_from` (`ip_from`),
 KEY `idx_ip_to` (`ip_to`),
 KEY `idx_ip_from_to` (`ip_from`,`ip_to`)

Есть ли лучший способ структурировать этот запрос, чтобы он не занимал то, что кажется вечнымбежать?

Ответы [ 2 ]

0 голосов
/ 03 марта 2019

Проблема:

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 плохо справился с оптимизацией условий диапазона для объединения.Новые версии могут быть лучше сейчас.

0 голосов
/ 03 марта 2019
select sessions.ip, 
     ip2loc.region_name, 
     ip2loc.city_name,
     ip2loc.latitude, 
     ip2loc.longitude,
     count(sessions.ip) as count 
from ip2location.ip2loc
INNER JOIN sessions ON INET_ATON(sessions.ip) between ip2loc.ip_from and. ip2locip_to 
group by sessions.ip

Убедитесь, что у вас также есть индекс для столбцов сеансов таблицы ip

. Вы можете улучшить составной индекс, добавив избыточность для нужных вам столбцов, в этом случае выберите

KEY `idx_ip_from_to` (`ip_from`,`ip_to`,region_name,  city_name, latitude, longitude )

чтобы запрос мог получить всю информацию в индексе и не нуждаться в доступе к данным таблицы

Для удобства чтения не следует использовать старый синтаксис неявного соединения, основанный на выражении where, но использовать явный синтаксис объединения, основанный на предложении объединения и ON

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