【问题标题】:How to store an IP in mySQL如何在 mySQL 中存储 IP
【发布时间】:2010-11-09 16:24:50
【问题描述】:

本周我们在办公室进行了一场健康的辩论。我们正在创建一个 Db 来存储代理信息,除了我们应该如何存储 IP 之外,大多数情况下我们已经制定了架构。一个阵营想要使用 4 个 smallint,一个用于每个八位字节,另一个想要使用 1 个 big int,INET_ATON。

这些表将非常庞大,因此性能是关键。我在中间,因为我通常在我的世界中使用 MS SQL 和 4 个小整数。我对这种类型的卷存储 IP 没有足够的经验。

我们将使用 perl 和 python 脚本来访问数据库,以进一步将数据标准化为其他几个表,以供热门话题、有趣的流量等使用。

我确信社区中有些人已经做了一些与我们正在做的事情类似的事情,我很想听听他们的经验以及哪个路由是最好的,1 个大整数或 4 个小整数作为 IP 地址。

编辑 - 我们关注的一个问题是空间,这个数据库将非常庞大,每天有 500,000,000 条记录。因此,我们正在尝试权衡空间问题和性能问题。

编辑 2 一些话题已经转向我们要存储的数据量……这不是我的问题。问题是哪种方式是存储 IP 地址的最佳方式以及为什么。就像我在 cmets 中所说的那样,我们为一家大型财富 50 强公司工作。我们的日志文件包含来自我们用户的使用数据。反过来,这些数据将在安全上下文中用于驱动某些指标并驱动多个安全工具。

【问题讨论】:

  • @OhioDude:就目前而言,Google 每天的点击量约为200,000,000。我认为每天为500,000,000 的点击做准备正是所谓的“过早优化”。
  • 是的,但是对网页的一次点击不会在日志文件中产生一行。网站上的每个图像和资源在代理日志中都有自己的行。
  • 即使您每页有 100 个图像/css/javascript 文件(不考虑这些资源往往会被浏览器大量缓存),您仍然只有一两个订单与谷歌相差甚远。存储 IP 号码将是您的小问题之一......
  • 这听起来不像是 SQL 数据库的最佳选择。您能否提供有关为什么要使用 SQL 数据库而不是其他存储方法的任何信息?
  • @SpoonMeiser:一天 500M 行大约每秒 6K 行,这不是不可能的,只要它们被正确批处理。

标签: python mysql perl ip-address


【解决方案1】:

我建议查看您将运行哪种类型的查询来决定采用哪种格式。

只有当您需要提取或比较单个八位字节时,您才需要考虑将它们拆分为单独的字段。

否则,将其存储为 4 字节整数。这还有一个好处是允许您使用 MySQL 内置的 INET_ATON()INET_NTOA() 函数。

性能与空间

存储:

如果您只支持 IPv4 地址,那么您在 MySQL 中的数据类型可以是 UNSIGNED INT,它只使用 4 个字节的存储空间。

要存储单个八位字节,您只需要使用 UNSIGNED TINYINT 数据类型,而不是 SMALLINTS,这将占用每个存储空间 1 个字节。

这两种方法都将使用类似的存储空间,但对于单独的字段可能会稍微多一些,从而产生一些开销。

更多信息:

性能:

使用单个字段会产生更好的性能,它是一个比较而不是 4 个。您提到您只会对整个 IP 地址运行查询,因此不需要将八位位组分开。使用 MySQL 的 INET_* 函数将在文本和整数表示之间进行一次转换以进行比较。

【讨论】:

  • 我们的大多数查询都会提取 IP 地址的聚合。例如,不同的 IP 地址如何访问此远程站点。或者这个特定主机在这 8 小时内访问了多少个网站。
  • 如果将其存储为 4 字节整数,请确保不要在其他任何地方对该假设进行硬编码。为什么? IPv6。确保您有明确的升级路径。
【解决方案2】:

BIGINT8 中的 MySQL 字节。

要存储IPv4 地址,UNSINGED INT 就足够了,我认为这是你应该使用的。

我无法想象4 八位字节会比单个INT 获得更多性能,而后者更方便。

另请注意,如果您要发出这样的查询:

SELECT  *
FROM    ips
WHERE   ? BETWEEN start_ip AND end_ip

,其中start_ipend_ip 是表中的列,性能会很差。

这些查询用于确定给定的IP 是否在子网范围内(通常是禁止它)。

为了提高这些查询的效率,您应该将整个范围存储为带有SPATIAL 索引的LineString 对象,然后像这样查询:

SELECT  *
FROM    ips
WHERE   MBRContains(?, ip_range)

有关如何操作的更多详细信息,请参阅我的博客中的此条目:

【讨论】:

    【解决方案3】:

    使用 PostgreSQL,有一个 native data type

    更严重的是,我会落入“一个 32 位整数”阵营。仅当所有四个八位字节一起考虑时,IP 地址才有意义,因此没有理由将八位字节存储在数据库中的单独列中。您会使用三个(或更多)不同的字段来存储电话号码吗?

    【讨论】:

    • 更改 RDBMS 并非易事。我喜欢 pgsql,但只为数据类型选择 dbms 是不合理的。
    • 很想使用 postgres,但是对于这个项目我们必须使用 mySQL
    【解决方案4】:

    拥有单独的字段对我来说听起来不是特别明智 - 就像将邮政编码分成多个部分或电话号码一样。

    如果您想了解有关这些部分的具体信息,可能会有用,但我认为没有真正的理由不使用 32 位 int。

    【讨论】:

    • 可能更多的评论
    【解决方案5】:

    ip 到 int 和 int 到 ip 的有效转换(可能对您有用): (PERL)

    sub ip2dec {
        my @octs = split /\./,shift;
        return ($octs[0] << 24) + ($octs[1] << 16) + ($octs[2] << 8) + $octs[3];
    }
    
    sub dec2ip {
        my $number = shift;
        my $first_oct = $number >> 24;
        my $reverse_1_ = $number - ($first_oct << 24);
        my $secon_oct = $reverse_1_ >> 16;
        my $reverse_2_ = $reverse_1_ - ($secon_oct << 16);
        my $third_oct = $reverse_2_ >> 8;
        my $fourt_oct = $reverse_2_ - ($third_oct << 8);
        return "$first_oct.$secon_oct.$third_oct.$fourt_oct";
    }
    

    【讨论】:

    • 我没有给你投反对票,但如果他的数据库已经具有此功能,我想在 Perl 中执行此操作有点多余。
    【解决方案6】:

    对于 ipv4 和 ipv6 的兼容性,请使用 VARBINARY(16) ,ipv4 将始终为 BINARY(4) 而 ipv6 将始终为 BINARY(16),因此 VARBINARY(16) 似乎是支持两者的最有效方式。并将它们从正常的可读格式转换为二进制,使用 INET6_ATON('127.0.0.1'),并反转它,使用 INET6_NTOA(binary)

    【讨论】:

      【解决方案7】:

      旧线程,但为了读者的利益,考虑使用 ip2long。它将 ip 转换为整数。

      基本上,您将在存储到数据库时使用 ip2long 进行转换,然后在从数据库中检索时使用 long2ip 进行转换。 DB 中的字段类型为 INT,因此与将 ip 存储为字符串相比,您将节省空间并获得更好的性能。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-12-07
        • 1970-01-01
        • 1970-01-01
        • 2015-03-10
        • 2011-10-30
        • 1970-01-01
        • 2015-08-26
        相关资源
        最近更新 更多