2017-03-20 41 views
0

我发现这个方法来存储在MySQL数据库作为整数IP地址使用INET_ATON:https://stackoverflow.com/a/5133610/4491952如何在MySQL中使用INET_ATON进行通配符搜索IP地址?

由于IPv4地址是4字节长,你可以使用恰好有4个字节,一个INT (UNSIGNED)

`ipv4` INT UNSIGNED 

而且INET_ATONINET_NTOA将它们转换:

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

对于IPv6地址,你可以使用一个BINARY代替:

`ipv6` BINARY(16) 

而且使用PHP’s inet_ptoninet_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']); 

但我怎么可以做一个通配符搜索,例如192.168%,使用INET_ATON和PHP的ip2long功能?

+5

你不这样做,通配符只对文本有意义。你会搜索一个很好的旧的“BETWEEN”范围。 –

+0

@ÁlvaroGonzález如果我将192.168.1.1这样的IP地址存储为192168001001(int 12)而不是包含点并使用varchar(15),那么这是一种不错的方法吗?然后我可以做通配符搜索。 –

+1

这样你就可以获得字符串比较的效率和二进制流的可读性。给我一分钟,我想我会写一个答案。 –

回答

2

通配符搜索对字符串进行操作,并且由于它通常无法从索引获益,因此它往往非常缓慢。

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

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

即使这些INET_ATON()实例是单纯的可读性,你可以只输入产生的整数:

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

...可以作为被改写。如果你使用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的正常工作。

2

MySQL提供的一个巧妙的技巧是位移。您可以使用它来查看IP是否包含在以cidr表示法写的地址块中。您可以使用此方法将您的地址视为X.X.X.X/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) 
相关问题