Shn*_*ugo 32 performance xml sql-server sql-server-2012
为了在那里给出答案,我做了以下测试场景。
首先我创建一个测试表并用 100.000 行填充它。一个随机数(0 到 1000)应该为每个随机数产生 ~100 行。这个数字被放入一个 varchar col 并作为一个值放入您的 XML。
然后我做一个像 OP 那样的调用,需要它使用 .exist() 和 .nodes() ,第二个有一个小优势,但都需要 5 到 6 秒。事实上,我调用了两次:第二次以交换的顺序和稍微改变的搜索参数和“//item”而不是完整路径来避免通过缓存结果或计划产生误报。
现在 - 真正让我感到惊讶的是什么!-在.nodes
用完整路径是比以前(9秒)慢得多,但.exist()
下降到半秒,用全路径甚至下降到约0.10秒。(同时.nodes()
具有短的路径比较好,但仍远远落后于.exist()
)
我自己的测试简而言之:XML 索引可以极大地破坏数据库。它们可以极大地加快速度(s.edit 2),但也可以减慢您的查询速度。我想了解它们是如何工作的...什么时候应该创建一个 XML 索引?为什么.nodes()
有索引比没有索引更糟糕?如何避免负面影响?
CREATE TABLE #testTbl(ID INT IDENTITY PRIMARY KEY, SomeData VARCHAR(100),XmlColumn XML);
GO
DECLARE @RndNumber VARCHAR(100)=(SELECT CAST(CAST(RAND()*1000 AS INT) AS VARCHAR(100)));
INSERT INTO #testTbl VALUES('Data_' + @RndNumber,
'<error application="application" host="host" type="exception" message="message" >
<serverVariables>
<item name="name1">
<value string="text" />
</item>
<item name="name2">
<value string="text2" />
</item>
<item name="name3">
<value string="text3" />
</item>
<item name="name4">
<value string="text4" />
</item>
<item name="name5">
<value string="My test ' + @RndNumber + '" />
</item>
<item name="name6">
<value string="text6" />
</item>
<item name="name7">
<value string="text7" />
</item>
</serverVariables>
</error>');
GO 100000
DECLARE @d DATETIME=GETDATE()
SELECT #testTbl.*
FROM #testTbl
CROSS APPLY XmlColumn.nodes('/error/serverVariables/item[@name="name5" and value/@string="My test 600"]') AS a(b);
SELECT CAST(GETDATE()-@d AS TIME) AS NodesFullPath_no_index;
GO
DECLARE @d DATETIME=GETDATE();
SELECT *
FROM #testTbl
WHERE XmlColumn.exist('/error/serverVariables/item[@name="name5" and value/@string="My test 600"]') = 1;
SELECT CAST(GETDATE()-@d AS TIME) AS ExistFullPath_no_index;
GO
DECLARE @d DATETIME=GETDATE();
SELECT *
FROM #testTbl
WHERE XmlColumn.exist('//item[@name="name5" and value/@string="My test 500"]') = 1;
SELECT CAST(GETDATE()-@d AS TIME) AS ExistShortPath_no_index;
GO
DECLARE @d DATETIME=GETDATE()
SELECT #testTbl.*
FROM #testTbl
CROSS APPLY XmlColumn.nodes('//item[@name="name5" and value/@string="My test 500"]') AS a(b);
SELECT CAST(GETDATE()-@d AS TIME) AS NodesShortPath_no_index;
GO
CREATE PRIMARY XML INDEX PXML_test_XmlColum1 ON #testTbl(XmlColumn);
CREATE XML INDEX IXML_test_XmlColumn2 ON #testTbl(XmlColumn) USING XML INDEX PXML_test_XmlColum1 FOR PATH;
GO
DECLARE @d DATETIME=GETDATE()
SELECT #testTbl.*
FROM #testTbl
CROSS APPLY XmlColumn.nodes('/error/serverVariables/item[@name="name5" and value/@string="My test 600"]') AS a(b);
SELECT CAST(GETDATE()-@d AS TIME) AS NodesFullPath_with_index;
GO
DECLARE @d DATETIME=GETDATE();
SELECT *
FROM #testTbl
WHERE XmlColumn.exist('/error/serverVariables/item[@name="name5" and value/@string="My test 600"]') = 1;
SELECT CAST(GETDATE()-@d AS TIME) AS ExistFullPath_with_index;
GO
DECLARE @d DATETIME=GETDATE();
SELECT *
FROM #testTbl
WHERE XmlColumn.exist('//item[@name="name5" and value/@string="My test 500"]') = 1;
SELECT CAST(GETDATE()-@d AS TIME) AS ExistShortPath_with_index;
GO
DECLARE @d DATETIME=GETDATE()
SELECT #testTbl.*
FROM #testTbl
CROSS APPLY XmlColumn.nodes('//item[@name="name5" and value/@string="My test 500"]') AS a(b);
SELECT CAST(GETDATE()-@d AS TIME) AS NodesShortPath_with_index;
GO
DROP TABLE #testTbl;
Run Code Online (Sandbox Code Playgroud)
这是本地安装在中型笔记本电脑上的 SQL Server 2012 的一个结果在这个测试中,我无法重现对 的极端负面影响NodesFullPath_with_index
,尽管它比没有索引要慢......
NodesFullPath_no_index 6.067
ExistFullPath_no_index 6.223
ExistShortPath_no_index 8.373
NodesShortPath_no_index 6.733
NodesFullPath_with_index 7.247
ExistFullPath_with_index 0.217
ExistShortPath_with_index 0.500
NodesShortPath_with_index 2.410
Run Code Online (Sandbox Code Playgroud)
根据 TT 的建议,我使用了上面的 XML,但复制了item
-nodes 以达到大约 450 个项目。我让命中节点在 XML 中非常高(因为我认为这.exist()
会在第一次命中时停止,而.nodes()
会继续)
创建 XML 索引将 mdf 文件炸毁到~21GB,~18GB 似乎属于索引 (!!!)
NodesFullPath_no_index 3min44
ExistFullPath_no_index 3min39
ExistShortPath_no_index 3min49
NodesShortPath_no_index 4min00
NodesFullPath_with_index 8min20
ExistFullPath_with_index 8,5 seconds !!!
ExistShortPath_with_index 1min21
NodesShortPath_with_index 13min41 !!!
Run Code Online (Sandbox Code Playgroud)
Mik*_*son 33
这里肯定有很多事情发生,所以我们只需要看看这会导致什么。
首先,SQL Server 2012 和 SQL Server 2014 之间的时间差异是由于 SQL Server 2014 中的新基数估计器。您可以使用 SQL Server 2014 中的跟踪标志来强制使用旧估计器,然后您将看到相同的时间SQL Server 2014 中的特性与 SQL Server 2012 中的相同。
比较nodes()
vsexist()
是不公平的,因为如果 XML 中的某一行有多个匹配的元素,它们将不会返回相同的结果。exist()
无论如何都会从基表中返回一行,而nodes()
可能会为基表中的每一行返回多于一行。
我们知道数据,但 SQL Server 不知道,并且必须构建一个考虑到这一点的查询计划。
要使nodes()
查询等效于exist()
查询,您可以执行以下操作。
SELECT testTbl.*
FROM testTbl
WHERE EXISTS (
SELECT *
FROM XmlColumn.nodes('/error/serverVariables/item[@name="name5" and value/@string="My test 600"]') AS a(b)
)
Run Code Online (Sandbox Code Playgroud)
对于这样的查询,使用nodes()
or之间没有区别exist()
,这是因为 SQL Server 为不使用索引的两个版本构建了几乎相同的计划,而在使用索引时构建了完全相同的计划。SQL Server 2012 和 SQL Server 2014 都是如此。
对于我在 SQL Server 2012 中,没有 XML 索引的nodes()
查询使用上述查询的修改版本需要 6 秒。使用完整路径或短路径没有区别。使用 XML 索引后,完整路径版本最快,需要 5 毫秒,使用短路径需要大约 500 毫秒。检查查询计划将告诉您为什么存在差异,但简短版本是当您使用短路径时,SQL Server 在短路径上的索引中查找(使用范围查找like
)并在丢弃行之前返回 700000 行值不匹配。使用完整路径时,SQL Server 可以直接使用路径表达式与节点的值一起进行查找,并从头开始只返回 105 行进行处理。
使用 SQL Server 2014 和新的基数估计器,使用 XML 索引时这些查询没有区别。如果不使用索引,查询仍然需要相同的时间,但它是 15 秒。使用新东西时显然不是改进。
不确定我是否完全忘记了您的问题实际上是关于什么的,因为我将查询修改为等效的,但这是我现在相信的。
为什么
nodes()
带有 XML 索引的查询(原始版本)比不使用索引时慢得多?
嗯,答案是 SQL Server 查询计划优化器做了一些不好的事情,那就是引入了一个假脱机操作符。我不知道为什么,但好消息是 SQL Server 2014 中的新基数估计器不再存在。如果
没有索引,无论使用什么基数估计器,查询都需要大约 7 秒。对于索引,旧的估算器 (SQL Server 2012) 需要 15 秒,而新的估算器 (SQL Server 2014) 需要大约 2 秒。
注意:以上发现对您的测试数据有效。如果您更改 XML 的大小、形状或形式,则可能会有一个完全不同的故事。如果不使用表中实际拥有的数据进行测试,就无法确定。
XML 索引的工作原理
SQL Server 中的 XML 索引是作为内部表实现的。主 XML 索引使用基表的主键加上节点 id 列创建表,总共 12 列。它将有一行,element/node/attribute etc.
因此该表当然可以根据存储的 XML 的大小变得非常大。有了主 XML 索引,SQL Server 可以使用内部表的主键来定位基表中每一行的 XML 节点和值。
辅助 XML 索引分为三种类型。当您创建二级 XML 索引时,会在内部表上创建一个非聚集索引,并且根据您创建的二级索引类型,它将具有不同的列和列顺序。
从CREATE XML INDEX (Transact-SQL):
VALUE
在键列是主 XML 索引(节点值和路径)的列上创建辅助 XML 索引。PATH
在基于主 XML 索引中的路径值和节点值的列上创建辅助 XML 索引。在 PATH 二级索引中,路径和节点值是在搜索路径时允许高效搜索的关键列。PROPERTY
在主 XML 索引的列(PK、路径和节点值)上创建辅助 XML 索引,其中 PK 是基表的主键。
因此,当您创建 PATH 索引时,该索引中的第一列是路径表达式,第二列是该节点中的值。实际上,路径以一种压缩格式存储并反转。它是反向存储的,这使它在使用短路径表达式的搜索中很有用。在您的短路径案例中,您搜索了//item/value/@string
,//item/@name
和//item
。由于路径在列中反向存储,SQL Server 可以使用范围查找,like = '€€€€€€%
其中€€€€€€
路径反向。当您使用完整路径时,没有理由使用,like
因为整个路径都在列中进行编码,并且该值也可以在搜索谓词中使用。
您的问题:
什么时候应该创建一个 XML 索引?
如果有的话,作为最后的手段。最好设计您的数据库,这样您就不必使用 XML 中的值来过滤 where 子句。如果您事先知道需要这样做,您可以使用属性提升来创建一个计算列,您可以在需要时对其进行索引。自 SQL Server 2012 SP1 起,您还可以使用选择性 XML 索引。幕后的工作与使用常规 XML 索引几乎相同,只是您在索引定义中指定路径表达式,并且仅对匹配的节点进行索引。这样你就可以节省很多空间。
为什么带有索引的 .nodes() 会比没有索引更糟糕?
在表上创建 XML 索引时,SQL Server 将始终使用该索引(内部表)来获取数据。该决定是在优化器对什么快什么不快有发言权之前完成的。优化器的输入被重写,因此它使用内部表,然后由优化器像常规查询一样尽力而为。当不使用索引时,会使用几个表值函数来代替。最重要的是,如果不进行测试,您无法判断什么会更快。
如何避免负面影响?
测试
归档时间: |
|
查看次数: |
4493 次 |
最近记录: |