【问题标题】:PHP/MySQL IP to country for Stats pagePHP/MySQL IP 到国家的统计页面
【发布时间】:2012-10-04 07:12:56
【问题描述】:

我目前正在为其他人转换脚本,在他们的旧代码中,他们有一个 CSV 文件,列出了所有 IP 及其国家/地区。文件格式如下:

16777216    17367039    AU  AUS AUSTRALIA
17367040    17432575    MY  MYS MALAYSIA
17432576    17498111    AU  AUS AUSTRALIA
17498112    17563647    KR  KOR REPUBLIC OF KOREA
17563648    17825791    CN  CHN CHINA

好吧,他们就是这样做的,我将它导入到新的 MySQL 数据库中。但问题是他们这里有 111K 记录!

进行比较并找到国家/地区的 IP 大约需要 40 秒。目前他们选择所有记录并与小于和大于下一个字段进行比较。

$datafile = file("iplist.csv");
    $countries = array();
    foreach($datafile as $data) {
        $data = explode(",", $data);
        foreach($iplist as $ipan => $ip) {
            if($ip > $data[0] && $ip < $data[1]) {
                $countries[$ip] = array($data[2], $data[4]);
            }
        }
    }

我当然知道这是一种可怕的方法,太可怕了!当我在 MySQL 中做这件事时甚至更糟糕,我的意思是 111K 记录就在那里。

我整天都在绞尽脑汁,想不出更好的办法来做到这一点。 IP 以标准格式存储在数据库中。我以为我会从 MySQL 中进行选择,然后在 where 语句中做一个多于和少于。但是对于我的生活,我可以想到如何。考虑到这就是他们制作 IP 列表的方式:

foreach($log as $row) {
        $iplist[$row[2]] = $row[2];

        if($last != "") {
            $dif = $last-$row[5];
            $avgtime += $dif;
        }
        $last = $row[5];
    }

我曾想过每条记录调用一次,但如果每个统计页面上出现正常的 120 条记录,那么这将是最少的 120 次 mysql 调用。

谁能给我一个更好更有效的方法来进行比较?

我正在考虑使用外部服务,但请求太多,我认为该网站会被列入黑名单。

【问题讨论】:

  • 为什么不直接导入呢?您没有说明为什么需要比较 IP。
  • 因为在统计页面上它必须显示访问者来自哪个国家。因此IP到国家代码。 PHP 脚本必须确定 IP 来自哪个国家才能显示。

标签: php mysql


【解决方案1】:

为什么不试试这个:

SELECT * FROM table WHERE ip_min >= $your_value and ip_max <= $your_value

ip_min 是第一列数据,ip_max 是第二列。 $your_value 显然是你知道的 ip 值。

另外关于 geoIP 位置,为什么不看看 MaxMind 等标准数据库之一。我个人喜欢使用带有 MaxMind 数据库的 mod_geoip 模块在 Web 服务器上处理国家/地区 ip geoLocation。然后,您可以每月更新此数据库,而不必担心尝试在您的应用程序中保留当前的 ​​IP 数据库。 mod_geoip 只是在 $_SERVER 超全局变量中公开了一些附加值,您可以读取这些值以获取国家代码或国家名称(甚至可以下到城市)。

【讨论】:

  • 这是可行的,但你能想象为从数据库中抓取的每条记录调用一次 MySQL select 语句吗?每次显示统计页面时记录 120 分钟。我已经测试过了,这也需要 30 到 40 秒才能显示页面。
  • @jfreak53 查看我的更新答案,了解如何将其完全移出数据库的评论。我想我最初并没有遵循 120 条记录的意思。是的,如果您尝试批量处理 ip(而不是按脚本执行),那么就没有什么真正直接的想法了。
  • 这是另一个问题,它需要 apache 模块,我们无权访问它。更不用说我有点认为这可能也很慢,但不确定,因为我从未测试过。我的想法是,如果有办法从 IP 列表中获取最小的 IP,然后是最大的 IP,然后从 mysql 中获取介于这两个数字之间的那些记录。只是不确定如何真正做到这一点。 mysql 中的BETWEEN 仅适用于我认为的日期。
【解决方案2】:

你应该在你的mysql查询中使用join:

SELECT s.*, g.country_code, g.country_name FROM stats s LEFT JOIN geoip g ON ( g.ip_min <= s.ip AND g.ip_max >= s.ip ) 

然后你会在一个查询中得到它。

更快的是使用INNER JOIN而不是LEFT JOIN,但它不会选择ip超出范围的记录

无论如何,我认为在插入统计数据表之前找到该位置并使用统计数据记录分配国家/地区的 ID 会更好 - 你还必须加入它,但它会工作得更快

【讨论】:

  • 我不敢相信我以前没有想到这个!谢谢。是的,它的工作原理是它仍然需要 1 分钟。 50 秒。进行搜索 :(,那是 inner。编辑:哦,那只有 5 条记录 limit 5 ;) 谢谢
  • 你有关于 (ip_min, ip_max) 的索引吗?
  • 尝试使用EXPLAIN查询(如EXPLAIN SELECT s.*, g.country_code, g.country_name FROM...),你会看到mysql是否使用任何键/索引,你也可以尝试使用BETWEEN而不是>=和
  • 是的,我对所有相关的列都有索引。这是EXPLAIN给我的:link我的查询是:link
猜你喜欢
  • 2011-05-16
  • 2018-12-04
  • 2012-11-05
  • 2012-02-22
  • 2020-07-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多