MySQL:如何进行更快的 IP 范围查询?地理IP

标签 mysql ip

我有一个 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

我现在有一个 UNIQUE 综合索引(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

最佳答案

我正在处理一个类似的问题,我必须搜索一个具有大约 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 上的索引。

关于MySQL:如何进行更快的 IP 范围查询?地理IP,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41079741/

相关文章:

MySQL 如何对这个查询进行排序?

ruby-on-rails - 从私有(private) NIC 访问时,Rails 显示 IP 为 127.0.0.1,但 Nginx 显示正确的 IP。公共(public) IP 可以正常转发

ip - 无法连接作为 headless (headless)服务创建的 Kubernetes 端点 IP

ip - 使用私有(private) IP 配置 Cassandra 以进行节点间通信

mysql - 将具有相似但不相同表的数据库合并到一个 MasterDB 中

mysql - 如何在不传递参数的情况下调用存储过程?

android - 当我使用 JSONParser (httpurlconnection) 运行我的 android 登录 Activity 时出现异常

mysql - 如何在mysql中通过多个表的多个连接来组合多行

Java - 打印给定范围内的随机IP地址

ubuntu - 如何与另一台计算机共享我的本地服务器?