级别: 中级 developerWorks 中国网站编辑团队, 编辑, IBM
2009 年 4 月 28 日 本系列文章为使用 DB2 pureXML 来有效的解决商业问题和在企业应用程序中高性能的管理 XML 数据提供了原理和指南。样例是基于真实世界金融应用场景的最佳实践,并示范了如果执行这个指南。这个例子可以很容易被应用于其它类型的 XML 应用程序。系列第 1 部分指出了为什么要使用 XML 并概述了 pureXML,第 2 部分描述如何向一个 DB2 数据库中有效添加 XML 数据技术,第 3 部分主要讲如何高效并有效率的查询 XML 数据。第 4 部分将如何使用 XML 索引和如何处理名称空间。本部分将阐述如何有效地更新 XML 数据、如何维护并监控一个 XML 数据库及如何开发 pureXML 应用。
有效的更新 XML 数据
<TIP>DB2 VERSION 9.5 支持标准化的 XQuery Update Facility 这允许你在 DB2 存储层面对一个 XML 文档中进行更改,而不需要在你的应用程序中读取和解析这个文档。这样做显著的提高了应用程序性能,并减少了应用程序的复杂度。你可以更改某个 XML 元素或属性值(全部作为“节点”),你可以用新节点来替换老的节点、删除或重命名节点、或在文档中的某些位置插入新节点。
简单 XML 更新指南
从图 4 中的利率衍生文档。假设在这个交易中你想从 party1 更改 tradeId 。因为每个交易都有两个交易方,更改包含一个谓词表达式来挑选正确的节点来更新非常重要。换句话说,更新的目标节点的路径必须是完全明确的一个节点,不能为空也不能超过一个。否则更新将会失败。图 73 中的更新将会失败并报 SQL16085N 如果它不包含谓词 [partyReference/@href="party1"],因为没有这个谓词会有两个 tradeId 元素满足条件。如果你错误拼写了这个路径中的任何一个元素,这回造成没有节点满足更新条件,更新也会失败。
update trades
set tradedoc = xmlquery('
copy $new := $TRADEDOC
modify do replace value of
$new/FpML/trade/tradeHeader/partyTradeIdentifier[partyReference/@href="party1"]/tradeId
with "MyGlobal999"
return $new')
where tradeid = 456; |
Figure 1: Updating the tradeId 图 73:更新 tradeId
<TIP>如果你需要在一个文档中更新多个元素,在一个更新语句中联合多个更新。假设我们需要更改图 5 中显示的 FpML 交易的 currency 和 amount 。图 74 显示这个更改子句可以包含一个更新表达式列表来更新一个文档中的多个节点。这比在一个事务中执行多个更新语句更有效。
update trades
set tradedoc = xmlquery('
copy $new := $TRADEDOC
modify (
do replace value of $new/FpML/trade/termDeposit/principal/currency
with "EUR",
do replace value of $new/FpML/trade/termDeposit/principal/amount
with 40000000 )
return $new')
where tradeid = 789; |
图 74:同时更新量节点
可以在【 7 】中找到更多 XML 更新的例子
从多个 XML 文档合并数据
当一个应用程序使用的交易文档从数据库中取出来时,他们可能需要用额外的信息来丰富,比如交易各方的更多细节。例如,我们读取交易文档并对每个交易方插入他们在 PARTIES 表中 XML 文档里的名字和汇率信息。结果是一个可以作为一个消息(例如,通过 web 服务)发送给另一个应用程序的单独文档。
在图 75 中的查询就是这么做的。它用 tradeId 123 来读取交易并在 SELECT 子句中使用 XMLQUERY 函数来把一个 XQuery 更新表达式应用到交易文档上。 COPY 子句从 XML 列 TRADEDOC 分配 XML 文档到变量 $new 中。更新子句包含一个 XQuery 循环(for $i in $new/ …)来循环文档中的 party 元素。对于每个 party 元素执行一个插入操作。 XML 元素 Name 和 Rating,通过一个在 partyId 值上的连接它们被插入和从 PARTIES 表的 PARTYIFO XML 列中读取
select xmlquery ('
copy $new := $TRADEDOC
modify
for $i in $new/FpML/party
return do insert
db2-fn:xmlcolumn("PARTIES.PARTYINFO")/Party[PtyID=$i/partyId]/(Name,Rating)
into $i
return $new' )
from trades
where tradeId=123; |
图 75:把 XML 数据插入到一个交易文档中
图 76 显示了上面 SQL 从交易文档中读取的细节。名字和汇率信息的插入发生在查询处理过程中的 on-the-fly 。这不是一个对数据库中文档的永久性的更新。
..(...).
</trade>
..<party id="party1">
<partyId>510026</partyId><Name>MyGlobal International Bank</Name><Rating>
<RatingDate>2006-05-16</RatingDate>
<RatingValue>Baa1</RatingValue>
</Rating>
</party>
<party id="party2">
<partyId>67781</partyId><Name>National Village Bank</Name><Rating>
<RatingDate>2006-06-01</RatingDate>
<RatingValue>Aa</RatingValue>
</Rating>
</party>
</FpML> |
图 76 添加到交易文档中 party 元素的其它信息
用关系数据丰富 XML 文档
如果交易各方信息是纯关系型的并如图 77 所示呢?
create table parties2 (
ptyId BIGINT,
shortname VARCHAR(10),
name VARCHAR(30),
status VARCHAR(30),
ratingDate DATE,
ratingValue VARCHAR(10) ) ; |
图 77:关系表包含 party 汇率信息
我们可以读取 party 名和汇率,并把它们作为 XML 元素插入到一个选定的交易文档。这在图 78 中用不同于之前在图 75 中的两种重要的查询的方法来完成。新的 XML 元素 Name 和 Rating 直接用元素构建器来构建。它们的值是来自于关系列中,并作为变量来引用,比如 {$NAME} 。其次,通过把 $i/partyId 作为一个参数传输到嵌入式 SQL 语句中。 db2-fn:sqlquery 函数在这里有两个参数,SQL 语句和参数。在 SQL 语句中 $i/partyId 的值是通过函数 parameter(1) 来引用的。 DB2 VERSION 9.5 支持这个类型的参数传递。
select xmlquery ('
copy $new := $TRADEDOC
modify for $i in $new/FpML/party
return do insert
db2-fn:sqlquery("select xmlquery(''
<Name>{$NAME}</Name>,
<Rating>
<RatingDate>{$RATINGDATE}</RatingDate>
<RatingValue>{$RATINGVALUE}</RatingValue>
</Rating>'')
from parties2
where ptyID=parameter(1)",$i/partyId)
into $i
return $new' )
from trades
where tradeId=123;
|
图 78:把关系数据作为 XML 元素插入到一个关系文档中
维护并监控一个 XML 数据库
如果你是一个 DBA,对于维护和监控一个有 XML 数据数据库的好消息是 XML 的出现并没有引入任何新的基础函数。 DB2 命令对于大多数普通维护和监控任务包含大量的未经更改的 pureXML,在 XML 数据显示时你只需要了解很少的使用细节。
搜集 XML 数据的统计信息
Runstats命令已经为在 XML 数据和 XML 索引上搜集统计信息进行了扩展。 DB2 的基于成本的优化器使用这些通解信息来为 XQuery 和 SQL/XML 查询生成有效的执行计划。因此,你可以像对关系数据一样继续使用runstats。<TIP>如果你的表包含关系型和 XML 数据并且你只想更新关系型统计信息,你可以执行有 EXCLUDING XML COLUMNS 子句的runstats命令。没有这个子句,默认总会搜集关系型数据和 XML 数据的统计信息。分布统计信息现在只收集表中的关系列。
<TIP>对于关系型数据和 XML 数据,你可以使用抽样来减少执行runstats命令的时间。在一个大的数据集中,10% 的数据的统计信息通常已经足够代表所有数据了。无论你选择多少的采样百分比,runstats命令允许你对行(Bernoulli 抽样)或页面(系统抽样)进行抽样。
图 79 显示了一些例子。第一个runstats命令对 TRADES 表搜集最广泛和详细的统计信息而且所它的所有索引,而不是进行抽样。在查询时间允许的情况下,这是一个理想情况。第二个runstats命令搜集相同的统计信息不过仅对 10% 的页面。在大多数情况下,这将提供给优化器接近于实际统计信息,却只花费很少的时间。第三个runstats命令抽样的行数为 15%,不收集分布统计信息,并对索引进行抽样,这在第一个runstats命令中没有对索引进行抽样。
runstats on table myschema.trades
with distribution and detailed indexes all;
runstats on table myschema.trades
with distribution and detailed indexes all tablesample system (10);
runstats on table myschema.trades
and sample detailed indexes all tablesample bernoulli (15); |
图 79:使用 runstats 命令来收集统计信息
关系型统计信息在表目标中可见,XML 列的统计信息存在表的描述符中是不可见的。<TIP>不过,如果出于支持的目的有必要的话,db2cat实用工具可以用来从 XML 列中把 XML 统计信息导出到一个文本文件 .
XML 索引统计信息和关系型索引非常像,它们也在编目表 SYSCAT.INDEXES 中显示。不过要注意,每个 XML 索引是表现为一个逻辑索引和一个物理索引。逻辑索引包括索引定义和在创建索引语句中索引名字。对应的物理索引包括实际的 B-tree 结构以及有一个系统生成的名字。 XML 索引统计信息是和物理索引相关联而不是逻辑索引。在图 80 中的查询显示了逻辑索引和物理索引的关系,以及 SYSCAT.INDEXXMLPATTERNS 中的其它有用的信息。
select indname, pindname, pattern, datatype from syscat.indexxmlpatterns; |
图 80: 显示逻辑和物理索引之间的关系
监控 XML 工作负载
无论你是研究不同页面大小的好处还是 XML 性能的其它方面,就像你对关系型数据一样试试用 DB2 snapshot 监控器。而且你可以直接尝试!例如在动态 SQL 快照显示 XQuery 和 SQL/XML 语句,就像普通的 SQL 语句那样。
DB2 也为 XML 数据提供缓冲池和表空间快照监控元素,它们匹配现有的关系型数据和索引计数器。因为关系数据和索引在一个表空间中是存储在不同的存储对象中的,它们分别有不同的计数器。相同的,DB2 VERSION 9.5 对 XML 数据使用了一个不同存储对象,叫 XDA(XML 数据域),并且对于那些页面它也有它自己的缓冲池计数器。
在图 8.1 中的例子是一个快照监控器输出的一个片段。你可以看到对于这三种不同的存储对象:data,index 和 XDA 的多个快照监控元素。你可以区别于关系型数据单独监控并分析 XML 的缓冲和 I/O 活动。任何对 XML 索引的活动都被包括在现有索引计数器中了。对 XDA 计数器的交互和相应的关系型计数器一样。例如,一个对 XDA 物理读对 XDA 逻辑读的较低的比率显示 XML 数据较高的缓冲池命中率,这也是我们期望的。更多关于缓冲池快照监控元素的详细信息,参见 DB2 VERSION 9.5 的文档。
注意,XML 内置(见 4.3 章)会造成 XML 数据存储在一个数据对象中而不是 XDA 对象。因此,所有的内置 XML 文档的活动被包括在数据对象计数器中,而不是 XDA 计数器。
Buffer pool data logical reads = 221759
Buffer pool data physical reads = 48580
Buffer pool temporary data logical reads = 10730
Buffer pool temporary data physical reads = 0
Buffer pool data writes = 6
Asynchronous pool data page reads = 0
Asynchronous pool data page writes = 6
Buffer pool index logical reads = 8340915
Buffer pool index physical reads = 54517
Buffer pool temporary index logical reads = 0
Buffer pool temporary index physical reads = 0
Buffer pool index writes = 0
Asynchronous pool index page reads = 0
Asynchronous pool index page writes = 0
Buffer pool xda logical reads = 2533633
Buffer pool xda physical reads = 189056
Buffer pool temporary xda logical reads = 374243
Buffer pool temporary xda physical reads = 0
Buffer pool xda writes
= 0
Asynchronous pool xda page reads = 97728
Asynchronous pool xda page writes = 0
Asynchronous data read requests = 0
Asynchronous index read requests = 0
Asynchronous xda read requests = 83528 |
图 81:快照监控器对数据,索引和 XDA 存储对象的输出
XML 数据的重组和备份并恢复
REORG、备份和恢复 XML 数据的指南和关系型数据库没有区别。 DB2 备份和恢复实用工具自动包含 XML 数据。 REORG 命令不受 XML 文档存储在树形结构中的影响。对在 XML 数据上的 REORG 命令的主要影响是如果在 REORG 命令中使用了 LONGLOBDATA 选项的话,被删除的空间的释放是在删除操作之后。有一个单独的文档提供 DB2 load,REORG 和备份 / 回复实用工具的指南,本文叫做“ Minimizing Planned Outages ”。
开发 pureXML 应用程序
对应用程序开发人员大多数操作 XML 文档的 DB2 pureXML 功能的基础值在应用程序层面将不再需要单调并效率低下的 DOM 编程了。因为在 DB2 pureXML 中 XML 文档是存储在一个解析过的格式中,文档片段或单独的值可以在不需要解析的情况下被抽取或更新。应用程序发送正确的 XML 查询或更新语句给 DB2 来替代预取并解析整个文档。对应用程序依然需要通过 DOM 或 SAX APIs 来访问 XML 数据的情况,他们可以使用新的 JDBC 4.0 功能。这些以及其它的应用程序开发者指南在下面章节中提供。
对简短的 XML 查询使用参数标记
<TIP>非常简短的数据库查询常常执行得非常快,编译的时间和对它们进行优化是它们运行时间的一部分。因此,只对它们编译一次然后在每次执行时传输谓词文字变量。你不能在 XQuery 中使用 SQL 风格的参数标记,SQL/XML 函数 XMLQUERY、XMLTABLE 和 XMLEXISTS 允许你把 SQL 参数作为一个变量传输给内置的 XQuery 表达式。建议有非常简短并重复运行的查询的应用程序这么做。
xquery for $t in db2-fn:xmlcolumn( ‘ TRADES.TRADEDOC')/FpML
where $t/party/partyId = 12345
return $t;
select tradedoc
from trades
where xmlexists('$TRADEDOC/FpML/party[partyId=12345]' ); |
图 82:两个在谓词中使用使用 hard-coded 文字值 XML 查询
select tradedoc
from trades
where xmlexists('$TRADEDOC/FpML/party[partyId=$x]'
passing cast(? as integer) as "x"); |
图 83:使用参数标记的 SQL/XML 查询
避免 DB2 和应用程序之间的代码页转换
XML 不同于 DB2 中的其它数据类型,因为它可以内部编码也可以外部编码。内部编目意味著 XML 数据的编码来自于与数据本身。外部编码意味著数据编码源自于应用程序代码页。你用来跟 DB2 交换 XML 数据的应用程序变量的数据类型决定如何派生编码。如果你的应用程序对 XML 使用字符类型变量,那么它是外部编码(使用应用程序代码页)。如果你使用二进制应用那个程序数据类型,那么 XML 数据将考虑使用内部编码。在这种情况下编码取决于一个 Unicode Byte-Order mark (BOM) 或 XML 文档自己的一个编码声明就像
<?xml version="1.0" encoding="UTF-8" ?>
从性能的角度,我们的目标是尽可能的避免代码页转换,因为这将消耗额外的 CPU 时钟周期。最好使用内部编码的 XML 数据,不要使用外部编码数据,因为它可以防止不必要的代码页转换。也就是说在你的应用程序中你应该尽量使用二进制数据类型而不是字符数据类型。例如,在 CLI 中当你使用 SQLBindParameter() 来在输入数据缓冲中绑定单数标记时,你应该使用 SQL_C_BINARY 数据缓冲而不是 SQL_C_CHAR、SQL_C_DBCHAR 或 SQL_C_WCHAR 。当从 Jave 应用程序插入 XML 数据时,以二进制流(setBinaryStream)的形式读取 XML 数据比作为字符串(setString)的形式更好。同样的,如果你的 Java 应用程序收到了来自 DB2 的 XML 并写入了一个文件中,如果 XML 是以非二进制数据的形式写入的话可能会发生代码转换。
当你从 DB2 中读取 XML 数据到你的应用程序中时,数据将被串行化。串行化是 XML 解析的逆反操作。它处理转换 DB2 的内部 XML 格式(一个解析后的,树形展现)到一个你应用程序能够理解的文本的 XML 格式。在大多数情况下最好让数据库执行隐式的串行化。这意味着你的 SQL/XML 语句简单的选择 XML 类型值,并且 DB2 尽可能有效的串行化到你的应用中。你不需要明确调用使用 XMLSERIALIZE 函数
通过 DOM 或 SAX APIs 来访问 DB2 中的 XML 数据
虽然 DB2 pureXML 功能让你在应用程序层面避免了大量的 XML 解析,通过 DOM API 或 SAX API 访问 XML 文档仍然是有好处的,基于你应用程序的设计和需求。 JDBC 4.0 介绍了一个新数据类型叫做 SQL/XML,同多个方法一起帮助 DOM 和 SAX 访问从 DB2 获取的 XML 文档。这在 DB2 VERSION 9.5 中是被支持的并且在图 84 中举例说明。要使用这些能力你需要一个 Java 6 或更高版本的 SDK 。
// get the result XML as a binary stream
SQLXML sqlxml = resultSet.getSQLXML(tradedoc);
InputStream binaryStream = sqlxml.getBinaryStream();
// get the result XML as a DOMSource
SQLXML sqlxml = resultSet.getSQLXML(tradedoc);
DOMSource domSource = sqlxml.getSource(DOMSource.class);
Document document = (Document) domSource.getNode();
Node myNode = …
// create a SQLXML object with the input XML document in it
DOMResult domResult = sqlxml.setResult(DOMResult.class);
domResult.setNode(myNode);
// set that xml document as the input to parameter marker 1
Mystmt.setSQLXML(1, sqlxml); |
图 84:在 JDBC4.0 中使用 SQLXML 的代码样本
更多关于DB2 VERSION 9.5 中的 JDBC 4.0 的信息,参见下面信息中心的主题:
总结
在本文中显示的最佳实践是通过在性能是一个关键因素的真实世界的应用程序中使用 DB2 pureXML 功能取得的经验教训。这些实践将帮助你避免常见错误,并在使用 pureXML 时很好的调整你的数据库以达到你商业和 IT 环境都期望的目标。
从查看数据开始到以一些维护和监控数据库性能的建议结束,本文中的原则和指南是大体上根据数据库对象的生命周期组织的。本文同样描述了一些应用程序开发人员使用 DB2 pureXML 的最佳实践。本文在一下方面描述了最佳实践:
- 使用有自动存储的 DB2 DMS 表空间
- 为 XML 数据选择正确的页面大小
- 什么时候以及如何为 XML 和关系型数据配置不同的页面大小
- 向数据库中添加 XML 数据方的不同方法:insert、import 和 load
- 如何更有效的添加大量很小的 XML 文档
- 如何对 XML 文档使用基于表的内置存储和压缩
- 验证 XML 数据的方法
- 什么时候以及如何使用 SQL 和 XQuery 来查询 XML 和关系型数据
- 使用路径来查询 XML 文档层次中的数据
- 对 XML 使用索引的指南
- 处理 XML 名称空间的指南
- 更新 XML 文档中的数据
- 收集 XML 数据和 XML 索引的统计信息来提高查询性能
- 监控数据库 XML 工作负载
- 如何提高简短查询的性能
- 在操作 XML 数据和 DB2 时代码页的影响
- 使用 Java DOM 或 SAX APIs
因为 XML 在企业运营中变得越来越重要,它已经成为一个需要共享、持续、搜索、保护以及维护的资产。基于它的使用,XML 数据可能也需要被更新、审计、以及和其它数据进行整合。 DB2 数据库在以 XML 的自然层次格式存储 XML 数据有优势,包括:
- DB2 了解 XML 数据的内部结构
- DB2 可以维护 XML 数据的层次结构和灵活的性质
- 在一个数据库中整合对 XML 文档和关系数据的访问
参考资料 学习
获得产品和技术
- 现在可以免费使用 DB2 。下载DB2 Express-C,这是为社区提供的 DB2 Express Edition 的免费版本,它提供了与 DB2 Express Edition 相同的核心数据特性,为构建和部署应用程序奠定了坚实的基础。
- 下载信息管理软件试用版,体验它们强大的功能。
讨论
关于作者  | |  | developerWorks 中国网站编辑团。 |
对本文的评价
|