2015-11-03 85 views
2

我有一个表,包含IP地址范围(作为整数值)和相应的国家,地区和城市分配到该范围。它看起来如下:如何有效搜索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

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

+0

这是一个很好的问题。我可以检查。这张表刚刚创建并且是静态的。我只是创建了表格并加载了数据。行以BeginRange顺序添加,但我会尝试重建索引。 –

+0

我不知道为什么有人会低估,但试着将问题的标题改为更具描述性的内容。就像“如何在IP地址范围内高效搜索”一样...... –

回答

0

事实证明,我在BeginRange + EndRange上的聚集索引效率不高,因为弗拉基米尔巴拉诺夫在他的回答中表示。我所做的是在BeginRange上创建一个PK /聚集索引,并在EndRange上创建一个单独的索引。现在查询立即执行。

5

这种搜索不能用您拥有的索引高效地完成。

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

@IPNumber >= BeginRange 
@IPNumber <= EndRange 

指数有助于快速O(log(n))查找范围的开始(或结束),但随后必须检查第二个谓词的行表中的其余部分。

检出计划中读取的实际行数。它会很大。

如果我没有弄错,有些类似(更复杂)question之前。即使它被要求提供Postgres,该方法也可以在SQL Server中使用。在这个问题上,这种搜索没有一次完成,而是完成了60万次。


“如何使搜索有效”这个问题的答案取决于几件事情。首先:你能保证表中的IP范围不重叠吗?换句话说,你能保证任何搜索将返回0或1行吗?

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