0

我在尝试在包含用户信息的表和包含地理位置数据的表(来自 GeoIP 的数据库)之间进行交叉引用时遇到问题。

我在用户表中有标准格式的 IP 地址(不是整数),在 GeoIP 表中有 GeoIP 数据,IP 范围为整数。

此查询有效,但速度非常慢且未优化。

SELECT email, country 
FROM users 
INNER JOIN geoip ON users.ip BETWEEN geoip.startip AND geoip.endip

我觉得我在这里错过了一些非常容易的事情。

更新:此查询有效,但速度很慢 - 有没有办法索引它以使其更快?现在,无论何时运行,每一行都需要大约 300-500 毫秒来执行,这太慢了。

SELECT email, country 
FROM users INNER JOIN geoip ON INET_ATON(users.ip) 
BETWEEN geoip.startip AND geoip.endip

谢谢!

更新 2:这是查询的 EXPLAIN 输出:

+----+-------------+-----------+------+---------------+------+---------+------+----------+-------------+
| id | select_type | table     | type | possible_keys | key  | key_len | ref  | rows     | Extra       |
+----+-------------+-----------+------+---------------+------+---------+------+----------+-------------+
|  1 | SIMPLE      | geoip     | ALL  | NULL          | NULL | NULL    | NULL |  3651972 |             | 
|  1 | SIMPLE      | users     | ALL  | NULL          | NULL | NULL    | NULL | 87996123 | Using where | 
+----+-------------+-----------+------+---------------+------+---------+------+----------+-------------+

我现在无法添加仅整数 IP 行,因为 DB 正在使用中,并且超过 9000 万行;这将是我在停机期间考虑做的事情,但现在,我想让它以这种方式运行。

4

1 回答 1

2

我还不能发表评论,所以这里有一个“答案”......

你确定它有效吗?如果我正确理解您的描述,您将 users.ip 作为 char 或 varchar 中的 CIDR 表示法,并将 geoip.startip/endip 作为整数。因此,此查询无法正确比较这两者。

这样做的正确方法是

SELECT email, country 
FROM users INNER JOIN geoip ON INET_ATON(users.ip) 
BETWEEN geoip.startip AND geoip.endip

或者

SELECT email, country 
FROM users INNER JOIN geoip ON users.ip 
BETWEEN INET_NTOA(geoip.startip) AND INET_NTOA(geoip.endip) 

哪个更好,取决于哪个表更大(更多行)。

但是,最好的方法是将 users.ip 存储为整数(或具有整数解释的另一列)。

于 2011-12-06T23:16:03.173 回答