1. 程式人生 > >論IP地址在資料庫中應該用何種形式儲存

論IP地址在資料庫中應該用何種形式儲存

轉自:http://www.cnblogs.com/skynet/archive/2011/01/09/1931044.html

在看公司專案程式碼時,有涉及到ip地址儲存,

使用的是varbinary(4),但沒有文件說明

這引發我的思考——緣起

當設計一個數據表時,考慮使用何種列的資料型別對效能有比較大的影響,如儲存空間、查詢開銷等。甚至還影響到一些操作,如ip地址以字串的形式儲存在資料庫中,就不可以直接比較大小。還有一點需要考慮,那就是可讀性!資料雖然是儲存在資料庫中,但也要考慮到可讀性問題。

本文要探討的是“IP地址在資料庫中,應該使用何種形式儲存?”,文章將以實驗為基礎介紹使用何種形式比較適合。

1、感性認識

大家都知道ip地址分為ipv4、ipv6,這裡我以ipv4為例介紹,ipv6原理是一樣的。ipv4的小為32bits(或者說是4Bytes),在使用過程中,我們通常是用點分十進位制格式,如192.168.120.65。如何把"192.168.120.65"儲存到資料庫中呢?

我們考慮下面三個因素:

  • 可讀性
  • 儲存效率
  • 查詢效率

把"192.168.120.65"儲存到資料庫中有多少中可行方法呢?見下表所示:

資料型別

大小

註釋

varchar(15)

佔7~15位元組

可讀性最好(192.168.120.65),但是最費儲存空間

bigint

8 位元組

可以將ip地址儲存為類似192168120065的格式,這種可讀性稍差,也比較費儲存空間

int

4 位元組

這種可讀性很差,會儲存為1084782657,由192*16777216+168*65536+120*256+65-2147483648計算所得,佔用儲存空間少。

tinyint

4 位元組

用4個欄位來分開儲存ip地址,可讀性稍差(分別為192, 168, 120, 65),儲存空間佔用少

varbinary(4)

4 位元組

可讀性差(0xC0A87841),儲存空間佔用少

從大小來看,依次varchar(15)> bigint> int、tinyint、varbinary(4)。

從可讀性來看,依次是varchar(15)> bigint> tinyint> varbinary(4)>int。

從查詢效率來看,

綜合考慮,似乎tinyint比較好,其次是varbinary(4)。但是tinyint需要佔多個表字段,而varbinary只需要佔用一個欄位即可。正確性還有待下面的實驗檢查!!!

2、理性認識

本小節通過建立5張表,分別用上述5中資料型別儲存ip地址,每張表插入1,000,000條記錄。說明為了方便消除差異,這些表中插入的都是192.168.120.65。建表和插入資料的sql語句如下(說明:插入1,000,000條記錄要花挺長時間的,如果你要自己實驗,可以考慮少插入點資料):

建表和插入資料的sql語句

然後我們執行儲存過程sp_spaceused檢視空間效率,執行下面的sql語句:

exec sp_spaceused ip_address_varchar
exec sp_spaceused ip_address_bigint
exec sp_spaceused ip_address_int
exec sp_spaceused ip_address_tinyint
exec sp_spaceused ip_address_varbinary

可以得到下面的結果:

image

說明:上面各個欄位的意思如下表所示

列名

資料型別

說明

reserved

varchar(18)

由資料庫中物件分配的空間總量。

data

varchar(18)

資料使用的空間總量。

index_size

varchar(18)

索引使用的空間總量。

unused

varchar(18)

為資料庫中的物件保留但尚未使用的空間總量。

可以看出,這5張表中的記錄都是1000000,ip_address_varchar佔空間最大30792 KB;其次是ip_address_bigint和ip_address_varbinary佔用16904 KB;最後是ip_address_int和ip_address_tinyint只佔用16904 KB。

所以從可讀性和空間效率上來看,最理想的是用tinyint的資料型別儲存ip地址。其次應該考慮varbinary(4)bigint

理論上bigint肯定要比varbinary佔用空間多,可是實驗得出來是一樣的,為什麼呢?我檢視幫助資訊也沒有看出什麼異常,varbinary(4)的確是佔用4個位元組、bigint也的確是佔用8個位元組,如下圖

image

image

如果有知道的,請告訴我一聲!不過讓我從這兩者之間選(信不過資料結果啊),肯定會選擇使用varbinary(4)而不是bigint。如果能夠證明資料結果沒有錯,應該選擇bigint,因為他的可讀性更好!

3、查詢效率

本小節比較上述5中儲存ip地址的查詢效率。為了比較查詢效率,這裡重新插入資料,消除每張表中的記錄都相同(192.168.120.65),下面編寫儲存過程像資料表中隨機插入1000條記錄(但是保證每張表的資料是一樣的)。儲存過程如下:

隨機插入N條ip地址到5張表中

考慮查詢在範圍192.0.0.0~192.255.255.255之間的ip地址的查詢效率問題。說明我忽略了預處理的開銷,即將192.0.0.0和192.255.255.255轉換為上述的5種類型的時間,程式碼中我直接使用了這些值,沒有給出轉換過程,具體程式碼如下:

查詢192.0.0.0~192.255.255.255之間的ip地址


執行得到的訊息如下:

SQL Server 分析和編譯時間: 
   CPU 時間 = 0 毫秒,佔用時間 = 1 毫秒。

(5 行受影響)
表 'ip_address_varchar'。掃描計數 1,邏輯讀取 6 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。

(3 行受影響)

(1 行受影響)

SQL Server 執行時間:
   CPU 時間 = 0 毫秒,佔用時間 = 113 毫秒。

SQL Server 執行時間:
   CPU 時間 = 0 毫秒,佔用時間 = 1 毫秒。

SQL Server 執行時間:
   CPU 時間 = 0 毫秒,佔用時間 = 1 毫秒。
SQL Server 分析和編譯時間: 
   CPU 時間 = 0 毫秒,佔用時間 = 1 毫秒。

=============================共115毫秒,ip_address_varchar

(5 行受影響)
表 'ip_address_bigint'。掃描計數 1,邏輯讀取 5 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。

(2 行受影響)

(1 行受影響)

SQL Server 執行時間:
   CPU 時間 = 0 毫秒,佔用時間 = 1 毫秒。

SQL Server 執行時間:
   CPU 時間 = 0 毫秒,佔用時間 = 1 毫秒。

SQL Server 執行時間:
   CPU 時間 = 0 毫秒,佔用時間 = 1 毫秒。
SQL Server 分析和編譯時間: 
   CPU 時間 = 0 毫秒,佔用時間 = 1 毫秒。

===================================共4毫秒,ip_address_bigint

(5 行受影響)
表 'ip_address_int'。掃描計數 1,邏輯讀取 5 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。

(2 行受影響)

(1 行受影響)

SQL Server 執行時間:
   CPU 時間 = 0 毫秒,佔用時間 = 146 毫秒。

SQL Server 執行時間:
   CPU 時間 = 0 毫秒,佔用時間 = 1 毫秒。

SQL Server 執行時間:
   CPU 時間 = 0 毫秒,佔用時間 = 1 毫秒。
SQL Server 分析和編譯時間: 
   CPU 時間 = 0 毫秒,佔用時間 = 1 毫秒。

===================================共149毫秒,ip_address_int

(5 行受影響)
表 'ip_address_tinyint'。掃描計數 1,邏輯讀取 5 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。

(2 行受影響)

(1 行受影響)

SQL Server 執行時間:
   CPU 時間 = 0 毫秒,佔用時間 = 85 毫秒。

SQL Server 執行時間:
   CPU 時間 = 0 毫秒,佔用時間 = 1 毫秒。

SQL Server 執行時間:
   CPU 時間 = 0 毫秒,佔用時間 = 1 毫秒。
SQL Server 分析和編譯時間: 
   CPU 時間 = 0 毫秒,佔用時間 = 1 毫秒。

=======================================共88毫秒,ip_address_tinyint

(5 行受影響)
表 'ip_address_varbinary'。掃描計數 1,邏輯讀取 5 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。

(2 行受影響)

(1 行受影響)

SQL Server 執行時間:
   CPU 時間 = 0 毫秒,佔用時間 = 13 毫秒。

SQL Server 執行時間:
   CPU 時間 = 0 毫秒,佔用時間 = 1 毫秒。

SQL Server 執行時間:
   CPU 時間 = 0 毫秒,佔用時間 = 1 毫秒。

===================================共15毫秒,ip_address_varbinary

上述結果只是初略的估計了效率,可能不太精確,但還是具有一定參考價值的!我只看ip_address_varbinary(15毫秒)、ip_address_tinyint(88毫秒)、ip_address_bigint(4毫秒)。

效率差距還是挺大的,綜合可讀性、儲存效率、查詢效率,我給這三者排序是:

如果考慮儲存效率,tinyint是最好的!其次是bigint,然後是varbinary(4)

如果更多的是考慮查詢效率,bigint是最好的!其次是varbinary(4),然後是tinyint

如果加我選擇,我會使用varbinary(4)。

——熬夜搞這問題,設計實驗和測試方法,頭腦現在有些不清醒了,不知實驗資料和結論有沒有出問題,有發現的請糾正!