2

我发现这种方法可以使用 INET_ATON 将 MySQL 数据库中的 IP 地址存储为整数:https ://stackoverflow.com/a/5133610/4491952

由于 IPv4 地址有 4 个字节长,您可以使用正好有 4 个字节的INT( UNSIGNED)

`ipv4` INT UNSIGNED

INET_ATON转换INET_NTOA它们:

INSERT INTO `table` (`ipv4`) VALUES (INET_ATON("127.0.0.1"));
SELECT INET_NTOA(`ipv4`) FROM `table`;

对于 IPv6 地址,您可以使用 aBINARY代替:

`ipv6` BINARY(16)

并使用PHP 的inet_ptonandinet_ntop进行转换:

'INSERT INTO `table` (`ipv6`) VALUES ("'.mysqli_real_escape_string(inet_pton('2001:4860:a005::68')).'")'
'SELECT `ipv6` FROM `table`'
$ipv6 = inet_pton($row['ipv6']);

但是如何使用 INET_ATON 和 PHP 的 ip2long 函数进行通配符搜索,例如 192.168.%?

4

2 回答 2

5

MySQL 提供的一个巧妙的技巧是位移。您可以使用它来查看 ip 是否包含在以 cidr 表示法编写的地址块中。您可以使用此方法将您的地址视为 XXXX/16 cidr 块。

set @cidr_block:='10.20.30.40/16';

select inet_ntoa(inet_aton(substring_index(@cidr_block,'/',1))>>(32-substring_index(@cidr_block,'/',-1))<<(32-substring_index(@cidr_block,'/',-1))) as first_ip,
                 inet_aton(substring_index(@cidr_block,'/',1))>>(32-substring_index(@cidr_block,'/',-1))<<(32-substring_index(@cidr_block,'/',-1))  as first_ip_num,
        inet_ntoa((((inet_aton(substring_index(@cidr_block,'/',1))>>(32-substring_index(@cidr_block,'/',-1)))+1)<<(32-substring_index(@cidr_block,'/',-1)))-1) as last_ip,
                 (((inet_aton(substring_index(@cidr_block,'/',1))>>(32-substring_index(@cidr_block,'/',-1)))+1)<<(32-substring_index(@cidr_block,'/',-1)))-1  as last_ip_num
;
+-----------+--------------+---------------+-------------+
| first_ip  | first_ip_num | last_ip       | last_ip_num |
+-----------+--------------+---------------+-------------+
| 10.20.0.0 |    169082880 | 10.20.255.255 |   169148415 |
+-----------+--------------+---------------+-------------+
1 row in set (0.00 sec)

查看 ip 是否在地址块中的快捷方式 - 只需筛选 cidr 地址和 ip 以查看它们是否相同。当然,如果应用于存储的值,这将是一个表扫描。

select inet_aton('127.0.0.1')>>16 = inet_aton('127.0.10.20')>>16 as `1 = true`;
+----------+
| 1 = true |
+----------+
|        1 |
+----------+
1 row in set (0.00 sec)

select inet_aton('127.0.0.1')>>16 = inet_aton('127.10.10.20')>>16 as `0 =  false`;
 +-----------+
 | 0 = false |
 +-----------+
 |         0 |
 +-----------+
 1 row in set (0.00 sec)
于 2017-03-20T16:49:50.707 回答
4

通配符搜索对字符串进行操作,因为它通常不能从索引中受益,所以它往往非常慢。

如果您将 IP 地址以针对机器的规范化表示形式存储(相对于人类可读的点表示法),您可以将它们视为数字,使用许多标准运算符并充分利用索引。一个例子:

SELECT *
FROM foo
WHERE dot_notation LIKE '192.168.%';

...可以重写为:

SELECT *
FROM foo
WHERE as_integer BETWEEN INET_ATON('192.168.0.0') AND INET_ATON('192.168.255.255');

即使这些INET_ATON()实例只是为了便于阅读,您也可以只输入结果整数。如果您使用 PHP,这很简单,因为您可以将其外包给 PHP:

$sql = 'SELECT *
    FROM foo
    WHERE as_integer BETWEEN ? AND ?';
$params = [
   // Not sure whether you still need the sprintf('%u') trick in 64-bit PHP
   ip2long('192.168.0.0'), ip2long('192.168.255.255')
];

我现在无法对其进行测试,但我知道这也适用于 IPv6。

于 2017-03-20T16:37:22.943 回答