在看公司項目代碼時,有涉及到ip位址存儲,
使用的是varbinary(4),但沒有文檔說明
這引發我的思考——緣起
當設計一個資料表時,考慮使用何種列的資料類型對性能有比較大的影響,如存儲空間、查詢開銷等。甚至還影響到一些操作,如ip位址以字元串的形式存儲在資料庫中,就不可以直接比較大小。還有一點需要考慮,那就是可讀性!資料雖然是存儲在資料庫中,但也要考慮到可讀性問題。
本文要探讨的是“ip位址在資料庫中,應該使用何種形式存儲?”,文章将以實驗為基礎介紹使用何種形式比較适合。
大
家都知道ip位址分為ipv4、ipv6,這裡我以ipv4為例介紹,ipv6原理是一樣的。ipv4的小為32bits(或者說是4bytes),在使
用過程中,我們通常是用點分十進制格式,如192.168.120.65。如何把"192.168.120.65"存儲到資料庫中呢?
我們考慮下面三個因素:
可讀性
存儲效率
查詢效率
把"192.168.120.65"存儲到資料庫中有多少中可行方法呢?見下表所示:
<b>資料類型</b>
<b>大小</b>
<b>注釋</b>
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個字段來分開存儲ip位址,可讀性稍差(分别為192, 168, 120, 65),存儲空間占用少
varbinary(4)
可讀性差(0xc0a87841),存儲空間占用少
從大小來看,依次varchar(15)> bigint> int、tinyint、varbinary(4)。
從可讀性來看,依次是varchar(15)> bigint> tinyint> varbinary(4)>int。
從查詢效率來看,
綜合考慮,似乎tinyint比較好,其次是varbinary(4)。但是tinyint需要占多個表字段,而varbinary隻需要占用一個字段即可。正确性還有待下面的實驗檢查!!!
本
小節通過建立5張表,分别用上述5中資料類型存儲ip位址,每張表插入1,000,000條記錄。說明為了友善消除差異,這些表中插入的都是
192.168.120.65。建表和插入資料的sql語句如下(說明:插入1,000,000條記錄要花挺長時間的,如果你要自己實驗,可以考慮少插入
點資料):
然後我們執行存儲過程sp_spaceused檢視空間效率,執行下面的sql語句:
可以得到下面的結果:

說明:上面各個字段的意思如下表所示 列名 資料類型 說明 <b>reserved</b> varchar(18) 由資料庫中對象配置設定的空間總量。 <b>data</b> 資料使用的空間總量。 <b>index_size</b> 索引使用的空間總量。 <b>unused</b> 為資料庫中的對象保留但尚未使用的空間總量。
可以看出,這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個位元組,如下圖
如果有知道的,請告訴我一聲!不過讓我從這兩者之間選(信不過資料結果啊),肯定會選擇使用varbinary(4)而不是bigint。如果能夠證明資料結果沒有錯,應該選擇bigint,因為他的可讀性更好!
本小節比較上述5中存儲ip位址的查詢效率。為了比較查詢效率,這裡重新插入資料,消除每張表中的記錄都相同(192.168.120.65),下面編寫存儲過程像資料表中随機插入1000條記錄(但是保證每張表的資料是一樣的)。存儲過程如下:
考慮查找在範圍192.0.0.0~192.255.255.255之間的ip位址的查詢效率問題。說明我忽略了預處理的開銷,即将
192.0.0.0和192.255.255.255轉換為上述的5種類型的時間,代碼中我直接使用了這些值,沒有給出轉換過程,具體代碼如下:
執行得到的消息如下:
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 毫秒。
=============================共115毫秒,ip_address_varchar
表 'ip_address_bigint'。掃描計數 1,邏輯讀取 5 次,實體讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 實體讀取 0 次,lob 預讀 0 次。
(2 行受影響)
===================================共4毫秒,ip_address_bigint
表 'ip_address_int'。掃描計數 1,邏輯讀取 5 次,實體讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 實體讀取 0 次,lob 預讀 0 次。
cpu 時間 = 0 毫秒,占用時間 = 146 毫秒。
===================================共149毫秒,ip_address_int
表 'ip_address_tinyint'。掃描計數 1,邏輯讀取 5 次,實體讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 實體讀取 0 次,lob 預讀 0 次。
cpu 時間 = 0 毫秒,占用時間 = 85 毫秒。
=======================================共88毫秒,ip_address_tinyint
表 'ip_address_varbinary'。掃描計數 1,邏輯讀取 5 次,實體讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 實體讀取 0 次,lob 預讀 0 次。
cpu 時間 = 0 毫秒,占用時間 = 13 毫秒。
===================================共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)。
——熬夜搞這問題,設計實驗和測試方法,頭腦現在有些不清醒了,不知實驗資料和結論有沒有出問題,有發現的請糾正!