|
级别: 中级 Matthias Nicola (mnicola@us.ibm.com), DB2/XML 性能负责人, IBM 硅谷实验室 2006 年 11 月 30 日 DB2® 9 提供了 pureXML 存储并给出 XQuery 和 SQL/XML 作为查询语言。XML 索引是高查询性能所必需的,但是其在查询评估方面的使用取决于查询谓词的表示方式。本文以一致的方式给出了一组指导原则,用于编写 XML 查询和创建 XML 索引,从而如期加快查询速度。还介绍了需要在 XML 查询执行计划中查找的内容,以便检测性能问题,然后找到解决这些问题的方法。可下载的 “备忘单” 概括了最重要的一些指导原则。 DB2 9 提供了 pureXML 存储以及 XML 索引、作为查询语言的 XQuery 和 SQL/XML、XML 模式支持、对实用程序(如 Import/Export 和 Runstats)的 XML 扩展。正如在关系查询中一样,索引对于高性能的 XQuery 和 SQL/XML 是至关重要的。DB2 允许在 XML 列上定义 path-specific XML 索引。这意味着可以使用它们来索引频繁在谓词和连接中使用的所选择的元素和属性。例如,使用图 1 中的示例数据,基于 author ID,可以使用下面的索引
图 1. 以文本(连续)格式和解析(分层)格式表示的示例 XML 文档 由于 DB2 不强制要求将单个 XML 模式与 XML 列中的所有文档相关联,所以特定元素和属性的数据类型事先是未知的。因此,要求为每个 XML 索引指定一个目标类型。稍后,您将在本文了解到为什么类型很重要。可以使用的 XML 索引数据类型如下:
VARCHAR(n) 索引的长度是一个强制约束。如果插入一个文档,其中索引元素或属性的值超过了最大长度 n,则插入操作将失败。同样,如果索引元素或属性的值大于 n,则 VARCHAR(n) 索引的 DOUBLE、DATE 或 TIMESTAMP 索引不是强制约束。例如,将 author ID 属性上的索引 您可以在 “Indexing XML Documents in DB2”(developerWorks,2006 年 5 月)中找到有关定义 XML 索引的更多详细内容。下面在讨论 XML 索引的使用时,也假定您熟悉查询 DB2 中 XML 数据的基本概念。有关更多信息,请参考以前的文章:“用 SQL 查询 DB2 XML 数据”(developerWorks,2006 年 3 月)和 “使用 XQuery 查询 DB2 XML 数据”(developerWorks,2006 年 4 月)给出了介绍,“DB2 9 中的 pureXML:怎样查询您的 XML 数据?”(developerWorks,2006 年 6 月)给出了更多示例和详细内容。
适合于 XQuery 和 SQL/XML 语句的 XML 索引 正如在关系查询中一样,索引对于高性能的 XQuery 和 SQL/XML 语句是至关重要的。当应用程序向 DB2 提交关系查询或 XML 查询时,查询编译器将比较查询谓词和现有的索引定义,然后确定是否存在可用索引用于执行查询。该过程被称为 “索引匹配”,并且为给定的查询生成一组合适的索引(可能是空集)。将该组索引输入到基于开销的优化器,用来决定是否使用任何合适的索引。本文的专注于索引匹配,而不是优化器的索引选择。在优化器决策方面,除了运行 “runstats” 来为优化器提供关于数据的准确的统计之外,所能做的事情不是很多。但却有大量的工作可以做来确保索引匹配。 在关系查询中,索引匹配通常是微不足道的。DB2 可以使用定义在单个关系列上的索引来响应此列上的任何等式谓词或范围谓词。但是,对于 XML 列,这将更加复杂。关系列上的索引包含了此列的所有值,而 XML 索引仅包含那些同时匹配 XML 模式和索引定义中的数据类型的节点值。因此,仅当 XML 索引拥有 “正确的”数据类型并且至少包含满足谓词的所有 XML 节点时,该 XML 索引才能用于评估 XML 查询谓词。对于 XML 索引的合格性,有两个主要要求:
本文说明了如何设计 XML 索引和查询以确保符合上述要求,以及如何避免常见错误。先从了解查询执行计划开始。DB2 中现有的解释工具(例如 Visual Explain 和 db2exfmt)可用于查看 XQuery 和 SQL/XML 的查询执行计划,正像它们在传统 SQL 中的作用一样。 为了执行 XML 查询,DB2 9 引入了三个新的内部查询操作符,名为 XSCAN、XISCAN 和 XANDOR。这些新操作符和现有的查询操作符(例如 TBSCAN、FETCH 和 SORT)允许 DB2 生成 SQL/XML 和 XQueries 的执行计划。现在看一下这三个新操作符,以及它们与 XML 索引在执行计划中是如何工作的。 XSCAN(XML 文档扫描) XISCAN(XML 索引扫描) XANDOR (连接 XML 索引) 下面看一个示例查询,分别了解不带索引、带一个索引和带多个索引的执行计划:
在图 2 中可以看到该查询的不同执行计划(简化了 db2exfmt 的输出)。因为执行计划中的逻辑流是自下而上、从左到右,所以看此类执行计划时,最好是从树中左下方的操作符开始。 如果该查询中没有合适的索引用于谓词,则使用最左边的计划 (a)。表扫描操作符 (TBSCAN) 将读取表 “BOOKS” 中的所有行。对于每一行,嵌套循环连接 (NLJOIN) 操作符把指向相应的 XML 文档的指针传递给 XSCAN 操作符。同样地,NLJOIN 并没有充当拥有两个输入的标准连接,而是协助 XSCAN 操作符来访问 XML 数据。XSCAN 操作符将遍历每个文档、评估谓词,如果满足谓词,则提取 “authors” 元素。RETURN 操作符将完成查询执行,并将查型结果返回到 API。 图 2. 三个执行计划:(a) 没有索引、(b) 一个索引、(c) 两个索引 如果有一个索引用于一个或两个谓词,例如 如果有用于两个谓词的索引,则可以参看图 2 中的计划 (c)。该计划使用两个 XISCAN,分别用于每一个谓词和索引。XANDOR 操作符使用这些 XISCAN 来轮流检查两个索引,以便有效地找到同时匹配两个谓词的文档的行 ID。FETCH 操作符仅对这些行进行检索,因此将表的 I/O 减到最少。随后对于每一个文档,XSCAN 将提取 “authors” 元素。如果谓词在路径中包括了 // or *,或者使用了范围比较(例如 < 和 >),则用索引 AND'ing (IXAND) 操作符代替 XANDOR。从逻辑上讲,这两个操作符执行了相同的操作,只不过是用于不同类型的谓词且使用不同的优化方法。 优化器可以决定不使用索引,即使该索引是可以使用的。例如,如果第二个索引没有有效减少表中需要检索的行数,例如访问索引所需的开销比节约表的 I/O 更重要,则优化器可能选择计划 (b) 而不是计划 (c)。然而,需要确保优化器考虑了所有合适的索引,从而以最低的开销和最短的执行时间来选择计划。换句话说,要遵守 XML 索引合格性的两个要求:
通配符 // 和 * 可以影响索引和查询谓词之间的包含关系。这是因为路径表达式是不同的,例如 现在,看一下通配符是如何影响索引的合格性的。以下面的查询为例。表 1 中演示了其 where 语句的四种变化。
表 1 最右侧的两列表示两个可选择的索引定义,表中各行展示了谓词可以 (+) 或不可以 (-) 由这两个索引进行评估。下面浏览一下表 1 中的行,来研究每个谓词的索引合格性。 对于第一个谓词,因为它仅包含了 “book” 的直接子元素 “price”,所以 第三个谓词使用了星号 (*) 作为通配符,这样它将查找 “book” 下值为 29 的任何子元素。不仅是 “price” 元素可以满足该谓词。例如,元素 表 1:索引合格性与 XML 索引和谓词中的通配符
第四个谓词 在 nutshell 中,DB2 查询编译器必须始终能够检验索引的限制等同于或低于谓词的限制,以便包含谓词正在查找的所有内容。 应意识到,在索引定义中使用通配符可能会不经意地索引更多节点(多于所需节点)。只要有可能,建议使用索引定义和查询中所需元素或属性的准确路径,而不使用通配符。诸如 //* or //text() 这类很普通的 XML 索引模式是可以接受的,但是应慎重使用。 需要注意 XML 索引合格性是否包含了名称空间。首先,如果表的 XML 文档包含了名称空间,则索引定义必须考虑名称空间。这再次涉及到索引/谓词的容纳性。以下面的 XML 文档和索引定义为例:
因为将索引
索引 查询 4:
查询 5:
查询 6:
表 2 中每一行用于一个查询,每一列分别用于前面定义的索引 表 2:索引合格性与 XML 索引和谓词中的名称空间
除了索引或谓词与通配符和名称空间的包容性之外,索引合格性的第二个要求是谓词和索引的数据类型必须匹配。在所有上述示例中, 关系索引的数据类型始终由索引列的类型来确定。不过,由于 DB2 不强制要求将 XML 模式与 XML 列相关联,所以元素或属性的数据类型不是预先确定的。因此,每个 XML 索引需要一个目标类型。而且类型是很重要的。假定 price 元素拥有值 9。字符串谓词 "9" < "29" 为假,而数值比较 9 < 29 为真。这里强调了如果想从语义上进行正确的数值比较,则应使用 DOUBLE 索引。最好将 “price” 元素作为 DOUBLE 进行索引。 表 3:XML 索引和谓词中的数据类型
在上述示例中,看到了包括文字值的值谓词。这些文字值决定了比较的数据类型。通常此类决定不适用于连接谓词。假定有一个表 “authors”,以 XML 格式表示详细的作者信息,包括出现在 book 数据中的 author ID。现在想使用连接来检索详细的 author 数据,且仅对 books 表中书籍的 authors 进行检索。在 author ID 上定义索引看起来很有用:
该查询检索了所需的作者信息,但是没有将索引用于连接处理。请注意 author ID 上的连接谓词没有包含文字值,该文字值将表明比较的数据类型。因此,DB2 必须考虑匹配任何数据类型的 author ID。例如,考虑表 4 中的 book 和 author 数据。Author John Doe 有一个数值 ID 值 (47),而 author Tom Noodle 有一个非数值 ID 值 (TN28)。在其他表中二者都有有效匹配。因此,二者必须包括在连接结果中。但是,如果 DB2 使用数值索引 authorIdx1 或 authorIdx2,则不会找到 author ID “TN28”,并且会返回一个不完全的结果。因此,DB2 不能使用那些索引,而是会采取表扫描来确保正确的查询结果。 表 4:示例 book 和 author 数据
在两种情况下,您可以帮助 DB2 来使用 DOUBLE 索引:
下面看一下这两种情况。 如果想忽略非数值 author ID,则可以在查询中指明,从而允许 DB2 使用 DOUBLE 索引。以下查询明确地将连接谓词的两端转换为 DOUBLE。这要求进行数值比较,并且显然不接受非数值连接匹配。因此,DB2 可以使用 DOUBLE 索引用于快速连接处理。由于遇到非数字值时,DOUBLE 转换将失败,因此应添加谓词,将非数值 author ID 排除在两个表之外。例如,该查询只考虑大于 0 的数值 ID。
如果知道不存在非数值 author ID,则不需要这些额外的谓词,查询就像如下所示(仍使用 DOUBLE 索引):
由于该查询没有包含用来限制 books 或 authors 表的值谓词,因此 DB2 必须在两个表上执行表扫描,以便读取所有 author ID。对于每个 author ID,使用索引来检查该 ID 是否在另一个表中出现。这比不使用任何索引的两个表扫描的嵌套循环连接要快得多。DB2 的基于开销的优化器决定了要扫描的表以及通过索引要访问的内容。表 5 展示了这两个执行计划。这两个执行计划也适用于采用 SQL/XML 编写的同样的连接,而 XMLEXISTS 谓词的样式决定了使用哪一个计划: 查询 1:
查询 2:
在这两个查询中,方括号中表示了连接谓词。查询 1 中,方括号中的谓词是以 $b 开始的表达式上的谓词,表示是一个可以使用索引的 查询 2 中,连接条件是以 $a 开始的表达式上的谓词,即 表 5:XML 连接查询的执行计划,由 db2exfmt 产生
总结一下关于 XML 连接查询的建议,通常将连接谓词转换为应使用的 XML 索引类型。否则,查询语义不允许使用索引。如果将 XML 索引定义为 DOUBLE,则用 xs:double 转换连接谓词。如果将 XML 索引定义为 VARCHAR,则用 fn:string 转换连接谓词,如表 6 所示。 表 6:转换连接谓词以便允许使用 XML 索引
XQuery 没有类似于关系查询中 “between” 谓词的专用功能或操作符。另外,当表达 “between” 条件时,需要注意 XQuery 一般比较谓词的存在本质。 假定您想找到价格在 20 到 30 之间的书。直观上您可能使用谓词
由于一般比较(>、<、=、<= 等等)有存在语义,如果存在值大于 20 的子元素 “price”,同时存在值小于 30 的子元素 “price”,则谓词 如果 DB2 在 20 到 30 之间使用单个索引范围扫描,则会错过该文档,并返回不完全的查询结果。或者 DB2 必须计算两个索引扫描的交集,通常这样做的开销将相当高。执行计划的差别如图 3 所示。左侧的执行计划展示了索引 AND'ing 的执行计划,DB2 必须考虑捕获示例文档。该计划是无效的,因为两个 XISCAN 可能产生相当多数量的行 ID,需要使用上述 IXAND 操作符从中排除许多 ID。原因是很多书价格高于 $20,同时还有很多价格低于 $30 的书。实际上,两个组合的 XISCAN 产生的行 ID 多于表中的行。这需要 IXAND 操作符仅仅为了找到一个小交集而进行繁重的工作。 如果您的目的是要实现真正的 “between” 谓词,则右侧的执行计划会更好一些,因为带有启动谓词的单个范围扫描仅传递匹配的行 ID。只有相当少的索引访问且没有索引 AND'ing,这样整体性能将提高一个或两个数量级 —— 取决于谓词选择。 图 3:对比索引 AND'ing 和单个范围扫描,评估一对范围谓词
仅当 DB2 可以确定数据项是单个项且不是多于一项的序列时,相同数据项上的一对范围谓词才可以由 DB2 编译器解释为 “between”,并由单个索引扫描进行评估。换句话说,必须这样来表示谓词,即 between(> 和 <)的两个部分始终用于相同的单个项。利用值比较(>、<、=)、self 轴或属性可以实现上述要求。 值比较 Self 轴 属性 简单回顾一下什么是文本节点。图 4 展示了示例文档及其在 XML 数据模式中的分层格式。每个元素由元素节点表示,实际的数据值由文本节点表示。在 XML 数据模型中,将元素的值定义为该元素下子树中所有文本节点的串联。因此,元素 “book” 的值为 “Database Systems29”。最底层元素的值等于其文本节点,例如,元素 “price” 的值为 29。 图 4:示例文档的 XML 数据模型
XPath 表达式
第一个查询将返回完整的元素节点,即
但是,在下一个查询中,将
因此,一般情况下,查询语义是不同的,这取决于
既然 表 7:带有或不带有 /text() 的索引和谓词
简单地说,建议不要在 XML 索引定义或查询谓词中使用 在上述章节中,看到了在元素
有一些情况下,非末端元素上的索引是有意义的。例如,假定您的查询包含区号和完整电话号码上的谓词。在这种情况下,您可以选择设计 phone 元素,如本文档中所示。
然后,可以在非末端元素 “phone” 和元素 “areacode” 上分别定义一个 XML 索引:
这将允许下面两个查询来使用索引访问,而不是表扫描。
XML 索引不能组合类似多列关系索引的关键索引。即,不能在两个或多个 XML 模式上定义单个索引。但是如果对元素进行了适当地嵌套,则有时可以模拟组合索引。例如,上述索引 phoneidx 更像是
所有已讨论的 XML 索引合格性的指导原则都适用于 XQuery 和 SQL/XML 查询。另外,对于 SQL/XML 函数 XMLQUERY 和 XMLEXISTS,还需要考虑一些特定因素。 如果在 SQL 语句的 在 XMLEXISTS 中表示谓词时,请确保使用了方括号,例如 有关这些 XMLQUERY 和 XMLEXISTS 语义的更多详细示例,请参考 “DB2 9 中 15 个 pureXML 性能最佳实践”(developerWorks,2006 年 10 月)。 请注意 XQuery
DB2 9 也不会将索引用于出现在父级(“..”)下的谓词,例如下面两个查询中 “price” 上的谓词:
这不是一个很重要的限制,因为可以始终不用 parent 轴来表示这些谓词:
|
