У меня есть база данных, использующая следующую схему:
CREATE TABLE IF NOT EXISTS `sessions` (
`starttime` datetime NOT NULL,
`ip` varchar(15) NOT NULL default '',
`country_name` varchar(45) default '',
`country_iso_code` varchar(2) default '',
`org` varchar(128) default '',
KEY (`ip`),
KEY (`starttime`),
KEY (`country_name`)
);
(Фактическая таблица содержит больше столбцов; я включил только столбцы, по которым запрашиваю.) Движок - InnoDB.
Как видите, есть 3 индекса - на ip
, starttime
и country_name
.
Таблица очень большая - она содержит около 1,5 миллионов строк. Я выполняю различные запросы по нему, пытаясь извлечь информацию за месяц (за август 2018 года, в приведенных ниже примерах).
Запрос, подобный этому
SELECT
UNIX_TIMESTAMP(starttime) as time_sec,
country_iso_code AS metric,
COUNT(country_iso_code) AS value
FROM
sessions
WHERE
starttime >= FROM_UNIXTIME(1533070800) AND
starttime <= FROM_UNIXTIME(1535749199)
GROUP BY metric;
довольно медленный, но терпимый (десятки секунд), несмотря на то, что на country_iso_code
нет индекса.
(Игнорировать первое, что есть в SELECT
; я знаю, что это, кажется, не имеет смысла, но это требуется в инструменте, который использует результат запроса. Точно так же игнорируйте использование FROM_UNIXTIME()
вместо строки даты, эта часть запроса генерируется автоматически, и я не могу ее контролировать.)
Однако такой запрос
SELECT
country_name AS Country,
COUNT(country_name) AS Attacks
FROM
sessions
WHERE
starttime >= FROM_UNIXTIME(1533070800) AND
starttime <= FROM_UNIXTIME(1535749199)
GROUP BY Country;
невыносимо медленно - я дал ему поработать около получаса и сдался без каких-либо результатов.
Результаты EXPLAIN
:
+----+-------------+----------+------------+-------+------------------------------------+--------------+---------+------+----------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+-------+------------------------------------+--------------+---------+------+----------+----------+-------------+
| 1 | SIMPLE | sessions | NULL | index | starttime,starttime_2,country_name | country_name | 138 | NULL | 14771687 | 35.81 | Using where |
+----+-------------+----------+------------+-------+------------------------------------+--------------+---------+------+----------+----------+-------------+
В чем именно проблема? Должен ли я индексировать что-то еще? Возможно составной индекс на (starttime
, country_name
)? Я прочитал это руководство , но, может быть, я его неправильно понял?
Вот некоторые другие запросы, которые так же медленны и, вероятно, страдают от той же проблемы:
Запрос № 2:
SELECT
ip AS IP,
COUNT(ip) AS Attacks
FROM
sessions
WHERE
starttime >= FROM_UNIXTIME(1533070800) AND
starttime <= FROM_UNIXTIME(1535749199)
GROUP BY ip;
Результаты EXPLAIN
:
+----+-------------+----------+------------+-------+--------------------------+------+---------+------+----------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+-------+--------------------------+------+---------+------+----------+----------+-------------+
| 1 | SIMPLE | sessions | NULL | index | starttime,ip,starttime_2 | ip | 47 | NULL | 14771780 | 35.81 | Using where |
+----+-------------+----------+------------+-------+--------------------------+------+---------+------+----------+----------+-------------+
Запрос № 3:
SELECT
org AS Organization,
COUNT(org) AS Attacks
FROM
sessions
WHERE
starttime >= FROM_UNIXTIME(1533070800) AND
starttime <= FROM_UNIXTIME(1535749199)
GROUP BY Organization;
Результаты EXPLAIN
:
+----+-------------+----------+------------+-------+---------------------------+------+---------+------+----------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+-------+---------------------------+------+---------+------+----------+----------+-------------+
| 1 | SIMPLE | sessions | NULL | index | starttime,starttime_2,org | org | 387 | NULL | 14771800 | 35.81 | Using where |
+----+-------------+----------+------------+-------+---------------------------+------+---------+------+----------+----------+-------------+
Запрос № 4:
SELECT
ip AS IP,
country_name AS Country,
city_name AS City,
org AS Organization,
COUNT(ip) AS Attacks
FROM
sessions
WHERE
starttime >= FROM_UNIXTIME(1533070800) AND
starttime <= FROM_UNIXTIME(1535749199)
GROUP BY ip;
Результаты EXPLAIN
:
+----+-------------+----------+------------+-------+--------------------------+------+---------+------+----------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+-------+--------------------------+------+---------+------+----------+----------+-------------+
| 1 | SIMPLE | sessions | NULL | index | starttime,ip,starttime_2 | ip | 47 | NULL | 14771914 | 35.81 | Using where |
+----+-------------+----------+------------+-------+--------------------------+------+---------+------+----------+----------+-------------+