[英]MySQL: How to make a faster IP range query? GeoIP
我有一个 PHP/MySQL geo-ip 脚本,它获取用户的 IP 地址,将其转换为长整数并在 IP 范围表中搜索用户 IP 所在位置的单个地理位置 ID:
$iplong = ip2long($_SERVER['REMOTE_ADDR']);
SELECT id FROM geoip
WHERE ".$iplong." BETWEEN range_begin AND range_end
ORDER BY range_begin DESC LIMIT 1
“geoip”表包含 2.5M 行。 “range_begin”和“range_end”列都是唯一索引。 IP 范围似乎没有重叠。 有时这个查询需要大约 1 秒才能完成,但我希望有一种方法可以改进它,因为它是我网站上最慢的查询。
谢谢
编辑:我将查询更改为:
SELECT * FROM geoip
WHERE range_begin <= ".$iplong." AND range_end >= ".$iplong."
ORDER BY range_begin DESC LIMIT 1
我现在有一个唯一的综合指数(range_begin、range_end)。 我使用了“EXPLAIN”函数,看起来它仍然搜索 1.2M 行:
id: 1
select_type: Simple
table: geoip
type: range
possible_keys: range_begin
key: range_begin
key_len: 8
ref: NULL
rows: 1282026
Extra: Using Index Condition
花一些时间思考为什么传统索引在这样的场景中毫无用处,这是一个非常有用的练习。 实际上,如果您可以让查询使用索引,您会发现它可能比运行全表扫描慢。
解释为什么会占用比此处可用空间更多的空间。 有一个解决方案——将 ipaddress 数据库视为一维空间并使用空间索引。 但MySQL的空间索引仅在2个维度的工作-所以你需要映射所描述的坐标转换为2维空间在这里
请注意,当您开始处理嵌套子网时,大于 / 限制方法虽然比空间索引快,但会变得混乱。
在我查看数据之前,还有一种更简单的方法让我逃过一劫。
第一次运行
SELECT * FROM Ip2location WHERE ip_from <= $IPAddress ORDER BY ip_from DESC LIMIT 1
这将返回等于或小于您正在搜索的值的最近 IP,因为您将返回数据库中的最高单个值。
接下来,当您返回整行时,只需确保 ip_to 大于或等于 IP,您就可以确保 IP 落在该范围内。
如果 IP 不在该范围内(如果 ip_to 较小),则表示没有该 IP 的记录。
简单且快速执行!
我正在处理一个类似的问题,我不得不搜索一个包含大约 400 万个 IP 范围的数据库,并找到了一个很好的解决方案,将扫描的行数从 400 万个减少到大约 5 个(取决于 IP):
此 SQL 语句:
SELECT id FROM geoip WHERE $iplong BETWEEN range_begin AND range_end
转化为:
SELECT id FROM geoip WHERE range_begin <= $iplong AND range_end >= $iplong
问题是 MySQL 检索所有带有 'range_begin <= $iplong' 的行,然后如果 'range_end >= $iplong' 需要扫描。 第一个 AND 条件 (range_begin <= $iplong) 检索了大约 200 万行,如果 range_end 匹配,则需要检查所有行。
然而,这可以通过添加一个 AND 条件来显着简化:
SELECT id FROM geoip WHERE range_begin <= $iplong AND range_begin >= $iplong-65535 AND range_end >= $iplong
该声明
range_begin <= $iplong AND range_begin >= $iplong-65535
仅检索 range_begin 介于 $iplong-65535 和 $iplong 之间的条目。 就我而言,这将检索到的行数从 4 Mio 减少了。 到大约 5,脚本运行时间从几分钟缩短到几秒钟。
关于 65535 的注意事项:这是我的表中 range_begin 和 range_end 之间的最大距离,即,对于我的所有行, (range_end-range_begin) <= 65535。 如果 IP 范围较大,则必须增加 65535,如果 IP 范围较小,则可以减小此常数。 如果这个常数太大(例如 40 亿),您将不会节省任何查询时间。
对于此查询,您只需要 range_begin 上的索引。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.