如何有效地搜索IP地址范围?

时间:2015-11-03 22:45:34

标签: sql-server

我有一个表,其中包含IP地址范围(作为整数值)以及分配给该范围的相应国家,地区和城市。它看起来如下:

CREATE TABLE [dbo].[IpToRegion]
(
    [BeginRange] [bigint] NOT NULL,
    [EndRange] [bigint] NOT NULL,
    [CountryCode] [varchar](10) NOT NULL,
    [Country] [varchar](50) NOT NULL,
    [Region] [varchar](100) NOT NULL,
    [City] [varchar](100) NOT NULL
) ON [PRIMARY]

CREATE UNIQUE CLUSTERED INDEX [ClusteredIndex-20151031-193911] ON [dbo].[IpToRegion]
(
    [BeginRange] ASC,
    [EndRange] ASC
)
GO

此表中有9.1M行。为了找到单个IP地址的位置,我首先将其转换为大型int,然后执行以下查询:

DECLARE @IPNumber BIGINT
DECLARE @IPAddress varchar(20)

Set @IPNumber = (CONVERT(bigint, PARSENAME(@IPAddress,1)) + CONVERT(bigint, PARSENAME(@IPAddress,2)) * 256 + CONVERT(bigint, PARSENAME(@IPAddress,3)) * 65536 + CONVERT(bigint, PARSENAME(@IPAddress,4)) * 16777216)

Select City + ', ' + Region + ', ' + Country
From IpToRegion 
Where @IPNumber Between BeginRange And EndRange

问题是此查询可能需要5到20秒才能执行。这是查询计划:

enter image description here

当然,我的问题是这个查询如何执行这么长时间?它在聚集索引上进行搜索并返回单行。我可以尝试一些不同的索引策略。但是,在这一点上,我更好奇为什么这个查询的表现如此糟糕。

2 个答案:

答案 0 :(得分:5)

使用您拥有的索引无法有效地进行此类搜索。

如果您查看计划中Index Seek运算符的详细信息,您会看到两个谓词。

@IPNumber >= BeginRange 
@IPNumber <= EndRange

索引有助于快速O(log(n))找到范围的开始(或结束),但是它必须检查表中其余行的第二个谓词。

查看计划中读取的实际行数。它会很大。

如果我没弄错的话,之前有点类似(更复杂)question。虽然有人问过Postgres,但这种方法也适用于SQL Server。在那个问题中,这种搜索不是一次,而是600K次。

“如何提高搜索效率”这一问题的答案取决于一些事情。首先:您能保证表中的IP范围不重叠吗?换句话说,你能保证任何搜索都会返回0或1行吗?

如果是,则在查询中添加简单的TOP(1)就足够了。

答案 1 :(得分:0)

事实证明,我在BeginRange + EndRange上的聚集索引并不像Vladimir Baranov在他的回答中所说的那样有效。我所做的是在BeginRange上创建一个PK /聚簇索引,在EndRange上创建一个单独的索引。现在查询立即执行。

相关问题