【问题标题】:MySQL: How to make a faster IP range query? GeoIPMySQL:如何进行更快的 IP 范围查询?地理IP
【发布时间】:2017-04-26 01:44:47
【问题描述】:

我有一个 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”表包含 250 万行。 “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”功能,看起来它仍然搜索了 120 万行:

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

【问题讨论】:

  • where range_begin&lt;=iplong and range_end&gt;=iplong 有什么改善吗?我认为它会是一样的,但我会先尝试这个.. 复合索引 (range_begin, range_end) 也应该有帮助
  • 我更改了查询并创建了一个复合索引。它似乎工作得更好,不过我见过的最长查询时间是 0.30 秒。我可以发誓我之前尝试过这个但它没有用(从 0.001 秒到 4 秒的速度不一致),仍然不确定我看到的是实际查询时间还是缓存查询时间 - 我会留意它.谢谢
  • 我只看到它运行在0.81秒,查询时间仍然很不一致,变化很大。
  • 请注意,我现在拥有的索引是唯一的综合索引(range_begin,range_end)。也许这是最快的速度?
  • @peppy - 0.001s 可能意味着“查询缓存”处于活动状态,并且您刚刚运行了相同的查询。

标签: mysql ip


【解决方案1】:

我正在处理一个类似的问题,我必须搜索一个包含大约 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'。第一个 AND 条件 (range_begin

然而,这可以通过添加一个 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)

对于这个查询,您只需要 range_begin 上的索引。

【讨论】:

  • 有趣的组合。但是,调整 65535 至关重要。而且申请IPv6恐怕也无望了。
【解决方案2】:

花一些时间思考为什么常规索引在这种情况下无用,这是一个非常有用的练习。实际上,如果您可以让查询使用索引,您会发现它可能比运行全表扫描要慢。

解释为什么会占用比此处更多的空间。有一个解决方案——就是把ipaddress数据库当作一维空间,使用空间索引。但是 mysql 空间索引只能在二维空间中工作 - 因此您需要将坐标映射到二维空间中,如 here

所述

请注意,大于/限制方法虽然比空间索引快,但在开始处理嵌套子网时会变得混乱。

【讨论】:

  • 我认为我成功地设置了这个解决方案,总共有 250 万行。我在一个循环中查询了 400 个不同的 IP 地址(400 个单独的 mysql 查询)作为测试。总时间大约用了 3 秒。这听起来对吗?
  • 这是我第一次使用多边形变量类型和空间索引。到目前为止,该解决方案似乎运行良好!
【解决方案3】:

在我查看数据之前,还有更简单的方法让我无法理解。

首先,运行

SELECT * FROM Ip2location WHERE ip_from <= $IPAddress ORDER BY ip_from DESC LIMIT 1

这将返回等于或小于您正在搜索的值的最接近的 IP,因为您正在返回数据库中的最高单个值。

接下来,当您返回整行时,只需确保 ip_to 大于或等于 IP,您就可以确保 IP 在该范围内。

如果 IP 不在该范围内(如果 ip_to 更小),则表示该 IP 没有记录。

简单且执行迅速!

【讨论】:

    猜你喜欢
    • 2013-11-23
    • 1970-01-01
    • 1970-01-01
    • 2019-08-30
    • 1970-01-01
    • 1970-01-01
    • 2018-07-16
    • 2020-11-10
    • 1970-01-01
    相关资源
    最近更新 更多