【问题标题】:XQuery - Optimize a query for a BIG databaseXQuery - 优化 BIG 数据库的查询
【发布时间】:2012-03-04 22:47:05
【问题描述】:

我有一个大型 xml 数据库(30 000 个文件,1.3 Go)。此数据库中的一个文件列出了数据库中存在的所有其他文件。我的目标是“简单地”检查列出的所有文件是否都存在于数据库中。但是我必须不关心文件的名称,而只关心文档中的 XML 代码。

是这样的:

declare variable $root :=  fn:collection();

declare function local:isValid($fileCode) {

let $fileSearchedIdentCode := $root/dmodule/identity/dmCode
return 
$fileCode/@attribute1 = $fileSearchedIdentCode/@attribute1 and
$fileCode/@attribute2 = $fileSearchedIdentCode/@attribute2 and
$fileCode/@attribute3 = $fileSearchedIdentCode/@attribute3

};

<result>
{ 
for $fileCode in $root/file[identity/@fileType eq 'listOfFiles']/fileContent/fileEntry/fileCode 
return
    if (local:isValid($fileCode))
    then   <filePresent>1</filePresent>  
    else <fileNonPresent>2</fileNonPresent>

}
</result>

上面的代码是为一个小型数据库运行的,但对我来说,它需要大量的时间。

所以,我想知道是否有人可以帮助我改进该代码以便在合理的时间内执行它;)

(我的数据库已编入索引)

感谢您的帮助!!

约翰

【问题讨论】:

  • 我无法编辑我的帖子 :( 抱歉没有说“你好”
  • 打招呼不是惯例。你能告诉你使用哪个数据库吗?使用索引可能需要使用专有扩展,或者优化您的表达式以匹配内置优化。 30k 文档不算多,但足够想用索引了。
  • 我必须在几个数据库(baseX、marklogic、oracle db XML 和 qizx)上创建一个基准。目前我正在使用 BaseX。
  • 优化查询通常涉及给定数据库的细节,所以我不确定这是一个特别好的问题。也就是说,我希望每个数据库都有不同的答案。您列出的所有数据库都不是一个答案。

标签: oracle11g query-optimization xquery marklogic


【解决方案1】:

似乎 属性索引 不适用于local:isValid 函数中的属性检查。您可以通过将它们重写为 XPath 谓词来实现:

declare variable $root :=  fn:collection();

declare function local:isValid($fileCode) {
  $root/dmodule/identity/dmCode[@attribute1 = $fileCode/@attribute1
    and @attribute2 = $fileCode/@attribute2
    and @attribute3 = $fileCode/@attribute3]
};

<result> { 
  for $fileCode in $root/file[identity/@fileType = 'listOfFiles']/fileContent/fileEntry/fileCode 
  return
    if (local:isValid($fileCode))
      then   <filePresent>1</filePresent>  
      else <fileNonPresent>2</fileNonPresent>
}</result>

在这些更改之后,BaseX 中的 Query Info 视图告诉我使用了索引:

Compiling:
- pre-evaluating fn:collection()
- rewriting And expression to predicate(s)
- rewriting fn:boolean(@*:attribute1 = $fileCode/@attribute1)
- rewriting fn:boolean(@*:attribute2 = $fileCode/@attribute2)
- rewriting fn:boolean(@*:attribute3 = $fileCode/@attribute3)
- applying attribute index
- applying attribute index

我的测试数据的评估时间从 4'500ms 下降到 ~20ms。

【讨论】:

  • 当我测试您的解决方案时出现了一些奇怪的情况:BaseX 无法执行某些操作...它运行、运行并再次运行,并带有“请稍候...”,即使是一个小测试。我是不是配置错了?
  • 你有/可以创建一个可管理大小的样本集合,我们可以用它来测试我们的建议吗?这样更容易做到正确。
  • 事实上,我只是用 5 个 XML 文件(28 Mo)进行测试,当我点击“运行”按钮时,它会写成“请稍候...”,但没有任何反应跨度>
  • 很遗憾我不能分享我的文件,它们是空客的数据。但我想这不是数据的问题:为什么只查询 5 个文件需要这么长时间?使用我之前的代码(在第一篇文章中)具有相同的 5 个文件,只需要 3000 毫秒,但现在更改“请稍候...” 30 分钟
  • Johan 您使用的是最新版本的 BaseX 吗?也许您还可以确保索引都是最新的运行optimize all 命令?我猜 Leo 的查询也应该针对您的数据进行优化。
【解决方案2】:

对于 MarkLogic,您需要注意索引查找仅发生在某些表达式和函数中。在这种情况下,您需要更紧凑的代码。这是一个应该产生相同结果的表单,但会以简单的方式使用索引:

<result>
{
    for $fileCode in
      collection()/
      file[identity/@fileType eq "listOfFiles"]/
      fileContent/
      fileEntry/
      fileCode
    let $fc1 := $fileCode/@attribute1/string()
    let $fc2 := $fileCode/@attribute2/string()
    let $fc3 := $fileCode/@attribute3/string()
    return
      if (collection()/
          dmodule/
          identity/
          dmCode[
            @attribute1 eq $fc1][
            @attribute2 eq $fc2][
            @attribute3 eq $fc3])
      then <filePresent>1</filePresent>
      else <fileNonPresent>2</fileNonPresent>
  }
</result>

但是,该代码将为每个 listOfFiles 条目执行一次数据库查找,这不是最佳的。

可以进一步优化。首先,MarkLogic 是一个面向文档的数据库,其中每个文档都有一个唯一的 URI。因此,如果您简单地将三个属性值编码到每个文档 URI 中,效率会高得多。我们可能会使用 string-join(($fc1, $fc2, $fc3), '/') 之类的东西来构建 URI。然后您可以使用doc() 调用检查每个值,这比XPath 查找更有效——即使使用索引也是如此。一旦进行了更改,listOfFiles 文档还可以存储 URI 而不是属性值。

其次,我认为结果格式不是很有用。它会告诉您一些文件丢失了,但没有告诉您哪些文件丢失了。我会重构,以便代码只返回丢失的文档 URI。我们还可以启用 MarkLogic 中可用的额外索引:URI 词典。这会自动维护所有文档 URI 的值索引,有点像您的 listOfFiles 文档。使用 URI 词典,我可以这样写:

<result>{
    let $uris :=
      collection()/
      file[identity/@fileType eq "listOfFiles"]/
      fileContent/
      fileEntry/
      fileCode/
      string-join(
        (@attribute1/string(),
         @attribute2/string(),
         @attribute3/string()),
        "/")
    let $uris-present := cts:uris((), "document", cts:document-query($uris))
    for $uri in $uris
    where not($uri = $uris-present)
    return <missing>{ $uri }</missing>
}</result>

这只需要一次数据库查找,其余的必要工作都在内存中完成。它应该比您的原始查询或我的第一次迭代要好得多。如果您不同意我对结果格式的修改,并且仍然希望看到每个输入 fileCode 的结果,您可以将 ...where...return... 子句重构为 ...return...if...then...else...,就像在原始查询中一样。

请务必使用https://github.com/marklogic/cq 中的配置文件工具 - 它可以帮助您尝试替代方案并发现优化机会。

【讨论】:

  • 快速附录重新:分析。一个类似的配置文件工具现在作为查询控制台的一部分随产品一起提供(将浏览器指向http://yourserver:8000/qconsole),所以如果你运行的是 MarkLogic 5,你就不需要 cq。
【解决方案3】:

您的测试系统列表中没有包含 eXist-db,但如果您有兴趣使用您的数据对其进行基准测试,这里有一篇很棒的文章,介绍了优化查询和智能地使用索引来加速 eXist 的性能- D b。见http://exist-db.org/exist/tuning.xml。您发布的查询无需修改即可正常工作,但文章中的建议肯定会帮助您提高性能。如果您需要帮助,请随时发布到存在开放邮件列表。

无论您使用哪种系统,我都非常想知道您的结果——不仅是我——我认为会有广泛的兴趣。

祝你好运!

【讨论】:

    【解决方案4】:

    如果您为比较中使用的每个属性定义一个属性范围索引,MarkLogic 应该会快速处理您的查询。

    您可以通过 MarkLogic Admin UI (http://hostname:8001) 执行此操作:

    • 在“数据库”下选择您的数据库
    • 选择左侧的属性范围索引
    • 选择添加以定义新的属性范围索引
    • 指定引用范围索引的元素(dmcode)和属性(attribute1、attribute2、attribute3)(如果您的元素在某个命名空间中,请不要忘记指定命名空间)。
    • 单击“确定”创建范围索引。

    您使用的是哪个版本的 MarkLogic? 如果您使用的是 MarkLogic 5,您还可以使用 Query Console 来测试您的查询:

    (http://主机名:8000/qconsole)

    如果您有任何问题,请随时询问/让我知道进展如何。 我来自 MarkLogic,很乐意提供帮助。

    【讨论】:

      【解决方案5】:

      将“eq”替换为等号 (=) 是否有帮助?

      【讨论】:

      • 它在性能方面没有任何改变......我刚刚测试过
      • 您是否尝试过不同版本的 basex(例如 BaseX 7 vs 7.1)?一般来说,像你这样的查询应该使用现有的(自动创建的)值索引进行优化,所以请随时为我们提供一些匿名的 XML sn-ps!
      猜你喜欢
      • 2011-02-27
      • 1970-01-01
      • 2019-03-30
      • 2017-01-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多