Запрос к двум целочисленным столбцам занимает абсурдное количество времени - PullRequest
0 голосов
/ 19 сентября 2011

У меня есть запрос, который генерируется (Django) следующим образом:

SELECT `geo_ip`.`id`, `geo_ip`.`start_ip`,
       `geo_ip`.`end_ip`, `geo_ip`.`start`,
       `geo_ip`.`end`, `geo_ip`.`cc`, `geo_ip`.`cn`
FROM `geo_ip`
WHERE (`geo_ip`.`start` <= 2084738290 AND `geo_ip`.`end` >= 2084738290 )
LIMIT 1

Он запрашивает таблицу GeoLocating с 134189 записями в ней.Каждый запрос выполняется> 100 мс при добавлении индексов, что делает его непригодным для более чем одноразового использования.Я собираюсь кешировать ответ, так что мне нужно выполнить поиск IP только один раз, но мне любопытно, упускаю ли я какой-то очевидный способ сделать его на порядок быстрее.Моя таблица:

CREATE TABLE `geo_ip` (
  `start_ip` char(15) NOT NULL,
  `end_ip` char(15) NOT NULL,
  `start` bigint(20) NOT NULL,
  `end` bigint(20) NOT NULL,
  `cc` varchar(6) NOT NULL,
  `cn` varchar(150) NOT NULL,
  `id` int(11) NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`),
) ENGINE=InnoDB AUTO_INCREMENT=134190 DEFAULT CHARSET=latin1

Создание индекса для обоих столбцов следующим образом:

ALTER TABLE geo_ip ADD INDEX (start, end);

Дает следующее объяснение:

EXPLAIN SELECT geo_ip.id, geo_ip.start_ip, geo_ip.end_ip,
               geo_ip.start, geo_ip.end, geo_ip.cc, geo_ip.cn
FROM geo_ip
WHERE (geo_ip.end >= 2084738290 AND geo_ip.start < 2084738290)
LIMIT 1;
+----+-------------+--------+-------+---------------+-------+---------+------+-------+----------+-------------+
| id | select_type | table  | type  | possible_keys | key   | key_len | ref  | rows  | filtered | Extra       |
+----+-------------+--------+-------+---------------+-------+---------+------+-------+----------+-------------+
|  1 | SIMPLE      | geo_ip | range | start         | start | 8       | NULL | 67005 |   100.00 | Using where |
+----+-------------+--------+-------+---------------+-------+---------+------+-------+----------+-------------+

Требуется более 100 мс дляcomplete выбирает:

SELECT geo_ip.id, geo_ip.start_ip, geo_ip.end_ip,
       geo_ip.start, geo_ip.end, geo_ip.cc,
       geo_ip.cn
FROM geo_ip
WHERE (geo_ip.end >= 2084738290 and geo_ip.start < 2084738290)
LIMIT 1;
+-------+--------------+----------------+------------+------------+----+-----------+
| id    | start_ip     | end_ip         | start      | end        | cc | cn        |
+-------+--------------+----------------+------------+------------+----+-----------+
| 51725 | 124.66.128.0 | 124.66.159.255 | 2084732928 | 2084741119 | SG | Singapore |
+-------+--------------+----------------+------------+------------+----+-----------+
1 row in set (0.18 sec)

Это дороже, чем отдельный индекс:

ALTER TABLE geo_ip ADD INDEX (`start`);
ALTER TABLE geo_ip ADD INDEX (`end`);
+----+-------------+--------+-------+---------------+-------+---------+------+-------+-------------+
| id | select_type | table  | type  | possible_keys | key   | key_len | ref  | rows  | Extra       |
+----+-------------+--------+-------+---------------+-------+---------+------+-------+-------------+
|  1 | SIMPLE      | geo_ip | range | start,end     | start | 8       | NULL | 68017 | Using where |
+----+-------------+--------+-------+---------------+-------+---------+------+-------+-------------+

Требуется около 100 мсек для выполнения этих запросов:

SELECT geo_ip.id, geo_ip.start_ip, geo_ip.end_ip, geo_ip.start, geo_ip.end, geo_ip.cc, geo_ip.cn FROM geo_ip
WHERE (geo_ip.end >= 2084738290 AND geo_ip.start < 2084738290) limit 1;
+-------+--------------+----------------+------------+------------+----+-----------+
| id    | start_ip     | end_ip         | start      | end        | cc | cn        |
+-------+--------------+----------------+------------+------------+----+-----------+
| 51725 | 124.66.128.0 | 124.66.159.255 | 2084732928 | 2084741119 | SG | Singapore |
+-------+--------------+----------------+------------+------------+----+-----------+
1 row in set (0.11 sec)

Нооба эти метода занимают слишком много времени, можно ли что-нибудь с этим сделать?

1 Ответ

0 голосов
/ 19 сентября 2011

Время всегда указывается в предложении where.

И поскольку вы работаете с двумя различными полями с «ниже» или «больше чем», необходимо прочитать множество индексов, чтобы выяснить, какая запись вам нужна.

Я должен был сделать свой стол следующим образом:

+-------+-------+----------------+------------+----+-----------+
| id    | type  | ip             | geo        | cc | cn        |
+-------+-------+----------------+------------+----+-----------+
| 51725 | start | 124.66.159.255 | 2084732928 | SG | Singapore |
+-------+-------+----------------+------------+----+-----------+
| 51726 | end   | 124.66.159.255 | 2084732928 | SG | Singapore |
+-------+-------+----------------+------------+----+-----------+

чтобы я мог выбрать это:

select * from table where geo between '2084732927' and '2084732928'

с индексом по гео. Должно быть намного, намного быстрее. Но извините, у меня нет времени, чтобы попробовать.

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