2011-09-19 296 views
0

我有獲取生成(按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 

它查詢一個大地定位表與它134189項。添加索引時,每個查詢需要> 100ms才能執行,這使得它不能用於一次性事物。我將緩存響應,因此我只需要執行一次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毫秒即可完成選擇:

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) 

但是這兩種方法都需要太長的方式,是有可能做到這事?

回答

0

時間總是在「where」子句中消耗。

而且由於您正在使用「低於」或「大於」兩個不同的字段,它必須讀取很多索引才能找出您想要的記錄。

我應該做我的表是這樣的:與地理索引

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

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

,這樣我可以選擇此項。 應該更快,更快。但抱歉,我沒有時間嘗試。