【问题标题】:SQL Server : Geography search performance - query nearest storesSQL Server:地理搜索性能 - 查询最近的商店
【发布时间】:2017-05-09 06:59:46
【问题描述】:

我有一个性能查询最近的商店:

我们有一张表,其中包含一个国家/地区的大约 50,000 条记录(商店/销售点位置)。

每条记录都有location“地理”类型的列

[LOCATION_geo] [geography]

为了提高性能,我使用此语法在该位置列上创建了一个空间索引

CREATE SPATIAL INDEX [LOCATION_geoIndex] 
ON [dbo].[StoreLocations] ([LOCATION_geo])
USING GEOGRAPHY_GRID 
WITH (
GRIDS =(LEVEL_1 = MEDIUM,LEVEL_2 = MEDIUM,LEVEL_3 = MEDIUM,LEVEL_4 = MEDIUM), 
CELLS_PER_OBJECT = 16, PAD_INDEX  = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
GO

我有一个存储过程来返回用户当前位置最近的 1000 个商店。

USE [CompanyDB]
GO
SET STATISTICS TIME ON;  
GO  
declare @point geography;
    set @point = geography::Point(49.2471855, -123.1078987, 4326);

    SELECT top (1000) [id]
          ,[Location_Name]
          ,[LOCATION_geo]from [MYDB].[dbo].[StoreLocations]
        where [LOCATION_geo].STDistance(@point) <= 10000
        ORDER BY [LOCATION_geo].STDistance(@point)

问题是查询总是需要 656 毫秒到 800 毫秒。这对于我们的网站来说是不可接受的性能,因为我们预计会有太多的同步调用。

(受影响的 1000 行)

SQL Server 执行时间:CPU 时间 = 923 毫秒,已用时间 = 1511 毫秒。

注意:大部分门店位于部分城市(约 10 个城市)。

我还注意到聚集索引查找成本 >= 总查询成本的 45%。

所以我的问题是有没有更好的方法来提高该查询的性能?

【问题讨论】:

  • 是否使用了空间索引?你能确认它是通过执行计划使用的吗?
  • 是的,它已被使用,成本为 8%,估计行大小为 17 B,已排序:真实,估计执行次数为 109.8。
  • 另外,执行计划中的这个索引总是给出警告“没有统计的列:....SRID,....pk0”,其实我不知道是什么意思
  • 我想如果你确定你正在使用索引,这是你可以获得的最好的性能。您可以考虑另一种解决方案 - 例如创建 10 个表用于存储每个大城市的位置,并创建一个表用于存储其余记录;然后,有了用户的坐标,计算出要查询哪些表;也许减少记录会提高性能;
  • 网格分辨率是空间索引中的GRIDS =(LEVEL_1 = MEDIUM,LEVEL_2 = MEDIUM,LEVEL_3 = MEDIUM,LEVEL_4 = MEDIUM) 部分。该文档比我做得更好。 technet.microsoft.com/en-us/library/bb964712(v=sql.105).aspx

标签: sql-server indexing geo geography sqlperformance


【解决方案1】:

我建议在表格中再添加一个名为距离的列,其中距离将是 LOCATION_geo 与 Point(0, 0, 0) 的距离。请参阅下面的示例插入语句:

   INSERT INTO [GWDB].[dbo].[StoreLocations]
          ([id]
          ,[Location_Name]
          ,[LOCATION_geo]
          ,[Distance])
    Values(@id
          ,@Location_Name
          ,@LOCATION_geo
          ,@LOCATION_geo..STDistance(Point(0, 0, 0))

您还应该在新列距离上创建一个索引并更改您的存储过程,如下所示:

USE [CompanyDB]
GO
SET STATISTICS TIME ON;  
GO  
declare @point geography;
declare @distance float;
    set @point = geography::Point(49.2471855, -123.1078987, 4326);
    set @distance = @point.STDistance(geography::Point(0, 0, 0);

    SELECT top (1000) [id]
          ,[Location_Name]
          ,[LOCATION_geo]from [GWDB].[dbo].[StoreLocations]
        where 
        distance < @distance AND
        [LOCATION_geo].STDistance(@point) <= 10000
        ORDER BY [LOCATION_geo].STDistance(@point)

【讨论】:

  • 值得一试。但这不会得到准确的结果,我认为如果搜索点在西边,这个查询只会得到东边的店铺(搜索点在东边的店铺)。
  • 我试过了,它并没有科学地提高性能,因为我认为性能低的主要原因是语句的顺序。谢谢。
【解决方案2】:

我不确定这在您的应用程序中的效果如何,在某些情况下这会更快,但在其他情况下会更慢。当您搜索的点靠近您的数据时,此搜索过程会更快。当搜索点离您的数据越远时,它就越慢。

在我的场景中,我的所有点都比较接近(1600 万条记录)。这些是我看到的速度差异。

|--Search Location--|--STIntersects() time--|--Numbers time--|
--------------------------------------------------------------
|Close              |5 seconds              |700 ms          |
|Far                |90 ms                  |4 seconds       |

基本上,这个想法是使用数字表逐步扩展您的搜索区域。

DECLARE @point GEOGRAPHY = GEOGRAPHY::Point(49.2471855, -123.1078987, 4326)
DECLARE @MaximumRaidus INT = 10000

SELECT TOP 100
  ID,
  Location_Name,
  Location_geo
FROM
  GWDB.dbo.StoreLocations WITH(INDEX([LOCATION_geoIndex]))
CROSS JOIN
  GWDB.dbo.Numbers N
WHERE
  N.n BETWEEN 0 AND SQRT(@MaximumRadius)
  AND Location_geo.STIntersects(@Point.STBuffer(POWER(N.n,2))) = 1
ORDER BY
  N.n

【讨论】:

  • 谢谢,但我没有得到 dbo.Numbers 表是什么??我没有这样的桌子。
  • @Tarek_El-Mallah 数字表只是一个整数表。 IE。表编号有一列 (n INT),其值从 0 到 10000 左右。它们对于执行此类操作、填补数据空白或将数据拆分为直方图等非常有用。谷歌这个术语,你应该找到一些关于它们的使用以及如何创建它们的参考。
猜你喜欢
  • 2013-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-13
  • 1970-01-01
相关资源
最近更新 更多