SQL 服务器:聚簇索引比等效的非聚簇索引慢得多
SQL Server: Clustered index considerably slower than equivalent non-clustered index
设置
我要描述的是 运行 在以下硬件上:
- 磁盘:RAID5 中的 6 个 2TB HDD(带 1 个冗余驱动器)
- CPU:英特尔至强 E5-2640 @ 2.4 GHz,6 核
- 内存:64GB
- SQL 服务器版本:SQL Server 2016 Developer
SQL Server Management Studio (SSMS) 和 sql 服务器实例都 运行 在此服务器上。所以所有查询都在本地执行。此外,在执行任何查询之前,我总是运行以下命令以确保没有数据访问缓存在内存中:
DBCC DROPCLEANBUFFERS
问题
我们有一个 SQL 服务器 table,大约有 11'600'000 行。从大的方面来说,不是特别大table,但随着时间的推移会增长很多。
table的结构如下:
CREATE TABLE [Trajectory](
[Id] [int] IDENTITY(1,1) NOT NULL,
[FlightDate] [date] NOT NULL,
[EntryTime] [datetime2] NOT NULL,
[ExitTime] [datetime2] NOT NULL,
[Geography] [geography] NOT NULL,
[GreatArcDistance] [real] NULL,
CONSTRAINT [PK_Trajectory] PRIMARY KEY CLUSTERED ([Id])
)
(为简单起见已排除一些列,但它们的数量和大小都非常小)
虽然行数不多,但由于 [Geography]
列,table 占用了大量磁盘空间 - space。此列的内容是 LINESTRINGS,大约有 3000 个点(包括 Z 和 M 值)。
现在,假设我们只是在 table 的 Id 列上有一个聚集索引,它也表示主键约束,如上面的 DDL 中所述。
我们遇到的问题是,当我们查询 table 的日期范围和特定的地理交集时,需要花费大量时间才能完成该查询。
我们正在查看的查询如下所示:
DEFINE @p1 = [...]
SELECT [Id], [Geography]--, (+ some other columns)
WHERE [FlightDate] BETWEEN '2018-09-04' AND '2018-09-12' AND [Geography].STIntersects(@p1) = 1
这是一个相当简单的查询,使用了我上面提到的两个过滤器。为了加快查询速度,我们尝试了几种不同类型的索引:
1。在 [Trajectory] ([FlightDate] ASC)
上创建非聚集索引 [IX_Trajectory_FlightDate]
当我们查询 table 时,在添加了这样的索引之后,期望查询计划如下所示:
- 对索引执行 INDEX SEEK(此操作将 11'600'000 行过滤到大约 50'000)
- 查找主 table 以获得 [地理] 列以及任何额外选择的列
- 对返回的每一行执行地理过滤器
[Geography].STIntersects(@p1) = 1
这也是它的作用。这是在 (SSMS) 中看到的实际查询执行计划的快照:
此查询需要很长时间才能完成(可以按分钟计算,如上面的屏幕截图所示)。
--- 更新 1 开始 ---
其他查询计划信息(对于主要步骤,注意:查询的执行与上面所示不同,因此时间有所不同。此查询花费了 2:39):
SELECT.QueryTimeStats
CpuTime=12241ms
ElapsedTime=157591ms
Key Lookup (97%)
Actual I/O Statistics
Actual Logical Reads=48165
Actual Physical Reads=81
Actual Time Statistics
Actual Elapsed CPU Time=144ms
Actual Elapsed Time=266ms
Index Seek (0%)
Actual I/O Statistics
Actual Logical Reads=85
Actual Physical Reads=0
Actual Read Aheads=73
Actual Scans=21
Filter (3%)
Actual Time Statistics
Actual Elapsed CPU Time=12156ms
Actual Elapsed Time=157583ms
对我来说,这个查询的所有时间或多或少都花在了 IO 上。为什么我无法解释。我还将添加以下感兴趣的内容:
- 表示占time/resources的3%的步耗时157583ms,而占time/resources的97%的步耗时266ms。我觉得很奇怪。
- 如果我将 STIntersect 过滤器替换为使用
EntryTime
列(未编入索引!)的不同过滤器,其行数大致 returns 相同,则查询时间会减少到大约 20 秒,尽管我仍然选择相同数量的行。我想对此的唯一解释是查询实际上不需要读取昂贵的 [Geography]
列就可以丢弃该行。
--- 更新 1 结束 ---
2。在 [Trajectory] ([FlightDate] ASC) INCLUDE ([Geography])
上创建非聚集索引 [IX_Trajectory_FlightDate_Includes_Geography]
此索引与其他索引的唯一不同之处在于它将大型 [Geography] 列与索引一起存储。但对查询计划的期望大致相同:
当我们查询 table 时,在添加了这样的索引之后,期望查询计划如下所示:
- 对索引执行 INDEX SEEK(此操作将 11'600'000 行过滤到大约 50'000)
- 对返回的每一行执行地理过滤器
[Geography].STIntersects(@p1) = 1
- 查找主要 table 以获得额外选择的列
此查询用时不到 10 秒。这是在 SSMS 中看到的两个查询计划:
注意上面的第2步和第3步相对于使用其他索引的查询进行了切换(意味着它仅在完全完成过滤后才执行查找,因此它只查找主table 大约 1'000 次,而不是 50'000 次)。现在,这向我表明执行此查询时实际花费时间的是查找主要 table,而不是其他任何东西,例如 INDEX SEEK 或 FILTER。
现在维护这样的索引并不是我们想要做的理想事情,因为当我们考虑 [=329= 中的 [Geography]
列有多大时,它使用了相当多的 space ] 是,以及未来会增长多少。像这样重建索引需要几个小时。
--- 更新 2 开始 ---
其他查询计划信息:
SELECT.QueryTimeStats
CpuTime=11648ms
ElapsedTime=7533ms
Key Lookup (88%)
Actual I/O Statistics
Actual Logical Reads=1191
Actual Physical Reads=0
Actual Time Statistics
Actual Elapsed CPU Time=0ms
Actual Elapsed Time=0ms
Index Seek (3%)
Actual I/O Statistics
Actual Logical Reads=7119
Actual Physical Reads=4
Actual Read Aheads=6678
Actual Scans=21
Actual Time Statistics
Actual Elapsed CPU Time=104ms
Actual Elapsed Time=168ms
Filter (9%)
Actual Time Statistics
Actual Elapsed CPU Time=11535ms
Actual Elapsed Time=6888ms
关于统计的补充说明:
- 当深入研究这些数字中的大部分时,它们在可用线程之间的分配非常好。
- 我的猜测是,从这些统计数据中得出的主要结论是,此查询在 "Key Lookup" 期间花在 IO 工作上的时间完全为零,而另一个查询必须做很多。我不确定为什么这会好得多,因为它仍然必须找到另外选择的列(我选择的那个不是
[Geography]
列。但是因为已经应用了过滤器在做查找之前,它显然要少做很多。但即便如此,零IO让我感到困惑。
- 物理读取很少。所有需要的数据(包括 [Geography] 列都是从 Index Seek 中读取的,只需 4 次物理读取。
--- 更新 2 结束 ---
问题
谁能回答这两个问题:
- 简单的查找操作是否可以解释(1)和(2)中描述的索引之间的性能差异?
- 为什么(3)中描述的聚簇索引比(2)中描述的索引慢很多?
- 如果以上两个都无法回答,那么在比较问题 1 和问题 2 中描述的这两个指标时,我们是否应该看到如此大的性能缺陷,还是我们的设置更有可能出现其他问题?
也许您可以在对多边形中的点进行完整测试之前,为边界框添加一列以进行轻量级检查。如果事实证明您可以使用边界框消除大部分记录,那么它将节省时间。
DECLARE @g geography,
@b_box geography,
@pIn geography,
@pOut geography
SET @g = geography::STPolyFromText('POLYGON((-91.119987 40.705402, -91.129158 40.682148, -91.162498 40.656311, -91.214912 40.643818, -91.262062 40.639545, -91.375610 40.603439, -91.411118 40.572971, -91.412872 40.547993, -91.382103 40.528496, -91.374794 40.503654, -91.385399 40.447250, -91.372757 40.402988, -91.385757 40.392361, -91.418816 40.386875, -91.448593 40.371902, -91.476883 40.390968, -91.490158 40.390762, -91.500221 40.405117, -91.527534 40.410126, -91.529449 40.435043, -91.538689 40.441246, -91.533051 40.455399, -91.579224 40.463718, -91.585869 40.484478, -91.616699 40.504833, -91.622375 40.532864, -91.691917 40.551640, -91.689796 40.581165, -91.716812 40.593399, -91.741547 40.609749, -91.946198 40.608234, -92.192993 40.600060, -92.361328 40.599548, -92.646240 40.591438, -92.717621 40.589645, -93.100731 40.584335, -93.370056 40.580482, -93.562691 40.580807, -93.786079 40.578445, -94.017830 40.574024, -94.238159 40.570972, -94.484993 40.574215, -94.639633 40.575756, -94.920364 40.577229, -95.217171 40.581898, -95.382294 40.584335, -95.767204 40.589046, -95.757271 40.620903, -95.767723 40.643116, -95.876335 40.730434, -95.851509 40.792599, -95.846153 40.848331, -95.834114 40.870300, -95.836258 40.901108, -95.837318 40.974258, -95.860611 41.002651, -95.859253 41.035004, -95.878517 41.065872, -95.857986 41.109188, -95.876396 41.164204, -95.859512 41.166866, -95.858910 41.180538, -95.915810 41.194065, -95.921959 41.207855, -95.910690 41.225246, -95.929939 41.302059, -95.910912 41.308472, -95.897301 41.286865, -95.888817 41.301392, -95.942604 41.340080, -95.939766 41.394810, -95.934776 41.462387, -95.952896 41.472393, -96.006607 41.481960, -96.013161 41.493000, -95.996399 41.511524, -95.993675 41.528111, -96.004303 41.536671, -96.049881 41.524342, -96.085548 41.537529, -96.091644 41.563152, -96.080544 41.576008, -96.111015 41.599014, -96.099014 41.654690, -96.119972 41.684105, -96.121910 41.694923, -96.085266 41.704998, -96.099480 41.731575, -96.099030 41.752987, -96.076126 41.791481, -96.135330 41.862633, -96.159676 41.904163, -96.145576 41.924919, -96.147034 41.966267, -96.184921 41.980698, -96.202545 41.996628, -96.235794 42.001270, -96.238426 42.028450, -96.265182 42.048908, -96.284821 42.123463, -96.351860 42.168194, -96.363205 42.214050, -96.337402 42.229530, -96.332352 42.260315, -96.342575 42.282089, -96.368393 42.298031, -96.389473 42.328796, -96.423866 42.349285, -96.411453 42.380924, -96.417320 42.414783, -96.397583 42.441799, -96.395767 42.467407, -96.439087 42.489246, -96.479935 42.517136, -96.489029 42.564034, -96.500633 42.573891, -96.488190 42.580486, -96.512535 42.629761, -96.540855 42.662411, -96.562729 42.668518, -96.626228 42.708359, -96.640396 42.748608, -96.632668 42.776840, -96.600563 42.799564, -96.587334 42.835388, -96.572815 42.834354, -96.555901 42.846668, -96.537201 42.896915, -96.543953 42.913876, -96.514626 42.952393, -96.516838 42.986469, -96.498711 43.012062, -96.519699 43.051521, -96.479263 43.061897, -96.461784 43.075596, -96.460495 43.087887, -96.451195 43.126324, -96.472801 43.209099, -96.486931 43.217926, -96.558289 43.225506, -96.566673 43.239651, -96.559250 43.253281, -96.570404 43.263630, -96.578812 43.290092, -96.540245 43.307678, -96.522575 43.356987, -96.524734 43.384247, -96.557388 43.400749, -96.588791 43.435562, -96.583473 43.481945, -96.597992 43.499874, -96.460136 43.499744, -96.060738 43.498566, -95.866615 43.498978, -95.464493 43.499577, -95.396278 43.500370, -94.920197 43.499413, -94.859573 43.500072, -94.454987 43.498146, -94.246544 43.498993, -93.973717 43.500343, -93.653481 43.500809, -93.500618 43.500534, -93.054184 43.501495, -93.027016 43.501316, -92.557831 43.500294, -92.452995 43.499496, -92.077370 43.499187, -91.730217 43.499603, -91.610954 43.500656, -91.223434 43.500835, -91.235771 43.464710, -91.210785 43.424076, -91.198112 43.370537, -91.176918 43.353970, -91.078369 43.313320, -91.066299 43.280704, -91.068924 43.257919, -91.161224 43.147594, -91.168442 43.082905, -91.159622 43.081200, -91.152084 43.001331, -91.138992 42.925907, -91.093300 42.871452, -91.081902 42.783375, -91.066040 42.744923, -90.999054 42.707066, -90.919281 42.680683, -90.892418 42.678246, -90.745483 42.657005, -90.694664 42.637932, -90.664253 42.571392, -90.639091 42.555714, -90.625580 42.528561, -90.638329 42.509361, -90.651772 42.494698, -90.648346 42.475643, -90.605827 42.460560, -90.563583 42.421837, -90.491043 42.388783, -90.441597 42.360073, -90.427681 42.340633, -90.417984 42.263924, -90.407173 42.242645, -90.367729 42.210209, -90.323601 42.197319, -90.230934 42.159721, -90.191574 42.122688, -90.176086 42.120502, -90.166649 42.103745, -90.168098 42.061043, -90.150536 42.033428, -90.142670 41.983963, -90.154518 41.930775, -90.195839 41.806137, -90.255310 41.781738, -90.304886 41.756466, -90.326027 41.722736, -90.341133 41.649090, -90.339348 41.602798, -90.348366 41.586849, -90.423004 41.567272, -90.434967 41.543579, -90.454994 41.527546, -90.540840 41.525970, -90.600700 41.509586, -90.658791 41.462318, -90.708214 41.450062, -90.779900 41.449821, -90.844139 41.444622, -90.949654 41.421234, -91.000694 41.431084, -91.027489 41.423508, -91.055786 41.401379, -91.073280 41.334896, -91.102348 41.267818, -91.101524 41.231522, -91.056320 41.176258, -91.018257 41.165825, -90.990341 41.144371, -90.957787 41.104359, -90.954651 41.070362, -90.960709 40.950504, -90.983276 40.923927, -91.049210 40.879585, -91.088905 40.833729, -91.092751 40.761547, -91.119987 40.705402))', 4326);
SET @g = @g.ReorientObject()
DECLARE @g_flat_box geometry = geometry::STGeomFromWKB(@g.STAsBinary(), @g.STSrid).STEnvelope();
DECLARE @g_rnd_box geography = geography::STGeomFromWKB(@g_flat_box.STAsBinary(), @g_flat_box.STSrid);
SELECT @g as [poly], @g_rnd_box as [box]
SET @pIn = geography::STPointFromText('POINT( -90.6204165 41.5795478)',4326)
SET @pOut = geography::STPointFromText('POINT( -80.6204165 31.5795478)',4326)
SELECT STIntersectionIn = @g_rnd_box.STIntersection( @pIn ).ToString(),
STIntersectionOut = @g_rnd_box.STIntersection( @pOut ).ToString(),
STIntersectionIn = @g.STIntersection( @pIn ).ToString(),
STIntersectionOut = @g.STIntersection( @pOut ).ToString()
GO
获得了这个例子的数据
查看您的查询,首先要考虑的是您在 SELECT 列表中包含一个空间列,该列是 .NET/CLR 数据类型,并且这些列存储在 IN_ROW_DATA
需要键查找的页面,除非索引中包含空间列,索引数据页中可能还包含空间边界框,以加快过滤,节省大部分磁盘 I/O。 我想说您发现了一个无需空间索引即可加速空间列过滤的有效技巧。
为了证明我的观点,我建议您参考原始的 SQL 文档,我相信您已经知道,关于 covering indexes,它澄清了以下内容:“Non - 将键列添加到非聚集索引的 leaf level
以提高查询性能。这允许查询优化器从索引扫描中找到所有需要的信息; table 或聚集索引未访问数据.”。最后一部分在这里非常重要,所以我假设边界框是空间列的“必需信息”的一部分,以帮助查询优化器避免访问 IN_ROW_DATA
.
结论:
- 简单的查找操作是否可以解释(1)和(2)中描述的索引之间的性能差异? 我会这么说,因为空间 CLR 数据类型存储在
IN_ROW_DATA
页面之外需要更多磁盘 I/O 在 (1).
- 为什么(3)中描述的聚簇索引比(2)中描述的索引慢很多? 同样的道理,在索引(2)中包含地理数据,省去了在
IN_ROW_DATA
页之外查找的需要,节省了大部分磁盘I/O;请记住,索引 (3) 仍然需要在 LOB_DATA
. 中查找空间数据
- 如果以上两个都无法回答,那么在比较问题 1 和问题 2 中描述的这两个指标时,我们是否应该看到如此大的性能缺陷,或者更可能是我们的设置有其他问题? N/A.
一切都与磁盘命中有关。
没有几何索引,需要获取和测试 50K 几何。这是 50K 磁盘命中。对于 HDD,一个简单的经验法则是 100/秒。因此 500 秒。查看您的#3:“对返回的每一行执行地理过滤器 [Geography].STIntersects(@p1) = 1”。
要加快速度,您必须使用空间索引并希望它比日期过滤器做得更好。
你的#2 说“包括几何”。这似乎会使索引变得如此庞大,以至于没有真正的帮助。正如您所说的“大致相同”。
(FlightDate, id) 上的聚簇索引,按此顺序将减少磁盘命中 if FlightDate
是最佳起点。 (不知道几何索引会不会更好。)
所以,有两个索引:Clustered(FlightDate, id)和Geometry(几何)。并希望优化器会选择更好的那个。
设置
我要描述的是 运行 在以下硬件上:
- 磁盘:RAID5 中的 6 个 2TB HDD(带 1 个冗余驱动器)
- CPU:英特尔至强 E5-2640 @ 2.4 GHz,6 核
- 内存:64GB
- SQL 服务器版本:SQL Server 2016 Developer
SQL Server Management Studio (SSMS) 和 sql 服务器实例都 运行 在此服务器上。所以所有查询都在本地执行。此外,在执行任何查询之前,我总是运行以下命令以确保没有数据访问缓存在内存中:
DBCC DROPCLEANBUFFERS
问题
我们有一个 SQL 服务器 table,大约有 11'600'000 行。从大的方面来说,不是特别大table,但随着时间的推移会增长很多。
table的结构如下:
CREATE TABLE [Trajectory](
[Id] [int] IDENTITY(1,1) NOT NULL,
[FlightDate] [date] NOT NULL,
[EntryTime] [datetime2] NOT NULL,
[ExitTime] [datetime2] NOT NULL,
[Geography] [geography] NOT NULL,
[GreatArcDistance] [real] NULL,
CONSTRAINT [PK_Trajectory] PRIMARY KEY CLUSTERED ([Id])
)
(为简单起见已排除一些列,但它们的数量和大小都非常小)
虽然行数不多,但由于 [Geography]
列,table 占用了大量磁盘空间 - space。此列的内容是 LINESTRINGS,大约有 3000 个点(包括 Z 和 M 值)。
现在,假设我们只是在 table 的 Id 列上有一个聚集索引,它也表示主键约束,如上面的 DDL 中所述。
我们遇到的问题是,当我们查询 table 的日期范围和特定的地理交集时,需要花费大量时间才能完成该查询。
我们正在查看的查询如下所示:
DEFINE @p1 = [...]
SELECT [Id], [Geography]--, (+ some other columns)
WHERE [FlightDate] BETWEEN '2018-09-04' AND '2018-09-12' AND [Geography].STIntersects(@p1) = 1
这是一个相当简单的查询,使用了我上面提到的两个过滤器。为了加快查询速度,我们尝试了几种不同类型的索引:
1。在 [Trajectory] ([FlightDate] ASC)
上创建非聚集索引 [IX_Trajectory_FlightDate]当我们查询 table 时,在添加了这样的索引之后,期望查询计划如下所示:
- 对索引执行 INDEX SEEK(此操作将 11'600'000 行过滤到大约 50'000)
- 查找主 table 以获得 [地理] 列以及任何额外选择的列
- 对返回的每一行执行地理过滤器
[Geography].STIntersects(@p1) = 1
这也是它的作用。这是在 (SSMS) 中看到的实际查询执行计划的快照:
此查询需要很长时间才能完成(可以按分钟计算,如上面的屏幕截图所示)。
--- 更新 1 开始 ---
其他查询计划信息(对于主要步骤,注意:查询的执行与上面所示不同,因此时间有所不同。此查询花费了 2:39):
SELECT.QueryTimeStats
CpuTime=12241ms
ElapsedTime=157591ms
Key Lookup (97%)
Actual I/O Statistics
Actual Logical Reads=48165
Actual Physical Reads=81
Actual Time Statistics
Actual Elapsed CPU Time=144ms
Actual Elapsed Time=266ms
Index Seek (0%)
Actual I/O Statistics
Actual Logical Reads=85
Actual Physical Reads=0
Actual Read Aheads=73
Actual Scans=21
Filter (3%)
Actual Time Statistics
Actual Elapsed CPU Time=12156ms
Actual Elapsed Time=157583ms
对我来说,这个查询的所有时间或多或少都花在了 IO 上。为什么我无法解释。我还将添加以下感兴趣的内容:
- 表示占time/resources的3%的步耗时157583ms,而占time/resources的97%的步耗时266ms。我觉得很奇怪。
- 如果我将 STIntersect 过滤器替换为使用
EntryTime
列(未编入索引!)的不同过滤器,其行数大致 returns 相同,则查询时间会减少到大约 20 秒,尽管我仍然选择相同数量的行。我想对此的唯一解释是查询实际上不需要读取昂贵的[Geography]
列就可以丢弃该行。
--- 更新 1 结束 ---
2。在 [Trajectory] ([FlightDate] ASC) INCLUDE ([Geography])
上创建非聚集索引 [IX_Trajectory_FlightDate_Includes_Geography]此索引与其他索引的唯一不同之处在于它将大型 [Geography] 列与索引一起存储。但对查询计划的期望大致相同:
当我们查询 table 时,在添加了这样的索引之后,期望查询计划如下所示:
- 对索引执行 INDEX SEEK(此操作将 11'600'000 行过滤到大约 50'000)
- 对返回的每一行执行地理过滤器
[Geography].STIntersects(@p1) = 1
- 查找主要 table 以获得额外选择的列
此查询用时不到 10 秒。这是在 SSMS 中看到的两个查询计划:
注意上面的第2步和第3步相对于使用其他索引的查询进行了切换(意味着它仅在完全完成过滤后才执行查找,因此它只查找主table 大约 1'000 次,而不是 50'000 次)。现在,这向我表明执行此查询时实际花费时间的是查找主要 table,而不是其他任何东西,例如 INDEX SEEK 或 FILTER。
现在维护这样的索引并不是我们想要做的理想事情,因为当我们考虑 [=329= 中的 [Geography]
列有多大时,它使用了相当多的 space ] 是,以及未来会增长多少。像这样重建索引需要几个小时。
--- 更新 2 开始 ---
其他查询计划信息:
SELECT.QueryTimeStats
CpuTime=11648ms
ElapsedTime=7533ms
Key Lookup (88%)
Actual I/O Statistics
Actual Logical Reads=1191
Actual Physical Reads=0
Actual Time Statistics
Actual Elapsed CPU Time=0ms
Actual Elapsed Time=0ms
Index Seek (3%)
Actual I/O Statistics
Actual Logical Reads=7119
Actual Physical Reads=4
Actual Read Aheads=6678
Actual Scans=21
Actual Time Statistics
Actual Elapsed CPU Time=104ms
Actual Elapsed Time=168ms
Filter (9%)
Actual Time Statistics
Actual Elapsed CPU Time=11535ms
Actual Elapsed Time=6888ms
关于统计的补充说明:
- 当深入研究这些数字中的大部分时,它们在可用线程之间的分配非常好。
- 我的猜测是,从这些统计数据中得出的主要结论是,此查询在 "Key Lookup" 期间花在 IO 工作上的时间完全为零,而另一个查询必须做很多。我不确定为什么这会好得多,因为它仍然必须找到另外选择的列(我选择的那个不是
[Geography]
列。但是因为已经应用了过滤器在做查找之前,它显然要少做很多。但即便如此,零IO让我感到困惑。 - 物理读取很少。所有需要的数据(包括 [Geography] 列都是从 Index Seek 中读取的,只需 4 次物理读取。
--- 更新 2 结束 ---
问题
谁能回答这两个问题:
- 简单的查找操作是否可以解释(1)和(2)中描述的索引之间的性能差异?
- 为什么(3)中描述的聚簇索引比(2)中描述的索引慢很多?
- 如果以上两个都无法回答,那么在比较问题 1 和问题 2 中描述的这两个指标时,我们是否应该看到如此大的性能缺陷,还是我们的设置更有可能出现其他问题?
也许您可以在对多边形中的点进行完整测试之前,为边界框添加一列以进行轻量级检查。如果事实证明您可以使用边界框消除大部分记录,那么它将节省时间。
DECLARE @g geography,
@b_box geography,
@pIn geography,
@pOut geography
SET @g = geography::STPolyFromText('POLYGON((-91.119987 40.705402, -91.129158 40.682148, -91.162498 40.656311, -91.214912 40.643818, -91.262062 40.639545, -91.375610 40.603439, -91.411118 40.572971, -91.412872 40.547993, -91.382103 40.528496, -91.374794 40.503654, -91.385399 40.447250, -91.372757 40.402988, -91.385757 40.392361, -91.418816 40.386875, -91.448593 40.371902, -91.476883 40.390968, -91.490158 40.390762, -91.500221 40.405117, -91.527534 40.410126, -91.529449 40.435043, -91.538689 40.441246, -91.533051 40.455399, -91.579224 40.463718, -91.585869 40.484478, -91.616699 40.504833, -91.622375 40.532864, -91.691917 40.551640, -91.689796 40.581165, -91.716812 40.593399, -91.741547 40.609749, -91.946198 40.608234, -92.192993 40.600060, -92.361328 40.599548, -92.646240 40.591438, -92.717621 40.589645, -93.100731 40.584335, -93.370056 40.580482, -93.562691 40.580807, -93.786079 40.578445, -94.017830 40.574024, -94.238159 40.570972, -94.484993 40.574215, -94.639633 40.575756, -94.920364 40.577229, -95.217171 40.581898, -95.382294 40.584335, -95.767204 40.589046, -95.757271 40.620903, -95.767723 40.643116, -95.876335 40.730434, -95.851509 40.792599, -95.846153 40.848331, -95.834114 40.870300, -95.836258 40.901108, -95.837318 40.974258, -95.860611 41.002651, -95.859253 41.035004, -95.878517 41.065872, -95.857986 41.109188, -95.876396 41.164204, -95.859512 41.166866, -95.858910 41.180538, -95.915810 41.194065, -95.921959 41.207855, -95.910690 41.225246, -95.929939 41.302059, -95.910912 41.308472, -95.897301 41.286865, -95.888817 41.301392, -95.942604 41.340080, -95.939766 41.394810, -95.934776 41.462387, -95.952896 41.472393, -96.006607 41.481960, -96.013161 41.493000, -95.996399 41.511524, -95.993675 41.528111, -96.004303 41.536671, -96.049881 41.524342, -96.085548 41.537529, -96.091644 41.563152, -96.080544 41.576008, -96.111015 41.599014, -96.099014 41.654690, -96.119972 41.684105, -96.121910 41.694923, -96.085266 41.704998, -96.099480 41.731575, -96.099030 41.752987, -96.076126 41.791481, -96.135330 41.862633, -96.159676 41.904163, -96.145576 41.924919, -96.147034 41.966267, -96.184921 41.980698, -96.202545 41.996628, -96.235794 42.001270, -96.238426 42.028450, -96.265182 42.048908, -96.284821 42.123463, -96.351860 42.168194, -96.363205 42.214050, -96.337402 42.229530, -96.332352 42.260315, -96.342575 42.282089, -96.368393 42.298031, -96.389473 42.328796, -96.423866 42.349285, -96.411453 42.380924, -96.417320 42.414783, -96.397583 42.441799, -96.395767 42.467407, -96.439087 42.489246, -96.479935 42.517136, -96.489029 42.564034, -96.500633 42.573891, -96.488190 42.580486, -96.512535 42.629761, -96.540855 42.662411, -96.562729 42.668518, -96.626228 42.708359, -96.640396 42.748608, -96.632668 42.776840, -96.600563 42.799564, -96.587334 42.835388, -96.572815 42.834354, -96.555901 42.846668, -96.537201 42.896915, -96.543953 42.913876, -96.514626 42.952393, -96.516838 42.986469, -96.498711 43.012062, -96.519699 43.051521, -96.479263 43.061897, -96.461784 43.075596, -96.460495 43.087887, -96.451195 43.126324, -96.472801 43.209099, -96.486931 43.217926, -96.558289 43.225506, -96.566673 43.239651, -96.559250 43.253281, -96.570404 43.263630, -96.578812 43.290092, -96.540245 43.307678, -96.522575 43.356987, -96.524734 43.384247, -96.557388 43.400749, -96.588791 43.435562, -96.583473 43.481945, -96.597992 43.499874, -96.460136 43.499744, -96.060738 43.498566, -95.866615 43.498978, -95.464493 43.499577, -95.396278 43.500370, -94.920197 43.499413, -94.859573 43.500072, -94.454987 43.498146, -94.246544 43.498993, -93.973717 43.500343, -93.653481 43.500809, -93.500618 43.500534, -93.054184 43.501495, -93.027016 43.501316, -92.557831 43.500294, -92.452995 43.499496, -92.077370 43.499187, -91.730217 43.499603, -91.610954 43.500656, -91.223434 43.500835, -91.235771 43.464710, -91.210785 43.424076, -91.198112 43.370537, -91.176918 43.353970, -91.078369 43.313320, -91.066299 43.280704, -91.068924 43.257919, -91.161224 43.147594, -91.168442 43.082905, -91.159622 43.081200, -91.152084 43.001331, -91.138992 42.925907, -91.093300 42.871452, -91.081902 42.783375, -91.066040 42.744923, -90.999054 42.707066, -90.919281 42.680683, -90.892418 42.678246, -90.745483 42.657005, -90.694664 42.637932, -90.664253 42.571392, -90.639091 42.555714, -90.625580 42.528561, -90.638329 42.509361, -90.651772 42.494698, -90.648346 42.475643, -90.605827 42.460560, -90.563583 42.421837, -90.491043 42.388783, -90.441597 42.360073, -90.427681 42.340633, -90.417984 42.263924, -90.407173 42.242645, -90.367729 42.210209, -90.323601 42.197319, -90.230934 42.159721, -90.191574 42.122688, -90.176086 42.120502, -90.166649 42.103745, -90.168098 42.061043, -90.150536 42.033428, -90.142670 41.983963, -90.154518 41.930775, -90.195839 41.806137, -90.255310 41.781738, -90.304886 41.756466, -90.326027 41.722736, -90.341133 41.649090, -90.339348 41.602798, -90.348366 41.586849, -90.423004 41.567272, -90.434967 41.543579, -90.454994 41.527546, -90.540840 41.525970, -90.600700 41.509586, -90.658791 41.462318, -90.708214 41.450062, -90.779900 41.449821, -90.844139 41.444622, -90.949654 41.421234, -91.000694 41.431084, -91.027489 41.423508, -91.055786 41.401379, -91.073280 41.334896, -91.102348 41.267818, -91.101524 41.231522, -91.056320 41.176258, -91.018257 41.165825, -90.990341 41.144371, -90.957787 41.104359, -90.954651 41.070362, -90.960709 40.950504, -90.983276 40.923927, -91.049210 40.879585, -91.088905 40.833729, -91.092751 40.761547, -91.119987 40.705402))', 4326);
SET @g = @g.ReorientObject()
DECLARE @g_flat_box geometry = geometry::STGeomFromWKB(@g.STAsBinary(), @g.STSrid).STEnvelope();
DECLARE @g_rnd_box geography = geography::STGeomFromWKB(@g_flat_box.STAsBinary(), @g_flat_box.STSrid);
SELECT @g as [poly], @g_rnd_box as [box]
SET @pIn = geography::STPointFromText('POINT( -90.6204165 41.5795478)',4326)
SET @pOut = geography::STPointFromText('POINT( -80.6204165 31.5795478)',4326)
SELECT STIntersectionIn = @g_rnd_box.STIntersection( @pIn ).ToString(),
STIntersectionOut = @g_rnd_box.STIntersection( @pOut ).ToString(),
STIntersectionIn = @g.STIntersection( @pIn ).ToString(),
STIntersectionOut = @g.STIntersection( @pOut ).ToString()
GO
获得了这个例子的数据
查看您的查询,首先要考虑的是您在 SELECT 列表中包含一个空间列,该列是 .NET/CLR 数据类型,并且这些列存储在 IN_ROW_DATA
需要键查找的页面,除非索引中包含空间列,索引数据页中可能还包含空间边界框,以加快过滤,节省大部分磁盘 I/O。 我想说您发现了一个无需空间索引即可加速空间列过滤的有效技巧。
为了证明我的观点,我建议您参考原始的 SQL 文档,我相信您已经知道,关于 covering indexes,它澄清了以下内容:“Non - 将键列添加到非聚集索引的 leaf level
以提高查询性能。这允许查询优化器从索引扫描中找到所有需要的信息; table 或聚集索引未访问数据.”。最后一部分在这里非常重要,所以我假设边界框是空间列的“必需信息”的一部分,以帮助查询优化器避免访问 IN_ROW_DATA
.
结论:
- 简单的查找操作是否可以解释(1)和(2)中描述的索引之间的性能差异? 我会这么说,因为空间 CLR 数据类型存储在
IN_ROW_DATA
页面之外需要更多磁盘 I/O 在 (1). - 为什么(3)中描述的聚簇索引比(2)中描述的索引慢很多? 同样的道理,在索引(2)中包含地理数据,省去了在
IN_ROW_DATA
页之外查找的需要,节省了大部分磁盘I/O;请记住,索引 (3) 仍然需要在LOB_DATA
. 中查找空间数据
- 如果以上两个都无法回答,那么在比较问题 1 和问题 2 中描述的这两个指标时,我们是否应该看到如此大的性能缺陷,或者更可能是我们的设置有其他问题? N/A.
一切都与磁盘命中有关。
没有几何索引,需要获取和测试 50K 几何。这是 50K 磁盘命中。对于 HDD,一个简单的经验法则是 100/秒。因此 500 秒。查看您的#3:“对返回的每一行执行地理过滤器 [Geography].STIntersects(@p1) = 1”。
要加快速度,您必须使用空间索引并希望它比日期过滤器做得更好。
你的#2 说“包括几何”。这似乎会使索引变得如此庞大,以至于没有真正的帮助。正如您所说的“大致相同”。
(FlightDate, id) 上的聚簇索引,按此顺序将减少磁盘命中 if FlightDate
是最佳起点。 (不知道几何索引会不会更好。)
所以,有两个索引:Clustered(FlightDate, id)和Geometry(几何)。并希望优化器会选择更好的那个。