西西軟件園多重安全檢測下載網(wǎng)站、值得信賴的軟件下載站!
軟件
軟件
文章
搜索

首頁西西教程數(shù)據(jù)庫教程 → IP地址在數(shù)據(jù)庫中的存儲形式分析

IP地址在數(shù)據(jù)庫中的存儲形式分析

相關(guān)軟件相關(guān)文章發(fā)表評論 來源:本站整理時間:2011/1/9 9:34:35字體大。A-A+

作者:佚名點擊:824次評論:0次標簽: IP地址

  • 類型:IP 工具大小:2.0M語言:中文 評分:6.0
  • 標簽:
立即下載
 當設(shè)計一個數(shù)據(jù)表時,考慮使用何種列的數(shù)據(jù)類型對性能有比較大的影響,如存儲空間、查詢開銷等。甚至還影響到一些操作,如ip地址以字符串的形式存儲在數(shù)據(jù)庫中,就不可以直接比較大小。還有一點需要考慮,那就是可讀性!數(shù)據(jù)雖然是存儲在數(shù)據(jù)庫中,但也要考慮到可讀性問題。

本文要探討的是“IP地址在數(shù)據(jù)庫中,應(yīng)該使用何種形式存儲?”,文章將以實驗為基礎(chǔ)介紹使用何種形式比較適合。

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

我們考慮下面三個因素:

可讀性
存儲效率
查詢效率
把"192.168.120.65"存儲到數(shù)據(jù)庫中有多少中可行方法呢?見下表所示:

數(shù)據(jù)類型
大小
注釋

varchar(15)
占7~15字節(jié)
可讀性最好(192.168.120.65),但是最費存儲空間

bigint
8 字節(jié)
可以將ip地址存儲為類似192168120065的格式,這種可讀性稍差,也比較費存儲空間

int
4 字節(jié)
這種可讀性很差,會存儲為1084782657,由192*16777216+168*65536+120*256+65-2147483648計算所得,占用存儲空間少。

tinyint
4 字節(jié)
用4個字段來分開存儲ip地址,可讀性稍差(分別為192, 168, 120, 65),存儲空間占用少

varbinary(4)
4 字節(jié)
可讀性差(0xC0A87841),存儲空間占用少


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

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

從查詢效率來看,

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

2、理性認識
本小節(jié)通過創(chuàng)建5張表,分別用上述5中數(shù)據(jù)類型存儲ip地址,每張表插入1,000,000條記錄。說明為了方便消除差異,這些表中插入的都是192.168.120.65。建表和插入數(shù)據(jù)的sql語句如下(說明:插入1,000,000條記錄要花挺長時間的,如果你要自己實驗,可以考慮少插入點數(shù)據(jù)):

建表和插入數(shù)據(jù)的sql語句create database ip_address_test;
go

use ip_address_test
/*****it defines ip address as varchar(15)*****/
create table ip_address_varchar(
id int identity(1,1) not null primary key,
ipAddress varchar(15)
);
/*****it defines ip address as bigint*****/
create table ip_address_bigint(
id int identity(1,1) not null primary key,
ipAddress bigint
);
/*****it defines ip address as int*****/
create table ip_address_int(
id int identity(1,1) not null primary key,
ipAddress int
);
/*****it defines ip address as tinyint*****/
create table ip_address_tinyint(
id int identity(1,1) not null primary key,
ip_address1 tinyint,
ip_address2 tinyint,
ip_address3 tinyint,
ip_address4 tinyint
);
/*****it defines ip address as varbinary(4)*****/
create table ip_address_varbinary(
id int identity(1,1) not null primary key,
ipAddress1 varbinary(4)
);


/*****insert data into tables*****/
declare @i int, @ip varchar(15)
set @i = 0
set @ip = '192.168.120.65'
while @i < 1000000
begin
/**** insert into ip_address_varchar values ****/
insert ip_address_varchar values(@ip)

/**** insert into ip_address_bigint values ****/
insert ip_address_bigint values(
convert( bigint,
right('000'+convert(varchar(3), parsename(@ip, 4)),3)+
right('000'+convert(varchar(3), parsename(@ip, 3)),3)+
right('000'+convert(varchar(3), parsename(@ip, 2)),3)+
right('000'+convert(varchar(3), parsename(@ip, 1)),3)
)
)

/**** insert into ip_address_int values ****/
insert ip_address_int values(
cast(
(cast(parsename(@ip, 4) as bigint)*16777216)+
(cast(parsename(@ip, 3) as bigint)*65536)+
(cast(parsename(@ip, 2) as bigint)*256)+
cast(parsename(@ip, 1) as bigint)
-2147483648
as int)
)

/**** insert into ip_address_tinyint values ****/
insert ip_address_tinyint values(
convert(tinyint, parsename(@ip, 4)),
convert(tinyint, parsename(@ip, 3)),
convert(tinyint, parsename(@ip, 2)),
convert(tinyint, parsename(@ip, 1))
)

/**** insert into ip_address_varbinary values ****/
insert ip_address_varbinary values(
cast( convert(tinyint, parsename(@ip, 4)) as varbinary)+
cast( convert(tinyint, parsename(@ip, 3)) as varbinary)+
cast( convert(tinyint, parsename(@ip, 2)) as varbinary)+
cast( convert(tinyint, parsename(@ip, 1)) as varbinary)
)

set @i = @i + 1
end
然后我們執(zhí)行存儲過程sp_spaceused查看空間效率,執(zhí)行下面的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

可以得到下面的結(jié)果:




說明:上面各個字段的意思如下表所示




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

所以從可讀性和空間效率上來看,最理想的是用tinyint的數(shù)據(jù)類型存儲ip地址。其次應(yīng)該考慮varbinary(4)和bigint。

理論上bigint肯定要比varbinary占用空間多,可是實驗得出來是一樣的,為什么呢?我查看幫助信息也沒有看出什么異常,varbinary(4)的確是占用4個字節(jié)、bigint也的確是占用8個字節(jié),如下圖



如果有知道的,請告訴我一聲!不過讓我從這兩者之間選(信不過數(shù)據(jù)結(jié)果。隙〞x擇使用varbinary(4)而不是bigint。如果能夠證明數(shù)據(jù)結(jié)果沒有錯,應(yīng)該選擇bigint,因為他的可讀性更好!

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

隨機插入N條ip地址到5張表中use ip_address_test
declare @ip1 tinyint, @ip2 tinyint, @ip3 tinyint, @ip4 tinyint, @i int
set @i = 1
while @i <= 1000
begin
set @ip1 = FLOOR(256*RAND(cast(cast(left(newid(),8) as varbinary ) as int )) )
set @ip2 = FLOOR(256*RAND(cast(cast(left(newid(),8) as varbinary ) as int )) )
set @ip3 = FLOOR(256*RAND(cast(cast(left(newid(),8) as varbinary ) as int )) )
set @ip4 = FLOOR(256*RAND(cast(cast(left(newid(),8) as varbinary ) as int )) )

/**** insert into ip_address_varchar ****/
declare @ip_varchar varchar(15)
set @ip_varchar = cast(@ip1 as varchar)+'.'+
cast(@ip2 as varchar)+'.'+
cast(@ip3 as varchar)+'.'+
cast(@ip4 as varchar)
insert into ip_address_varchar values(@ip_varchar)

/**** insert into ip_address_bigint ****/
declare @ip_bigint bigint
set @ip_bigint = convert( bigint,
right('000'+convert(varchar(3), @ip1),3)+
right('000'+convert(varchar(3), @ip2),3)+
right('000'+convert(varchar(3), @ip3),3)+
right('000'+convert(varchar(3), @ip4),3)
)
insert into ip_address_bigint values(@ip_bigint)

/**** insert into ip_address_int ****/
declare @ip_int int
set @ip_int = cast(
(cast(@ip1 as bigint)*16777216)+
(cast(@ip2 as bigint)*65536)+
(cast(@ip3 as bigint)*256)+
cast(@ip4 as bigint)
-2147483648
as int)
insert into ip_address_int values(@ip_int)

/**** insert into ip_address_tinyint ****/
insert into ip_address_tinyint values(@ip1,@ip2,@ip3,@ip4)

/**** insert into ip_address_varbinary ****/
declare @ip_varbinary varbinary(4)
set @ip_varbinary = cast( convert(tinyint, @ip1) as varbinary)+
cast( convert(tinyint, @ip2) as varbinary)+
cast( convert(tinyint, @ip3) as varbinary)+
cast( convert(tinyint, @ip4) as varbinary)
insert into ip_address_varbinary values(@ip_varbinary)

set @i = @i + 1
end
考慮查找在范圍192.0.0.0~192.255.255.255之間的ip地址的查詢效率問題。說明我忽略了預(yù)處理的開銷,即將192.0.0.0和192.255.255.255轉(zhuǎn)換為上述的5種類型的時間,代碼中我直接使用了這些值,沒有給出轉(zhuǎn)換過程,具體代碼如下:

查詢192.0.0.0~192.255.255.255之間的ip地址use ip_address_test
set statistics profile on
set statistics io on
set statistics time on
/**** find from ip_address_varchar ****/
select * from ip_address_varchar
where(
cast(parsename(ipAddress, 4) as int) between 192 and 192
and cast(parsename(ipAddress, 3) as int) between 0 and 255
and cast(parsename(ipAddress, 2) as int) between 0 and 255
and cast(parsename(ipAddress, 1) as int) between 0 and 255
)
set statistics profile off
set statistics io off
set statistics time off

set statistics profile on
set statistics io on
set statistics time on
/*****find from ip_address_bigint*****/
select * from ip_address_bigint
where(
ipAddress between 192000000000 and 192255255255
)
set statistics profile off
set statistics io off
set statistics time off

set statistics profile on
set statistics io on
set statistics time on
/*****find from ip_address_int*****/
select * from ip_address_int
where(
ipAddress between 1073741824 and 1090519039
)
set statistics profile off
set statistics io off
set statistics time off

set statistics profile on
set statistics io on
set statistics time on
/*****find from ip_address_tinyint*****/
select * from ip_address_tinyint
where(
ip_address1 between 192 and 192
and ip_address2 between 0 and 255
and ip_address3 between 0 and 255
and ip_address4 between 0 and 255
)
set statistics profile off
set statistics io off
set statistics time off

set statistics profile on
set statistics io on
set statistics time on
/*****find from ip_address_varbinary*****/
select * from ip_address_varbinary
where(
ipAddress1 between 0xC0000000 and 0xC0FFFFFF
)
set statistics profile off
set statistics io off
set statistics time off執(zhí)行得到的消息如下:

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

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

(3 行受影響)

(1 行受影響)

SQL Server 執(zhí)行時間:
CPU 時間 = 0 毫秒,占用時間 = 113 毫秒。

SQL Server 執(zhí)行時間:
CPU 時間 = 0 毫秒,占用時間 = 1 毫秒。

SQL Server 執(zhí)行時間:
CPU 時間 = 0 毫秒,占用時間 = 1 毫秒。
SQL Server 分析和編譯時間:
CPU 時間 = 0 毫秒,占用時間 = 1 毫秒。

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

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

(2 行受影響)

(1 行受影響)

SQL Server 執(zhí)行時間:
CPU 時間 = 0 毫秒,占用時間 = 1 毫秒。

SQL Server 執(zhí)行時間:
CPU 時間 = 0 毫秒,占用時間 = 1 毫秒。

SQL Server 執(zhí)行時間:
CPU 時間 = 0 毫秒,占用時間 = 1 毫秒。
SQL Server 分析和編譯時間:
CPU 時間 = 0 毫秒,占用時間 = 1 毫秒。

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

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

(2 行受影響)

(1 行受影響)

SQL Server 執(zhí)行時間:
CPU 時間 = 0 毫秒,占用時間 = 146 毫秒。

SQL Server 執(zhí)行時間:
CPU 時間 = 0 毫秒,占用時間 = 1 毫秒。

SQL Server 執(zhí)行時間:
CPU 時間 = 0 毫秒,占用時間 = 1 毫秒。
SQL Server 分析和編譯時間:
CPU 時間 = 0 毫秒,占用時間 = 1 毫秒。

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

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

(2 行受影響)

(1 行受影響)

SQL Server 執(zhí)行時間:
CPU 時間 = 0 毫秒,占用時間 = 85 毫秒。

SQL Server 執(zhí)行時間:
CPU 時間 = 0 毫秒,占用時間 = 1 毫秒。

SQL Server 執(zhí)行時間:
CPU 時間 = 0 毫秒,占用時間 = 1 毫秒。
SQL Server 分析和編譯時間:
CPU 時間 = 0 毫秒,占用時間 = 1 毫秒。

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

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

(2 行受影響)

(1 行受影響)

SQL Server 執(zhí)行時間:
CPU 時間 = 0 毫秒,占用時間 = 13 毫秒。

SQL Server 執(zhí)行時間:
CPU 時間 = 0 毫秒,占用時間 = 1 毫秒。

SQL Server 執(zhí)行時間:
CPU 時間 = 0 毫秒,占用時間 = 1 毫秒。

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

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

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

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

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

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

    相關(guān)評論

    閱讀本文后您有什么感想? 已有人給出評價!

    • 8 喜歡喜歡
    • 3 頂
    • 1 難過難過
    • 5 囧
    • 3 圍觀圍觀
    • 2 無聊無聊

    熱門評論

    最新評論

    發(fā)表評論 查看所有評論(0)

    昵稱:
    表情: 高興 可 汗 我不要 害羞 好 下下下 送花 屎 親親
    字數(shù): 0/500 (您的評論需要經(jīng)過審核才能顯示)