2017-03-20 44 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) 
相關問題