Bri*_*hek 4 index sql-server optimization clustered-index nonclustered-index
我在 MS-SQL 中有下表:
CREATE TABLE [dbo].[dbip_locations](
[ip_from] [bigint] NOT NULL,
[ip_to] [bigint] NOT NULL,
[country_code] [nvarchar](64) NOT NULL,
[region_name] [nvarchar](128) NOT NULL,
[city_name] [nvarchar](128) NOT NULL,
[latitude] [float] NOT NULL,
[longitude] [float] NOT NULL
)
Run Code Online (Sandbox Code Playgroud)
ip_from 和 ip_to 列是根据 ipv4 地址计算的,如下所示:
return (
convert(bigint, parsename(@ip, 1)) +
convert(bigint, parsename(@ip, 2)) * convert(bigint, 256) +
convert(bigint, parsename(@ip, 3)) * convert(bigint, 65536) +
convert(bigint, parsename(@ip, 4)) * convert(bigint, 16777216)
)
Run Code Online (Sandbox Code Playgroud)
然后,我使用使用上述计算转换为 bigint 的 ipv4 地址来搜索 ip 地址位于 ip_from 和 ip_to 列之间的行。
我总是会找到一行,虽然它不是由模式强制执行的,但这是数据中的现实。
这是查询:
SELECT TOP(1) [latitude], [longitude] FROM [dbo].[dbip_locations] WHERE @ip_int BETWEEN ip_from AND ip_to
Run Code Online (Sandbox Code Playgroud)
我目前在 ip_from 和 ip_to 列上都有两个非聚集的、非唯一的索引。查询执行得非常快,但我每秒执行大量此类查询,并且想知道是否可以通过使用不同的索引获得更好的性能?也许是聚集多列索引或使用唯一索引?
我可以使用更好的索引吗?
我认为具有 ip_from 和 ip_to 的复合索引对于此范围搜索将是最有效的。如果返回的唯一列是纬度和经度,则应将这些查询作为非集群中的非键列包含在内,以获得此特定查询的最佳性能。索引应该被聚集而不是你经常返回其他列。
由于您的查询TOP (1)没有指定ORDER BY,因此可能会在范围搜索内返回任何任意行。如果您每次都需要返回同一行,请使用列指定“ORDER BY”以唯一标识行。
在搜索将始终返回 1 行(或无)的假设下,即(ip_from, ip_to)间隔不重叠,查询可以变得更高效:
使用特定ORDER BY且仅ip_from用于搜索。在我们找到符合这个条件的第一行之后,我们也检查第二个条件:
SELECT [latitude], [longitude]
FROM
( SELECT TOP (1)
[latitude], [longitude], ip_to
FROM [dbo].[dbip_locations]
WHERE ip_from <= @ip_int
ORDER BY ip_from DESC
) AS t
WHERE @ip_int <= ip_to ;
Run Code Online (Sandbox Code Playgroud)
一个简单的索引就(ip_from)足够了,查询将执行单个索引查找,然后对表中的相关行进行另一次读取(在堆或 CI 上)。
如果您想要更好的性能,请在(ip_from) INCLUDE (ip_to, latitude, longitude).
我应该补充一点,上述复合索引也将有助于您的原始查询。查询还需要索引查找。那么,有什么区别呢?
可能至关重要的区别在于,即使使用简单索引,当它返回 0 结果时,该查询的性能也会好得多,即。当您搜索不在表中存储的任何间隔中的 IP 时。因为它将执行单索引查找,找到第一个匹配的行,然后检查第二个条件(通过INCLUDE列或堆/CI)并拒绝它。
您的查询必须执行索引查找,然后遍历索引的整个其余部分(因此平均为完整索引扫描的 1/2),结果却发现没有与第二个条件 about 匹配的行ip_to。