我懵了,如果要存IP 地址,用什麼數據類型比較好?

2023.04.27
我懵了,如果要存IP 地址,用什麼數據類型比較好?

IP 地址的本質是32 位無符號整數,類似於192.168.0.1​ 這種點分十進制的字符串寫法只是為了幫助人們理解和記憶,192.168.0.1​ 對應的十進製表示是無符號整數3232235521。

提到IP 地址(IPv4),大夥兒腦子里肯定馬上能浮現類似於192.168.0.1、127.0.0.1 這種常見的IP 地址,然後結合這個問題“MySQL 中用什麼數據類型存IP 地址?”,於是乎脫口而出用char 字符串類型存。

面試官一臉冷漠,你頓時意識到情況不對,又仔細琢磨了一下。

然後發現,這個IP 地址的長度是變化的,最短可以是0.0.0.0 只需要7 位,最長可以是255.255.255.255 需要15 位,於是自信地回答使用varchar(15) 來存儲IP 地址,並為自己能夠想到這一層而暗自竊喜。

誰知面試官竟輕蔑一笑,問你“確定嗎?”,你覺得這是面試官在考驗你,於是堅定的回答“確定”。

然後就開始了下一題

......

人們經常使用varchar(15) 列來存儲IP 地址,但事實上這並不是最優解。

IP 地址的本質是32 位無符號整數,類似於192.168.0.1 這種點分十進制的字符串寫法只是為了幫助人們理解和記憶,192.168.0.1 對應的十進製表示是無符號整數3232235521。

所以,說用字符串類型存IP 地址的,其實是潛意識中以為IP 地址是字符串,存的是點分十進制的字符串,但正確的應該是存32 位的無符號整數

所謂有符號數其實就是將最高位作為符號位,比如32 位的有符號INT,最高位是符號位,剩下31 位才是真實的數值,所以有符號INT 的取值區間為:

無符號INT 的取值區間為:

下表列出了MySQL 出各個整數類型有符號和無符號的的取值範圍,在定義表時,可以在數據類型後面添加關鍵字UNSIGNED 來定義無符號整數,否則默認為有符號整數:

類型

有符號數取值範圍

無符號數取值範圍

TINYINT(1 字節,8 bit)

-128 〜 127

0 〜 255

SMALLINT(2 字節,16 bit)

-32768 〜 32767

0 〜 65535

MEDIUMINT(3 字節,24 bit)

-8388608 〜 8388607

0 〜 16777215

INT(4 字節,32 bit)

-2147483648 〜 2147483647

0 〜 4294967295

BIGINT(8 字節,64 bit)

-9223372036854775808 〜 9223372036854775807

0 〜 18446744073709551615

結合上表,可以看出,32 位的無符號 INT 正好可以容納IPv4 地址,下面是 INT UNSIGNED 和 VARCHAR(15) 兩種數據類型的對比:

  1. 存儲空間:4 字節的 INT 類型15 字節的 VARCHAR(15) 更加節省存儲空間。另外,VARCHAR 除了會保存需要的字符數,還會另加一個字節來記錄長度(如果列聲明的長度超過255,則使用兩個字節記錄長度),所以 VARCHAR(15) 其實要佔用16 個字節。
  2. 檢索速度:如果我們要在IP 地址上建立索引,那麼對於字符串索引來說,整數索引的檢索速度簡直就是降緯打擊了,因為字符串類型的比較是需要從第一位字符開始遍歷依次進行的,速度較慢。

MySQL 非常貼心地提供了IPv4 地址點分十進制和無符號整數的相互轉換函數,inet_aton 和 inet_ntoa(底層是二進制移位操作,速度很快):

圖片

當然你更應該在業務中去執行這些轉換,減輕MySQL 的壓力。