#InterSystems IRIS for Health

0 关注者 · 862 帖子

InterSystems IRIS for Health™ 是全球第一个也是唯一一个专门为医疗应用程序的快速开发而设计的数据平台,用于管理全世界最重要的数据。它包括强大的开箱即用的功能:事务处理和分析、可扩展的医疗保健数据模型、基于 FHIR 的解决方案开发、对医疗保健互操作性标准的支持等等。所有这些将使开发者能够快速实现价值并构建具有突破性的应用程序。了解更多信息

文章 姚 鑫 · 五月 14, 2021 2m read

Caché Global

第一章 简介global☆☆☆☆☆

第二章 全局变量结构(一)☆☆☆☆☆

第二章 全局变量结构(二)☆☆☆☆☆

第三章 使用多维存储(全局变量)(一)☆☆☆☆☆

第三章 使用多维存储(全局变量)(二)☆☆☆☆☆

第三章 使用多维存储(全局变量)(三)☆☆☆☆☆

第三章 使用多维存储(全局变量)(四)☆☆☆☆☆

第四章 多维存储的SQL和对象使用(一)☆☆☆☆☆

第四章 多维存储的SQL和对象使用(二)☆☆☆☆☆

第五章 管理全局变量(一)☆☆☆☆☆

第五章 管理全局变量(二)☆☆☆☆☆

第六章 临时全局变量和IRISTEMP数据库☆☆☆☆☆

前言

经过快一个月的连载 《Caché Global》 共12篇。对于刚接触M的语言的同学,由浅入深帮助你快速进步,对于老手,丰富更多的细节

涵盖以下主题:

  • “简介”概述了全局变量的功能和用途。
  • “全局变量结构”描述了全局变量是如何存储在磁盘上的,它们是如何命名和引用的,以及它们的结构。
  • “使用多维存储(全局)”介绍如何以编程方式使用全局变量。
  • “多维存储的SQL和对象使用”描述了对象和SQL引擎如何使用全局变量存储数据。
  • “管理全局变量”介绍了主要从管理门户管理全局的工具。
  • “临时全局变量和TEMP数据库”描述了如何使用临时全局变量来帮助进行复杂的处理。

预告

下一期系列将用一个月的时间连载,《IRIS 单元测试》《Caché 网络实用工具》,敬请期待。

交流群

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f9VqwzNP-1608850948003)(3E1D939266954ED48BDAEA9B8086B11E)]

0
0 292
文章 姚 鑫 · 五月 14, 2021 3m read

第六章 临时全局变量和IRISTEMP数据库

对于某些操作,可能需要全局变量的功能,而不需要无限期保存数据。例如,可能希望使用全局对某些不需要存储到磁盘的数据进行排序。对于这些操作,InterSystems IRIS提供了临时全局机制。该机制的工作方式如下:

  • 对于应用程序名称空间,可以定义一个全局映射,以便将具有特定命名约定的全局变量映射到IRISTEMP数据库,该数据库是一个特殊的数据库,如下所述。

例如,可以定义一个全局映射,以便将名称为^AcmeTemp*的所有全局变量映射到IRISTEMP数据库。

  • 当代码需要临时存储数据并再次读取它时,代码将向使用该命名约定的全局变量写入数据,并从全局变量读取数据。

例如,要保存值,代码可能会执行以下操作:

 set ^AcmeTempOrderApp("sortedarray")=some value

然后,稍后代码可能会执行以下操作:

 set somevariable = ^AcmeTempOrderApp("sortedarray")

通过使用临时全局变量,可以利用IRISTEMP数据库没有日志记录这一事实。因为数据库没有日记记录,所以使用该数据库的操作不会产生日记文件。日志文件可能会变得很大,并可能导致空间问题。但是,请注意以下几点:

不能回滚修改IRISTEMP数据库中的全局变量的任何事务;此行为特定于IRISTEMP。如果需要通过事务管理临时工作,请不要使用IRISTEMP中的全局变量来实现此目的。

请注意,仅对不需要保存的工作使用IRISTEMP

定义临时全局变量的映射

要定义临时全局变量的映射,请执行以下操作:

  1. 选择一个命名约定,并确保所有开发人员都知道这一点。请注意以下几点:
  • 考虑是要有多个临时全局变量还是要少一些具有多个节点的临时全局变量。与读取或写入相同数量的独立全局变量相比,InterSystems IRIS更容易高效地读取或写入同一全局变量中的不同节点。这种效率差异对于少数全局变量来说可以忽略不计,但当有数百个独立的全局变量时,效率差异就非常明显。

  • 如果计划在多个名称空间中使用相同的全局映射,那么设计一个系统,使一个名称空间中的工作不会干扰另一个名称空间中的工作。例如,可以使用命名空间名称作为全局变量中的下标。

  • 类似地,即使在一个命名空间内,也要设计一个系统,使代码的每个部分在同一全局中使用不同的全局或不同的下标,以避免干扰。

  • 请勿使用系统保留的全局名称。

  1. 在管理门户中,导航到命名空间页面(System Administration > Configuration > System Configuration > Namespaces)。
  2. 在应用程序命名空间所在的行中,单击Global Mappings。
  3. 在全局映射页面中,单击新建全局映射。
  4. 对于全局数据库位置,选择IRISTEMP
  5. 对于全局名称,输入以星号(*)结尾的名称。不要包括名称的第一个插入符号。

例如: AcmeTemp*

此映射会导致名称以AcmeTemp*开头的所有全局变量映射到IRISTEMP数据库。

  1. 单击OK。

注意:>> 显示在新映射行的第一列中,表示已打开映射进行编辑。

  1. 要保存映射以便InterSystems IRIS使用它们,请单击保存更改。

image

image

image

image

IRISTEMP的系统使用

请注意,InterSystems使用临时全局变量作为临时空间,例如,在执行某些查询(用于排序、分组、计算聚合等)期间用作临时索引。

IRISTEMP中包含的一些系统全局变量包括:

  • ^IRIS.Temp*
  • ^mtemp*

永远不要更改这些全局变量中的任何一个。

0
0 141
文章 姚 鑫 · 五月 13, 2021 4m read

第五章 管理全局变量(二)

在全局变量中查找值

“查找全局变量字符串”页使可以在下标或选定全局变量的值中查找给定的字符串。

要访问和使用此页,请执行以下操作:

  1. 显示“全局变量”页。
  2. 选择要使用的全局变量。为此,请参阅“全局页简介”一节中的步骤2和3。
  3. 单击查找按钮。
  4. 对于查找内容,输入要搜索的字符串。
  5. (可选)清除大小写匹配。默认情况下,搜索区分大小写。
  6. 单击Find First或Find All。

然后,页面显示选定全局变量中下标或值包含给定字符串的第一个节点或所有节点。该表左侧显示了节点下标,右侧显示了相应的值。

  1. 如果使用的是Find First,请根据需要单击Find Next以查看下一个节点。
  2. 完成后,单击关闭窗口。

执行批量更换

注意:在进行任何编辑之前,请确保知道IRIS使用哪个全局系统,以及应用程序使用哪个全局系统;参见“一般建议”此选项会永久更改数据。不建议在生产系统中使用。

出于开发目的,“查找全局字符串”页面还提供了对全局节点中的值进行整体更改的选项。要使用此选项:

  1. 显示“全局”页面。
  2. 选择要使用的全局。为此,请参见“全球页面简介”一节中的步骤2和3
  3. 单击替换按钮。
  4. 使用此页面查找上一节中描述的值。
  5. 为“替换为”指定一个值。
  6. 单击全部替换。
  7. 单击确定确认此操作。然后,页面会显示变更的预览。
  8. 如果结果可以接受,请单击保存。
  9. 单击确定确认此操作。

导出全局变量

注意:因为导入全局是非常容易的(这是一个不可逆转的变化),所以最好只导出需要导入的全局。请注意,如果导出所有全局变量,导出将包括所有包含代码的全局变量。请确保知道IRIS使用哪些全局系统,以及应用程序使用哪些全局系统;

“导出全局”页面允许导出全局。

要访问和使用此页面:

  1. 显示“全局”页面。
  2. 指定要使用的全局。为此,请参见“全球页面简介”一节中的步骤2和3
  3. 单击导出按钮。
  4. 指定要将全局文件导出到的文件。为此,请在输入服务器<主机名>上的导出路径和名称字段中输入文件名(包括其绝对或相对路径名),或者单击浏览并导航到该文件。
  5. 使用字符集列表选择导出文件的字符集。
  6. 在页面的中央框中:选择输出格式,选择记录格式
  7. 选择或清除“在此检查”以在后台运行导出...
  8. 单击导出。
  9. 如果文件已经存在,请单击“确定”用新版本覆盖它。

导出会创建一个. gof文件。

导入全局变量

注意:在导入任何全局变量之前,请确保知道IRIS使用哪些全局变量,以及应用程序使用哪些全局变量;参见“一般建议”没有撤消选项。将全局导入现有全局(从而合并数据)后,无法将全局恢复到其以前的状态。

“导入全局”页面允许导入全局。要访问和使用此页面:

  1. 显示“全局”页面。
  2. 单击导入按钮。
  3. 指定导入文件。为此,请在输入导入文件的路径和名称字段中输入文件(包括其绝对或相对路径名),或者单击浏览并导航到该文件。
  4. 使用字符集列表选择导入文件的字符集。
  5. 选择下一步。
  6. 使用表中的复选框选择要导入的全局。
  7. 也可以选择在后台运行导入。如果选择此项,任务将在后台运行。
  8. 单击导入。

删除全局变量

注意:在删除任何全局变量之前,请确保知道IRIS使用哪些全局变量,以及应用程序使用哪些全局变量;参见“一般建议”没有撤消选项。无法恢复已删除的全局。

“删除全局”页面允许删除全局。要访问和使用此页面:

  1. 显示“全局”页面。
  2. 选择要使用的全局。为此,请参见“全球页面简介”一节中的步骤2和3
  3. 单击删除按钮。
  4. 单击确定确认此操作。

管理任务的应用程序接口

InterSystems IRIS还提供了以下应用编程接口来执行本章中描述的一些任务:

  • %SYSTEM.OBJ提供了以下方法:
    • Export()使能够将全局导出到一个XML文件。
    • Load()LoadDir()使能够导入包含在XML文件中的全局。

这两者都可以通过$SYSTEM变量获得,例如:$SYSTEM.OBJ.Export

  • 类别%Library.Global提供了以下方法:
    • Export()使能够将全局导出到.gof和其他文件格式(不包括XML)。
    • Import()使能够将全局导入到.gof和其他文件格式(不包括XML)。

%Library.Global 还提供了Get()类查询,根据给定的搜索条件,可以使用该查询来查找全局。

image

0
0 108
文章 姚 鑫 · 五月 13, 2021 4m read

第五章 管理全局变量(二)

在全局变量中查找值

“查找全局变量字符串”页使可以在下标或选定全局变量的值中查找给定的字符串。

要访问和使用此页,请执行以下操作:

  1. 显示“全局变量”页。
  2. 选择要使用的全局变量。为此,请参阅“全局页简介”一节中的步骤2和3。
  3. 单击查找按钮。
  4. 对于查找内容,输入要搜索的字符串。
  5. (可选)清除大小写匹配。默认情况下,搜索区分大小写。
  6. 单击Find First或Find All。

然后,页面显示选定全局变量中下标或值包含给定字符串的第一个节点或所有节点。该表左侧显示了节点下标,右侧显示了相应的值。

  1. 如果使用的是Find First,请根据需要单击Find Next以查看下一个节点。
  2. 完成后,单击关闭窗口。

执行批量更换

注意:在进行任何编辑之前,请确保知道IRIS使用哪个全局系统,以及应用程序使用哪个全局系统;参见“一般建议”此选项会永久更改数据。不建议在生产系统中使用。

出于开发目的,“查找全局字符串”页面还提供了对全局节点中的值进行整体更改的选项。要使用此选项:

  1. 显示“全局”页面。
  2. 选择要使用的全局。为此,请参见“全球页面简介”一节中的步骤2和3
  3. 单击替换按钮。
  4. 使用此页面查找上一节中描述的值。
  5. 为“替换为”指定一个值。
  6. 单击全部替换。
  7. 单击确定确认此操作。然后,页面会显示变更的预览。
  8. 如果结果可以接受,请单击保存。
  9. 单击确定确认此操作。

导出全局变量

注意:因为导入全局是非常容易的(这是一个不可逆转的变化),所以最好只导出需要导入的全局。请注意,如果导出所有全局变量,导出将包括所有包含代码的全局变量。请确保知道IRIS使用哪些全局系统,以及应用程序使用哪些全局系统;

“导出全局”页面允许导出全局。

要访问和使用此页面:

  1. 显示“全局”页面。
  2. 指定要使用的全局。为此,请参见“全球页面简介”一节中的步骤2和3
  3. 单击导出按钮。
  4. 指定要将全局文件导出到的文件。为此,请在输入服务器<主机名>上的导出路径和名称字段中输入文件名(包括其绝对或相对路径名),或者单击浏览并导航到该文件。
  5. 使用字符集列表选择导出文件的字符集。
  6. 在页面的中央框中:选择输出格式,选择记录格式
  7. 选择或清除“在此检查”以在后台运行导出...
  8. 单击导出。
  9. 如果文件已经存在,请单击“确定”用新版本覆盖它。

导出会创建一个. gof文件。

导入全局变量

注意:在导入任何全局变量之前,请确保知道IRIS使用哪些全局变量,以及应用程序使用哪些全局变量;参见“一般建议”没有撤消选项。将全局导入现有全局(从而合并数据)后,无法将全局恢复到其以前的状态。

“导入全局”页面允许导入全局。要访问和使用此页面:

  1. 显示“全局”页面。
  2. 单击导入按钮。
  3. 指定导入文件。为此,请在输入导入文件的路径和名称字段中输入文件(包括其绝对或相对路径名),或者单击浏览并导航到该文件。
  4. 使用字符集列表选择导入文件的字符集。
  5. 选择下一步。
  6. 使用表中的复选框选择要导入的全局。
  7. 也可以选择在后台运行导入。如果选择此项,任务将在后台运行。
  8. 单击导入。

删除全局变量

注意:在删除任何全局变量之前,请确保知道IRIS使用哪些全局变量,以及应用程序使用哪些全局变量;参见“一般建议”没有撤消选项。无法恢复已删除的全局。

“删除全局”页面允许删除全局。要访问和使用此页面:

  1. 显示“全局”页面。
  2. 选择要使用的全局。为此,请参见“全球页面简介”一节中的步骤2和3
  3. 单击删除按钮。
  4. 单击确定确认此操作。

管理任务的应用程序接口

InterSystems IRIS还提供了以下应用编程接口来执行本章中描述的一些任务:

  • %SYSTEM.OBJ提供了以下方法:
    • Export()使能够将全局导出到一个XML文件。
    • Load()LoadDir()使能够导入包含在XML文件中的全局。

这两者都可以通过$SYSTEM变量获得,例如:$SYSTEM.OBJ.Export

  • 类别%Library.Global提供了以下方法:
    • Export()使能够将全局导出到.gof和其他文件格式(不包括XML)。
    • Import()使能够将全局导入到.gof和其他文件格式(不包括XML)。

%Library.Global 还提供了Get()类查询,根据给定的搜索条件,可以使用该查询来查找全局。

image

0
0 99
文章 Michael Lei · 五月 12, 2021 16m read

本贴提供了在 VMware ESXi 5.5 及更高版本的环境中部署 Caché 2015 及更高版本时,关于配置、系统规模调整和容量规划等方面的指南。

我假定您已经了解 VMware vSphere 虚拟化平台,所以直接给出推荐。 本指南中的推荐不特定于任何具体硬件或站点特定的实现,也不应作为规划和配置 vSphere 部署的全面指南,而是一份您可以做出选择的最佳实践配置清单。 我希望您的 VMware 专家实施团队能针对具体站点对这些推荐进行评估。


这里是 InterSystems 数据平台和性能系列的其他帖子的列表。

_注:_本帖更新于 2017 年 1 月 3 日,强调必须为生产数据库实例设置虚拟机内存预留,以保证 Caché 有足够内存可用,并且不会出现内存交换或膨胀而对数据库性能产生负面影响。 更多详细信息,请参见下面的内存部分。

0
0 386
文章 Michael Lei · 五月 12, 2021 17m read

在本帖中,我将展示使用_外部备份_来备份 Caché 的策略,以及与基于快照的解决方案集成的示例。 如今,大多数解决方案部署在基于 VMware 的 Linux 上,因此许多帖子都以展示解决方案如何集成 VMware 快照技术为例。

Caché 备份 - 包括电池?

Caché 安装后即包含 Caché 在线备份,可提供不间断的 Caché 数据库备份。 但随着系统规模的扩大,您应该考虑更高效的备份解决方案。 集成了快照技术的_外部备份_是推荐的系统(包括 Caché 数据库)备份解决方案。

外部备份有特殊注意事项吗?

外部备份的在线文档包含了全部详细信息。 一个关键考虑事项是:

“为确保快照的完整性,Caché 提供了在创建快照时冻结数据库写操作的方法。 在创建快照期间,只冻结对数据库文件的物理写入,从而允许用户进程继续在内存中不间断地执行更新。”

还需要注意的是,虚拟化系统上的部分快照过程会导致正在备份的虚拟机短暂暂停,这段时间通常称为关闭时间。 该时间通常不到一秒,因此不会被用户注意到,也不会影响系统运行,但在某些情况下,关闭时间可能较长。 如果关闭时间长于 Caché 数据库镜像的 QoS 超时时间,那么备份节点将认为主节点出现故障,并将进行故障转移。 在本帖的后面部分,我将说明在需要对镜像 QoS 超时时间进行更改时如何查看关闭时间。

0
0 754
文章 Michael Lei · 五月 12, 2021 7m read

部分 在上个帖子中,我们安排了使用 pButtons 进行 24 小时的性能指标收集。 在本帖中,我们将研究几个收集到的关键指标,以及它们与底层系统硬件的关系。 我们还将开始探索 Caché(或任一 InterSystems 数据平台)指标与系统指标之间的关系。 以及如何使用这些指标来了解系统的每日节拍率并诊断性能问题。

0
1 250
文章 Michael Lei · 五月 12, 2021 13m read

本周,我将关注 CPU - 主要硬件食物群之一 :) 一位客户请我就以下情况提供建议:他们的生产服务器已接近使用寿命终止,是时候更新硬件了。 他们还考虑通过虚拟化来整合服务器,并希望适当调整裸机或虚拟机的容量规模。 今天我们将关注 CPU,在后面的帖子中,我将介绍适当调整其他主要食物群(内存和 IO)规模的方法。

所以问题是:

  • 如何将五年多以前对处理器的应用要求转换成针对当今的处理器?
  • 目前的处理器有哪些是合适的?
  • 虚拟化如何影响 CPU 容量计划?
0
0 256
文章 Michael Lei · 五月 12, 2021 15m read

本帖将展示为 InterSystems 数据平台上运行的数据库应用调整共享内存需求(包括 global 和例程缓冲区、gmheap 以及 locksize)的方法,以及在配置服务器和虚拟化 Caché 应用程序时应考虑的一些性能提示。 和以往一样,当我谈到 Caché 时,我指的是所有数据平台(Ensemble、HealthShare、iKnow 和 Caché)。


[本系列其他帖子的列表](https://cn.community.intersystems.com/post/intersystems-数据平台的容量规划和性能系列文章)

当我最初开始使用 Caché 时,大多数客户的操作系统是 32 位的,Caché 应用程序的内存有限且昂贵。 通常部署的英特尔服务器只有几个核心,唯一的扩展方式是选择大型服务器,或者使用 ECP 横向扩展。 现在,即使是基本的生产级服务器也具有多个处理器、几十个核心,并且最小内存为 128 或 256 GB,可能达到 TB。 对于大多数数据库安装,ECP 已被遗忘,我们现在可以在单台服务器上大幅提高应用事务处理速率。

0
1 280
文章 Michael Lei · 五月 10, 2021 10m read

关键字:深度学习,Grad-CAM,X 射线,Covid-19,HealthShare,IRIS

目的

在复活节周末,我谈到了一些针对 Covid-19 肺的深度学习分类器。  演示结果还算不错,似乎与当时有关该主题的一些学术研究刊物相吻合。 但它真的足够“好”吗? 

最近,我偶然收听了一个关于“机器学习中的可解释性”的在线午餐网络讲座,Don 在演讲的最后谈到了这个分类结果:

上图也出现在 “Why Should I Trust You?” Explaining the Predictions of Any Classifier 这篇研究论文中。   我们可以看到,分类器实际上经过训练,以背景像素(如雪等野生环境)作为主要输入,对宠物狗和野狼进行分类。 

这关乎我过去的兴趣,现在也激起一些好奇:

  • 我们如何“观察”这些通常以“黑盒”形式表示的 Covid-19 分类器,了解哪些像素实际上促成了“Covid-19 肺”结果?
  • 在这种情况下,我们可以利用的最简单的形式或工具是什么? 

这也是篇简单的 10 分钟笔记。 最后,我会谈到为什么它也与我们即将推出的全新 IRIS 和 HealthShare 功能有关。

范围

幸运的是,过去几年中,各种 CNN 衍生分类器都有了方便的工具:

我们将使用 Grad-CAM 对我们上一篇帖子中的 Covid-19 肺分类器进行快速演示。

"Tensorflow 2.2.0rc + Jupyter" Docker 在配备 Nvidia T4 GPU 的 AWS Ubuntu 16.04 服务器上使用。   TensorFlow 2 提供了简单的梯度带实现。 

这是我在 Ubuntu 服务器上启动的快速笔记:  

docker run -itd --runtime=nvidia  -v /zhong/tf/:/tf  -p 8896:8888 -p 6026:6006 --name tf-gpu2 tensorflow/tensorflow:2.2.0rc2-gpu-py3-jupyter

方法

您可以放心地在此处忽略以上 Grad-CAM 研究出版物中引用的数字。

这里引用这些数字只是为了对后面使用的 Python 代码进行连续的原始提案(第 4 页和第 5 页)交叉检查,也希望能提供更好的结果透明度。

                                                             

(1):为了得到任意类 c 的宽度 u 和高度 v 的类判别定位图,我们首先计算类 c 的得分相对于卷积层的特征图 Ak 的梯度 yc(softmax 前)。 这些回流的梯度被全局平均池化,得到目标类的神经元重要性权重 ak。

(2):计算出目标类 c 的 ak 后,我们对激活图进行加权组合,然后执行 ReLU。  这就得到了与卷积特征图大小相同的粗略热图。  

测试

现在,我们来尝试一下目前能找到的最简单的编码:

1. 导入软件包

import tensorflow as tf;
print(tf.__version__)

2.2.0-rc2

import tensorflow as tf
import tensorflow.keras.backend as K
from tensorflow.keras.applications.inception_v3 import InceptionV3
from tensorflow.keras.preprocessing import image
from tensorflow.keras.applications.inception_v3 import preprocess_input, decode_predictions
import numpy as np
import os
import imutils
import matplotlib.pyplot as plt
import cv2

2. 加载我们之前训练和保存的模型

new_model = tf.keras.models.load_model('saved_model/inceptionV3')&lt;br>new_model.summary()

可以看到,在最终的全局平均池化之前,模型中 4D 的最后一个 CNN 层被称为“mixed10”。 

3. 计算 Grad-CAM 热图  

下面是一个实现了上述 Grad-CAM 公式 (1) 和 (2) 的简单版本热图。  这篇帖子对其做出了解释。 

with tf.GradientTape() as tape:&lt;br>  last_conv_layer = model.get_layer('mixed10') &lt;br>  iterate = tf.keras.models.Model([model.inputs], [model.output, last_conv_layer.output])&lt;br>  model_out, last_conv_layer = iterate(testX)&lt;br>  class_out = model_out[:, np.argmax(model_out[0])]&lt;br>  grads = tape.gradient(class_out, last_conv_layer)&lt;br>  pooled_grads = K.mean(grads, axis=(0, 1, 2))
  heatmap = tf.reduce_mean(tf.multiply(pooled_grads, last_conv_layer), axis=-1)

 在我们的示例中,它将生成一个热图 NumPy 数组 (27, 6, 6)。 然后,我们可以将它重新调整为原始 X 射线图像尺寸并叠加在 X 射线图像上方。

不过,在这种情况下,我们将使用略详细的版本,这篇帖子对此也有很好的解释。 它组成了一个函数,Grad-CAM 热图已调整为原始 X 射线图的大小:

# import the necessary packages&lt;br>from tensorflow.keras.models import Model&lt;br>import tensorflow as tf&lt;br>import numpy as np&lt;br>import cv2

class GradCAM:&lt;br>    def __init__(self, model, classIdx, layerName=None):&lt;br>        self.model = model&lt;br>        self.classIdx = classIdx&lt;br>        self.layerName = layerName&lt;br>        if self.layerName is None:&lt;br>            self.layerName = self.find_target_layer()

    def find_target_layer(self):&lt;br>        for layer in reversed(self.model.layers):&lt;br>            # check to see if the layer has a 4D output&lt;br>            if len(layer.output_shape) == 4:&lt;br>                return layer.name
        raise ValueError("Could not find 4D layer. Cannot apply GradCAM.")

    def compute_heatmap(self, image, eps=1e-8):&lt;br>        gradModel = Model(&lt;br>            inputs=[self.model.inputs],&lt;br>            outputs=[self.model.get_layer(self.layerName).output,&lt;br>                self.model.output])
        # record operations for automatic differentiation&lt;br>&lt;strong>        with tf.GradientTape() as tape:&lt;br>            inputs = tf.cast(image, tf.float32)&lt;br>            (convOutputs, predictions) = gradModel(inputs)&lt;br>            loss = predictions[:, self.classIdx]&lt;/strong>
        # use automatic differentiation to compute the gradients&lt;br>        grads = tape.gradient(loss, convOutputs)
        # compute the guided gradients&lt;br>        castConvOutputs = tf.cast(convOutputs > 0, "float32")&lt;br>        castGrads = tf.cast(grads > 0, "float32")&lt;br>        guidedGrads = castConvOutputs * castGrads * grads
        convOutputs = convOutputs[0]&lt;br>        guidedGrads = guidedGrads[0]
        weights = tf.reduce_mean(guidedGrads, axis=(0, 1))&lt;br>        cam = tf.reduce_sum(tf.multiply(weights, convOutputs), axis=-1)

        # resize the heatmap to oringnal X-Ray image size&lt;br>        (w, h) = (image.shape[2], image.shape[1])&lt;br>        heatmap = cv2.resize(cam.numpy(), (w, h))

        # normalize the heatmap&lt;br>        numer = heatmap - np.min(heatmap)&lt;br>        denom = (heatmap.max() - heatmap.min()) + eps&lt;br>        heatmap = numer / denom&lt;br>        heatmap = (heatmap * 255).astype("uint8")

        # return the resulting heatmap to the calling function&lt;br>        return heatmap

4. 加载 Covid-19 肺部 X 射线图

现在,加载一个从未在模型训练和验证过程中使用过的测试 X 射线图。 (也已上传到上一篇帖子中)

filename = './test/nejmoa2001191_f1-PA.jpeg'
orignal = cv2.imread(filename)
plt.imshow(orignal)
plt.show()

调整为 256 x 256,归一化为像素值在 0.0 到 1.0 之间的 numpy 数组“dataXG”。

orig = cv2.cvtColor(orignal, cv2.COLOR_BGR2RGB)&lt;br>resized = cv2.resize(orig, (256, 256))&lt;br>dataXG = np.array(resized) / 255.0&lt;br>dataXG = np.expand_dims(dataXG, axis=0)

**5. 进行快速分类 **

 现在可以调用上面新加载的模型进行快速预测:

preds = new_model.predict(dataXG)&lt;br>i = np.argmax(preds[0])&lt;br>print(i, preds)

0     [[0.9171522  0.06534185 0.01750595]]

因此它被归类为 0 型 - Covid-19 肺,概率为 0.9171522。

6. 计算 Grad-CAM 热图

# Compute the heatmap based on step 3&lt;br>cam = GradCAM(model=new_model, classIdx=i, layerName='mixed10') # find the last 4d shape "mixed10" in this case&lt;br>heatmap = cam.compute_heatmap(dataXG)

#show the calculated heatmap
plt.imshow(heatmap)
plt.show()

7. 在原始 X 射线图上显示热图

# Old fashioned way to overlay a transparent heatmap onto original image, the same as above&lt;br>heatmapY = cv2.resize(heatmap, (orig.shape[1], orig.shape[0]))&lt;br>heatmapY = cv2.applyColorMap(heatmapY, cv2.COLORMAP_HOT)  # COLORMAP_JET, COLORMAP_VIRIDIS, COLORMAP_HOT&lt;br>imageY = cv2.addWeighted(heatmapY, 0.5, orignal, 1.0, 0)&lt;br>print(heatmapY.shape, orig.shape)

# draw the orignal x-ray, the heatmap, and the overlay together&lt;br>output = np.hstack([orig, heatmapY, imageY])&lt;br>fig, ax = plt.subplots(figsize=(20, 18))&lt;br>ax.imshow(np.random.rand(1, 99), interpolation='nearest')&lt;br>plt.imshow(output)&lt;br>plt.show()

(842, 1090, 3) (842, 1090, 3)

这似乎表明我们的 Covid-19 演示分类器“相信”患者的“右侧气管旁带”周围出现了一些“浑浊”问题? 我不是很明白,这要请教真正的放射科医生。

那么,接下来再尝试一些从现实世界案例提交到 GitHub 仓库中的测试图像:

filename = './test/1-s2.0-S0929664620300449-gr2_lrg-b.jpg'

0    [[9.9799889e-01 3.8319459e-04 1.6178709e-03]]

这似乎也是合理的 Covid-19 解释,表明问题更多地发生在左心线区域?

再试试另一个随机测试 X 射线图:

filename = '../Covid_M/all/test/covid/radiol.2020200490.fig3.jpeg'

0      [[0.9317619  0.0169084  0.05132957]]

没想到这并不完全正确,但看起来好像也不算太离谱?  它显示了两个问题区域,左侧为主要问题,右侧为部分问题,这与放射科医师的标记应该是有些对应的? (希望它不是在人类标记上训练 - 这是可解释性问题的另一个层面)。

我要在这里打住了,我猜不会有太多人对 X 射线感兴趣。

 

原因

我个人对“可解释性”和“可理解性”以及相关技术方法的重要性有着深切的体会。  在此领域的任何尝试都是值得的,无论它有多么微不足道。 最终,“数据公平”、“数据公正”和“数据信任”将建立在其数字经济过程透明化的基础之上。  另外,它现在开始能为人所用了。     25 年前,当年轻的我在 1995 年的夏天忙着写博士论文的时候,我甚至不指望对被广泛用作黑盒的所谓“神经网络”有任何了解。 当时的 AI 更像是逻辑推理机“专家系统”,“神经网络”只是被称为“神经网络”,而“深度学习”尚未诞生。 现在,越来越多的研究和工具不断涌现,可供 AI 开发者轻松使用。 

最后,具体到这个演示,我很欣赏这种工具的一点是,它甚至不需要以像素级的标记作为起点,而是试图自动生成肺部病变区域,实现一种类似半自动标记的效果。 这在实际应用中是有意义的。   我记得去年,我的一位放射科医生朋友为了帮助我获取一些骨折数据,一遍又一遍地生成 U-Net 训练的像素标签,这很伤眼睛。 

未来计划

回到正题。 得益于过去 10 多年深度学习的快速发展,医学影像已成为 AI 领域比较成熟的方向。 这是值得我们深入研究的。 不过,接下来如果有时间,我希望我们能在 NLP 方面多做一些尝试。   

致谢

所有来源均已根据需要插入上述文本。 如有其他需要,我还将提供更多引用。  

免责声明: 

再次说明,我现在写这篇快速笔记是为了防止相关信息随时间遗失。 本文完全是出自“开发者”角度的个人观点。 内容和文本随时可能会被更改或完善。 以上内容主要是展示技术思想和方法,而不是临床解释。临床解释需要放射科专家根据大量优质数据建立黄金规则。

0
0 250
文章 姚 鑫 · 五月 10, 2021 5m read

第四章 多维存储的SQL和对象使用(二)

索引

持久化类可以定义一个或多个索引;其他数据结构用于提高操作(如排序或条件搜索)的效率。InterSystems SQL在执行查询时使用这些索引。InterSystems IRIS对象和SQL在执行INSERTUPDATEDELETE操作时自动维护索引内的正确值。

标准索引的存储结构

标准索引将一个或多个属性值的有序集与包含属性的对象的对象ID值相关联。

例如,假设我们定义了一个简单的持久化MyApp.Person类,该类具有两个文本属性和一个关于其Name属性的索引:

Class MyApp.Person Extends %Persistent
{
Index NameIdx On Name;

Property Name As %String;
Property Age As %Integer;
}

如果我们创建并保存此Person类的多个实例,则生成的数据和索引全局变量类似于:

 // data global
 ^MyApp.PersonD = 3  // counter node
 ^MyApp.PersonD(1) = $LB("",34,"Jones")
 ^MyApp.PersonD(2) = $LB("",22,"Smith")
 ^MyApp.PersonD(3) = $LB("",45,"Jones")


 // index global
 ^MyApp.PersonI("NameIdx"," JONES",1) = ""
 ^MyApp.PersonI("NameIdx"," JONES",3) = ""
 ^MyApp.PersonI("NameIdx"," SMITH",2) = ""

请注意有关全局索引的以下事项:

  1. 默认情况下,它被放在一个全局变量中,全局变量的名称是后面附加“i”(表示索引)的类名。
  2. 默认情况下,第一个下标是索引名;这允许将多个索引存储在同一全局中,而不会发生冲突。
  3. 第二个下标包含整理后的数据值。在这种情况下,使用默认的SQLUPPER排序函数对数据进行排序。这会将所有字符转换为大写(不考虑大小写进行排序),并在前面加上一个空格字符(强制所有数据作为字符串进行排序)。
  4. 第三个下标包含包含索引数据值的对象的对象ID值。
  5. 节点本身是空的;所有需要的数据都保存在下标中。请注意,如果索引定义指定数据应与索引一起存储,则将其放置在全局索引的节点中。

该索引包含足够的信息来满足许多查询,比如按姓名列出所有Person类。

位图索引

位图索引类似于标准索引,不同之处在于它使用一系列位字符串来存储与索引值对应的一组对象ID值。

位图索引的逻辑运算

位字符串是一个包含一组特殊压缩格式的位(01值)的字符串。 InterSystems IRIS包含一组有效创建和使用位字符串的函数。 这些都列在下表中:

位操作

函数描述
$Bit在位串中设置或获取位。
$BitCount计算位串中的位数。
$BitFind查找位串中下一个出现的位。
$BitLogic对两个或多个位串执行逻辑(AND, OR)操作。

在位图索引中,位字符串中的顺序位置对应于索引表中的行(对象ID号)。 对于给定值,位图索引维护一个位字符串,在给定值存在的每一行中包含1,在没有给定值的每一行中包含0。 请注意,位图索引只适用于使用系统分配的默认存储结构的对象,数值型对象ID值。

例如,假设我们有一个类似如下的表:

IDStateProduct
1MAHat
2NYHat
3NYChair
4MAChair
5MAHat

如果StateProduct列有位图索引,则它们包含以下值:

State列上的位图索引包含以下位字符串值:


MA	1	0	0	1	1
NY	0	1	1	0	0

注意,对于值“MA”,在与State等于“MA”的表行对应的位置(1、4和5)中有一个1。

类似地,Product列上的位图索引包含以下位字符串值(注意,这些值在索引中被排序为大写):


CHAIR	0	0	1	1	0
HAT	1	1	0	0	1

InterSystems SQL Engine可以通过对这些索引维护的位串进行迭代、计算位内位数或执行逻辑组合(AND, or)来执行许多操作。 例如,要找到State等于“MA”Product等于“HAT”的所有行,SQL引擎可以简单地将适当的位串与逻辑and组合在一起。

除了这些索引之外,系统还维护一个额外的索引,称为“区段索引”,对于存在的每一行包含1,对于不存在的行(如已删除的行)包含0。 这用于某些操作,如否定。

位图索引的存储结构

位图索引将一个或多个属性值的有序集合与一个或多个包含与属性值对应的对象ID值的位字符串相关联。

例如,假设我们定义了一个简单的持久MyAppPerson类具有两个文字属性和Age属性上的位图索引:

Class MyApp.Person Extends %Persistent
{
Index AgeIdx On Age [Type = bitmap];

Property Name As %String;
Property Age As %Integer;
}

如果我们创建并保存这个Person类的几个实例,得到的数据和索引全局变量类似于:

 // data global
 ^MyApp.PersonD = 3  // counter node
 ^MyApp.PersonD(1) = $LB("",34,"Jones")
 ^MyApp.PersonD(2) = $LB("",34,"Smith")
 ^MyApp.PersonD(3) = $LB("",45,"Jones")

 // index global
 ^MyApp.PersonI("AgeIdx",34,1) = 110...
 ^MyApp.PersonI("AgeIdx",45,1) = 001...

 // extent index global
 ^MyApp.PersonI("$Person",1) = 111...
 ^MyApp.PersonI("$Person",2) = 111...

关于全局索引,请注意以下几点:

  1. 默认情况下,它被放置在一个全局变量中,全局变量的名称是类名,后面附加一个“I”(表示Index)。
  2. 默认情况下,第一个下标是索引名;这允许多个索引存储在同一个全局中,而不会发生冲突。
  3. 第二个下标包含经过整理的数据值。在这种情况下,不应用排序函数,因为这是数字数据的索引。
  4. 第三个下标包含块编号;为了提高效率,位图索引被分成一系列位串,每个位串包含表中大约64000行的信息。这些位串中的每一个都被称为块。
  5. 节点包含位串。

另请注意:因为该表有一个位图索引,所以会自动维护一个区索引。该盘区索引存储在索引GLOBAL中,并使用前缀有“$”字符的类名作为其第一个下标。

位图索引的直接访问

下面的示例使用类区索引来计算存储的对象实例(行)的总数。注意,它使用$ORDER来迭代区索引的块(每个块包含大约64000行的信息):

ClassMethod Count1() As %Integer
{
    New total,chunk,data
    Set total = 0
    
    Set chunk = $Order(^Sample.PersonI("$Person",""),1,data)
    While (chunk '= "") {
        Set total = total + $bitcount(data,1)
        Set chunk = $Order(^Sample.PersonI("$Person",chunk),1,data)
    }

    Quit total
}
DHC-APP>w ##class(PHA.TEST.SQL).Count1()
208
0
0 120
文章 姚 鑫 · 五月 9, 2021 6m read

第四章 多维存储的SQL和对象使用(一)

本章介绍InterSystems IRIS®对象和SQL引擎如何利用多维存储(全局变量)来存储持久对象、关系表和索引。

尽管InterSystems IRIS对象和SQL引擎会自动提供和管理数据存储结构,但了解其工作原理的详细信息还是很有用的。

数据的对象视图和关系视图使用的存储结构是相同的。为简单起见,本章仅从对象角度介绍存储。

数据

每个使用%Storage.Persistent存储类(默认)的持久化类都可以使用多维存储(全局变量)的一个或多个节点在InterSystems IRIS数据库中存储其自身的实例。

每个持久化类都有一个存储定义,用于定义其属性如何存储在全局变量节点中。这个存储定义(称为“默认结构”)由类编译器自动管理。

默认结构

用于存储持久对象的默认结构非常简单:

  • 数据存储在名称以完整类名(包括包名)开头的全局变量中。附加“D”以形成全局数据的名称,而附加“I”作为全局索引。
  • 每个实例的数据都存储在全局数据的单个节点中,所有非瞬态属性都放在$list结构中。
  • 数据全局变量中的每个节点都以对象ID值作为下标。默认情况下,对象ID值是通过调用存储在全局变量数据根(没有下标)的计数器节点上的$Increment函数提供的整数。

例如,假设我们定义了一个简单的持久化类MyApp.Person,它有两个文本属性:

Class MyApp.Person Extends %Persistent
{
Property Name As %String;
Property Age As %Integer;
}

如果我们创建并保存此类的两个实例,得到的全局变量结果将类似于:

 ^MyApp.PersonD = 2  // counter node
 ^MyApp.PersonD(1) = $LB("",530,"Abraham")
 ^MyApp.PersonD(2) = $LB("",680,"Philip")

注意,存储在每个节点中的$List结构的第一部分是空的; 这是为类名保留的。 如果定义Person类的子类,则此槽包含子类名。 当多个对象存储在同一个区段内时,%OpenId方法(由%Persistent类提供)使用此信息多态地打开正确的对象类型。 此槽在类存储定义中显示为名为“%%CLASSNAME”的属性。

IDKEY

IDKEY机制允许显式定义用作对象ID的值。为此,只需将IDKEY索引定义添加到类中,并指定将提供ID值的一个或多个属性。请注意,一旦保存对象,其对象ID值就不能更改。这意味着在保存使用IDKEY机制的对象后,不能再修改该对象ID所基于的任何特性。

Class MyApp.Person Extends %Persistent
{
Index IDKEY On Name [ Idkey ];

Property Name As %String;
Property Age As %Integer;
}

如果我们创建并保存Person类的两个实例,得到的全局变量结果现在类似于:

 ^MyApp.PersonD("Abraham") = $LB("",530,"Abraham")
 ^MyApp.PersonD("Philip") = $LB("",680,"Philip")

请注意,不再定义任何计数器节点。还要注意,通过将对象ID基于Name属性,我们已经暗示了Name的值对于每个对象必须是唯一的。

如果IDKEY索引基于多个属性,则主数据节点具有多个下标。例如:

Class MyApp.Person Extends %Persistent
{
Index IDKEY On (Name,Age) [ Idkey ];

Property Name As %String;
Property Age As %Integer;
}

在这种情况下,生成的全局变量现在类似于:

 ^MyApp.PersonD("Abraham",530) = $LB("",530,"Abraham")
 ^MyApp.PersonD("Philip",680) = $LB("",680,"Philip")

重要提示:IDKEY索引使用的任何属性的值中都不能有连续的一对竖线(||),除非该属性是对持久类实例的有效引用。 这种限制是由InterSystems SQL机制的工作方式强加的。 在IDKey属性中使用||会导致不可预知的行为。

Subclasses

默认情况下,持久性对象的子类引入的任何字段都存储在附加节点中。 子类的名称用作附加的下标值。

例如,假设我们定义了一个具有两个文本属性的简单持久MyApp.Person类:

Class MyApp.Person Extends %Persistent
{
Property Name As %String;

Property Age As %Integer;
}

现在,我们定义了一个持久子类MyApp.Students,它引入了两个额外的文本属性:

Class MyApp.Student Extends Person
{
Property Major As %String;

Property GPA As %Double;
}

如果我们创建并保存此MyApp.Student类的两个实例,得到的全局结果将类似于:

^MyApp.PersonD = 2  // counter node
^MyApp.PersonD(1) = $LB("Student",19,"Jack")
^MyApp.PersonD(1,"Student") = $LB(3.2,"Physics")

^MyApp.PersonD(2) = $LB("Student",20,"Jill")
^MyApp.PersonD(2,"Student") = $LB(3.8,"Chemistry")

Person类继承的属性存储在主节点中,而由Student类引入的属性存储在另一个子节点中。这种结构确保了学生数据可以作为人员数据互换使用。例如,列出所有Person对象名称的SQL查询正确地获取PersonStudent数据。当属性被添加到超类或子类时,这种结构还使类编译器更容易维护数据兼容性。

请注意,主节点的第一部分包含字符串“Student”-它标识包含学生数据的节点。

父子关系

在父子关系中,子对象的实例存储为它们所属的父对象的子节点。这种结构确保子实例数据与父数据在物理上是集群的。

/// An Invoice class
Class MyApp.Invoice Extends %Persistent
{
Property CustomerName As %String;

/// an Invoice has CHILDREN that are LineItems
Relationship Items As LineItem  [inverse = TheInvoice, cardinality = CHILDREN];
}

LineItem

/// A LineItem class
Class MyApp.LineItem Extends %Persistent
{
Property Product As %String;
Property Quantity As %Integer;

/// a LineItem has a PARENT that is an Invoice
Relationship TheInvoice As Invoice [inverse = Items, cardinality = PARENT];
}

如果我们存储多个Invoice对象的实例,每个实例都有关联的LineItem对象,则得到的全局变量结果将类似于:

^MyApp.InvoiceD = 2  // invoice counter node
^MyApp.InvoiceD(1) = $LB("","Wiley Coyote")
^MyApp.InvoiceD(1,"Items",1) = $LB("","Rocket Roller Skates",2)
^MyApp.InvoiceD(1,"Items",2) = $LB("","Acme Magnet",1)

^MyApp.InvoiceD(2) = $LB("","Road Runner")
^MyApp.InvoiceD(2,"Items",1) = $LB("","Birdseed",30)

嵌入对象

存储嵌入对象的方法是先将它们转换为序列化状态(默认情况下是包含对象属性的$List结构),然后以与任何其他属性相同的方式存储此串行状态。

例如,假设我们定义了一个具有两个文字属性的简单串行(可嵌入)类:

Class MyApp.MyAddress Extends %SerialObject
{
Property City As %String;
Property State As %String;
}

现在,我们修改前面的示例以添加嵌入的Home Address属性:

Class MyApp.MyClass Extends %Persistent
{
Property Name As %String;
Property Age As %Integer;
Property Home As MyAddress;
}

如果我们创建并保存此类的两个实例,则生成的全局变量相当于:

 ^MyApp.MyClassD = 2  // counter node
 ^MyApp.MyClassD(1) = $LB(530,"Abraham",$LB("UR","Mesopotamia"))
 ^MyApp.MyClassD(2) = $LB(680,"Philip",$LB("Bethsaida","Israel"))

通过将全局流的数据拆分成一系列块(每个块小于32K字节)并将这些块写入一系列顺序节点,全局流被存储在全局流中。文件流存储在外部文件中。

0
0 129
文章 姚 鑫 · 五月 8, 2021 7m read

第三章 使用多维存储(全局变量)(四)

管理事务

InterSystems IRIS提供了使用全局变量实现完整事务处理所需的基本操作。 InterSystems IRIS对象和SQL自动利用这些特性。 如果直接将事务性数据写入全局变量,则可以使用这些操作。

事务命令是TSTART,它定义事务的开始; TCOMMIT,它提交当前事务; 和TROLLBACK,它将中止当前事务,并撤消自事务开始以来对全局变量所做的任何更改。

例如,下面的ObjectScript代码定义了事务的开始,设置了一些全局变量节点,然后根据ok的值提交或回滚事务:


/// w ##class(PHA.TEST.Global).GlobalTro(0)
ClassMethod GlobalTro(ok)
{

	TSTART

	Set ^Data(1) = "Apple1"
	Set ^Data(2) = "Berry1"

	If (ok) {
		TCOMMIT
	}
	Else {
	 	TROLLBACK
	}
	zw ^Data
	q ""
}

TSTART在InterSystems IRIS日志文件中写入事务开始标记。 这定义了事务的起始边界。 在上面的示例中,如果变量oktrue(非零),则TCOMMIT命令标记事务成功结束,并将事务完成标记写入日志文件。 如果okfalse(0),那么TROLLBACK命令将撤消自事务开始以来进行的每一个setkill操作。 在这种情况下,^Data(1)^Data(2)被恢复到原来的值。

注意,在事务成功完成时,不会写入任何数据。 这是因为事务期间对数据库的所有修改都是在事务过程中正常执行的。 只有在回滚的情况下,数据库中的数据才会受到影响。 这意味着本例中的事务具有有限的隔离性; 也就是说,其他进程可以在事务提交之前看到修改后的全局值。 这通常被称为未提交的读取。 这是好是坏取决于应用程序的需求; 在许多情况下,这是完全合理的行为。 如果应用程序需要更高级别的隔离,则可以通过使用锁来实现。 这将在下一节中进行描述。

锁和事务

要创建隔离事务-也就是说,为了防止其他进程在提交事务之前看到修改的数据-需要使用锁。在ObjectScript中,可以通过lock命令直接获取和释放锁定。锁按照约定工作;对于给定的数据结构(如用于持久对象),所有需要锁的代码都使用相同的逻辑锁引用(即,锁命令使用相同的地址)。

在事务中,锁有一个特殊的行为; 在事务过程中获取的任何锁在事务结束之前都不会被释放。 要了解为什么会这样,请考虑典型事务执行的操作:

  1. 使用TSTART启动事务。
  2. 获取要修改的一个或多个节点上的锁。这通常被称为“写”锁。
  3. 修改一个或多个节点。
  4. 释放锁(或多个锁)。因为我们处于事务中,所以这些锁在此时实际上不会被释放。
  5. 使用TCOMMIT提交事务。此时,上一步中释放的所有锁实际上都已释放。

如果另一个进程想要查看此事务中涉及的节点,并且不想看到未提交的修改,则它只需在从节点读取数据之前测试锁(称为“读”锁)。因为写锁定一直保持到事务结束,所以在事务完成(提交或回滚)之前,读取进程看不到数据。

大多数数据库管理系统使用类似的机制来提供事务隔离。InterSystems IRIS的独特之处在于它让开发人员可以使用这种机制。这使得有可能为新的应用程序类型创建自定义数据库结构,同时仍然支持事务。当然,可以简单地使用InterSystems IRIS对象或SQL来管理数据,并让事务得到自动管理。

对TSTART的嵌套调用

InterSystems IRIS维护一个特殊的系统变量$TLEVEL,该变量跟踪TSTART命令被调用的次数。$TLEVEL从值0开始;每次调用TSTART时,$TLEVEL的值递增1,而每次调用TCOMMIT时,$TLEVEL的值递减1。如果调用TCOMMIT导致将$TLEVEL设置回0,则事务结束(以COMMIT结束)。

调用TROLLBACK命令总是终止当前事务,并将$TLEVEL设置回0,而不管$TLEVEL的值是多少。

此行为使应用程序能够将事务包装在本身包含事务的代码(如对象方法)周围。例如,持久对象提供的%Save方法始终将其操作作为事务执行。通过显式调用TSTARTTCOMMIT,可以创建包含几个对象保存操作的更大事务:

    TSTART
    Set sc = object1.%Save()
    If ($$$ISOK(sc)) {
        // 第一次保存有效,执行第二次保存
        Set sc = object2.%Save()
    }
    
    If ($$$ISERR(sc)) {
        // 其中一个保存失败,正在回滚
        TROLLBACK
    }
    Else {
        // 提交
        TCOMMIT
    }

管理并发性

设置或检索单个全局变量节点的操作是原子的;它可以保证始终成功并获得一致的结果。对于多个节点上的操作或控制事务隔离,InterSystems IRIS提供获取和释放锁的功能。

锁由IRIS锁管理器管理。在ObjectScript中,可以通过lock命令直接获取和释放锁定。(InterSystems IRIS对象和SQL根据需要自动获取和释放锁)。

检查最新的全局变量引用

最新的全局变量引用记录在ObjectScript $ZREFERENCE特殊变量中。$ZREFERENCE包含最新的全局引用,包括下标和扩展全局引用(如果指定)。请注意,$ZREFERENCE既不指示全局引用是否成功,也不指示指定的全局是否存在。InterSystems IRIS只记录最近指定的全局引用。

裸全球变量引用

在带下标的全局引用之后,InterSystems IRIS会将裸指示符设置为该全局名称和下标级别。然后,可以使用裸全局引用(省略全局名称和更高级别的下标)对相同的全局变量和下标级别进行后续引用。这简化了在相同(或更低)下标级别对相同全局变量的重复引用。

在裸引用中指定较低的下标级别会将裸指示符重置为该下标级别。因此,在使用裸全局变量引用时,始终使用由最新全局引用建立的下标级别。

裸指示符值记录在$ZREFERENCE特殊变量中。裸露指示符被初始化为空字符串。在未设置裸指示器的情况下尝试裸全局引用会导致<NAKED> 错误。更改命名空间会重新初始化裸体指示符。可以通过将$ZREFERENCE设置为空字符串(“”)来重新初始化裸指示符。

在下面的示例中,第一个引用中指定了带下标的GLOBAL ^Produce(“fruit”,1)。InterSystems IRIS将此全局变量名称和下标保存在裸体指示符中,以便后续的裸体全局引用可以省略全局名称“Production”和更高下标级别的“Fruit”。当^(3,1)裸引用达到更低的下标级别时,此新的下标级别将成为任何后续裸全局变引用的假设。

/// w ##class(PHA.TEST.Global).GlobalNake()
ClassMethod GlobalNake()
{
	SET ^Produce("fruit",1)="Apples"  /* 完整的全局变量引用  */
	SET ^(2)="Oranges"                /* 裸全局变量全局引用 */
	SET ^(3)="Pears"                  /* 假设下标级别为2 */
	SET ^(3,1)="Bartlett pears"       /* 转到下标级别3  */
	SET ^(2)="Anjou pears"            /* 假设下标级别为3 */
	WRITE "latest global reference is: ",$ZREFERENCE,!
	ZWRITE ^Produce
	KILL ^Produce
	q ""
}
DHC-APP>w ##class(PHA.TEST.Global).GlobalNake()
latest global reference is: ^Produce("fruit",3,2)
^Produce("fruit",1)="Apples"
^Produce("fruit",2)="Oranges"
^Produce("fruit",3)="Pears"
^Produce("fruit",3,1)="Bartlett pears"
^Produce("fruit",3,2)="Anjou pears"

除了极少数例外,每个全局变量变引用(全引用或裸引用)都会设置裸指示器。$ZREFERENCE特殊变量包含最新全局变引用的完整全局名称和下标,即使这是一个裸全局引用。ZWRITE命令还显示每个全局的完整全局名称和下标,无论它是否使用裸引用设置。

应谨慎使用裸全局变量引用,因为InterSystems IRIS在不总是明显的情况下设置裸指示器,包括以下情况:

  • 完整全局变量引用最初设置裸露指示符,随后的完整全局引用或裸露全局引用会更改裸露指示符,即使全局引用不成功。例如,试图写入不存在的全局变量的值会设置裸指示符。
  • 无论InterSystems IRIS如何计算后置条件,引用下标全局的后置条件命令都会设置裸指示符。
  • 引用下标全局变量的可选函数参数可能设置或不设置裸指示符,具体取决于IRIS是否计算所有参数。例如,$get的第二个参数总是设置裸指示符,即使它包含的默认值没有使用。InterSystems IRIS按从左到右的顺序计算参数,因此最后一个参数可能会重置由第一个参数设置的裸指示符。
  • 回滚事务的TROLLBACK命令不会将裸指示符回滚到事务开始时的值。

如果完整全局变量引用包含扩展全局变量引用,则后续的裸全局变量引用将采用相同的扩展全局引用;不必将扩展引用指定为裸全局引用的一部分。

0
0 107
公告 Claire Zheng · 五月 8, 2021

亲爱的社区开发者们,大家好!

欢迎积极参与新一轮InterSystems开发者竞赛!

🏆 InterSystems 编程大赛:FHIR 加速器 🏆

提交在AWS上使用InterSystems的IRIS FHIR-as-a-service的应用程序,或帮助使用InterSystems IRIS FHIR加速器开发的解决方案。点击这篇文章,了解如何在AWS上申请应用InterSystems IRIS FHIR 加速器服务 (FHIRaaS) 。

时间:2021年5月10日-6月6日

奖金总额: $8,750 

👉  点击登录活动页面 👈


0
0 167
文章 姚 鑫 · 五月 7, 2021 7m read

第三章 使用多维存储(全局变量)(三)

在全局变量中复制数据

若要将全局变量(全部或部分)的内容复制到另一个全局变量(或局部数组)中,请使用ObjectScript Merge命令。

下面的示例演示如何使用Merge命令将OldData全局变量的全部内容复制到NewData全局变量中:

 Merge ^NewData = ^OldData

如果合并命令的source参数有下标,则复制该节点及其后代中的所有数据。如果Destination参数有下标,则使用目标地址作为顶级节点复制数据。例如,以下代码:

 Merge ^NewData(1,2) = ^OldData(5,6,7)

^OldData(5,6,7)及其下的所有数据复制到^NewData(1,2)

维护全局变量内的共享计数器

大规模事务处理应用程序的一个主要并发瓶颈可能是创建唯一标识符值。例如,考虑一个订单处理应用程序,在该应用程序中,必须为每一张新发票指定一个唯一的标识号。传统的方法是维护某种计数器表。每个创建新发票的进程都会等待获取此计数器上的锁,递增其值,然后将其解锁。这可能会导致对此单个记录的激烈资源争用。

为了解决此问题,InterSystems IRIS提供了ObjectScript $INCREMENT函数。$INCREMENT自动递增全局节点的值(如果该节点没有值,则设置为1)。$INCREMENT的原子性意味着不需要锁;该函数保证返回一个新的增量值,不会受到任何其他进程的干扰。

可以使用$INCREMENT,如下所示。首先,必须决定在其中存放计数器的全局节点。接下来,无论何时需要新的计数器值,只需调用$INCREMENT

 SET counter = $INCREMENT(^MyCounter)

InterSystems IRIS对象和SQL使用的默认存储结构使用$INCREMENT来分配唯一的对象(行)标识符值。

对全局变量中的数据进行排序

存储在全局变量中的数据会根据下标的值自动排序。例如,下面的ObjectScript代码定义了一组全局变量(按随机顺序),然后遍历它们以演示全局节点按下标自动排序:

/// w ##class(PHA.TEST.Global).GlobalSort()
ClassMethod GlobalSort()
{
	Kill ^Data

	Set ^Data("Cambridge") = ""
	Set ^Data("New York") = ""
	Set ^Data("Boston") = ""
	Set ^Data("London") = ""
	Set ^Data("Athens") = ""


	Set key = $Order(^Data(""))
	While (key '= "") {
		Write key,!
		Set key = $Order(^Data(key)) 
	}
	q ""
}
DHC-APP> w ##class(PHA.TEST.Global).GlobalSort()
Athens
Boston
Cambridge
London
New York
 

应用程序可以利用全局函数提供的自动排序来执行排序操作或维护对某些值的有序、交叉引用的索引。 InterSystems SQL和ObjectScript使用全局变量自动执行这些任务。

全局变量节点排序规则

全局变量节点的排序顺序(称为排序)在两个级别上进行控制:全局变量本身内部和使用全局变量的应用程序。

在应用程序级别,可以通过对用作下标的值执行数据转换来控制全局节点的排序方式(InterSystems SQL和对象通过用户指定的排序函数来执行此操作)。 例如,如果创建一个按字母顺序排序但忽略大小写的名称列表,那么通常你会使用名称的大写版本作为下标:

/// w ##class(PHA.TEST.Global).GlobalSortAlpha()
ClassMethod GlobalSortAlpha()
{
	Kill ^Data

	For name = "Cobra","jackal","zebra","AARDVark" {
		Set ^Data($ZCONVERT(name,"U")) = name
	}


	Set key = $Order(^Data(""))
	While (key '= "") {
		Write ^Data(key),!  
		Set key = $Order(^Data(key)) 
	}
	q ""
}
DHC-APP>w ##class(PHA.TEST.Global).GlobalSortAlpha()
AARDVark
Cobra
jackal
zebra
 

此示例将每个名称转换为大写(使用$ZCONVERT函数),以便对下标进行排序,而不考虑大小写。每个节点都包含未转换的值,以便可以显示原始值。

数值和字符串值下标

数字值在字符串值之前进行排序;也就是说,值1在值“a”之前。如果对给定的下标同时使用数值和字符串值,则需要注意这一点。如果将全局变量用于索引(即根据值对数据进行排序),则最常见的是将值排序为数字(如薪水salaries)或字符串(如邮政编码postal codes)。

对于按数字排序的节点,典型的解决方案是使用一元+运算符将下标值强制为数字值。例如,如果要构建按年龄对id值进行排序的索引,则可以强制年龄始终为数字:

 Set ^Data(+age,id) = ""

如果希望将值排序为字符串(如“0022”“0342”“1584”),则可以通过添加空格(“”)字符来强制下标值始终为字符串。例如,如果正在构建一个按邮政编码对id值进行排序的索引,则可以强制zipcode始终为字符串:

 Set ^Data(" "_zipcode,id) = ""

这确保带有前导零的值(如“0022”)始终被视为字符串。

$SORTBEGIN$SORTEND函数

通常,不必担心在InterSystems IRIS中对数据进行排序。无论使用SQL还是直接全局访问,排序都是自动处理的。

然而,在某些情况下,可以更有效地进行排序。 具体来说,在以下情况下(1)需要设置大量随机(即未排序)的全局节点,(2)生成的全局节点的总大小接近InterSystems IRIS缓冲池的很大一部分,那么性能可能会受到不利影响- 因为很多SET操作涉及到磁盘操作(因为数据不适合缓存)。 这种情况通常出现在涉及创建索引全局函数的情况下,例如批量数据加载、索引填充或对临时全局函数中的未索引值进行排序

为了有效地处理这些情况,ObjectScript提供了$SORTBEGIN$SORTEND函数。 $SORTBEGIN函数为全局变量(或其中的一部分)启动了一种特殊模式,在这种模式中,进入全局变量的数据集被写入一个特殊的临时缓冲区,并在内存(或临时磁盘存储)中进行排序。 当在操作结束时调用$SORTEND函数时,数据将按顺序写入实际的全局存储中。 总体操作效率更高,因为实际的写操作是按照要求更少磁盘操作的顺序完成的。

$SORTBEGIN函数很容易使用; 在开始排序操作之前,用你想要排序的全局变量的名称调用它,并在操作完成时调用$SORTEND:

/// w ##class(PHA.TEST.Global).GlobalSortBeginEnd()
ClassMethod GlobalSortBeginEnd()
{

	Kill ^Data
	
	// 为^Data全局初始化排序模式
	Set ret = $SortBegin(^Data)

	For i = 1:1:10000 {
		Set ^Data($Random(1000000)) = ""
	}

	Set ret = $SortEnd(^Data)

	// ^Data现在已经设置和排序

	Set start = $ZH 
	// 现在迭代并显示(按顺序)
	Set key = $Order(^Data(""))
	While (key '= "") {
		Write key,!
		Set key = $Order(^Data(key)) 
	}
	
	Set elap = $ZH - start  
	Write "Time (seconds): ",elap
	q ""
}

$SORTBEGIN函数是为全局变量创建的特殊情况而设计的,在使用时必须小心。 特别地,在$SORTBEGIN模式下,不能从正在写入的全局变量中读取数据; 由于数据没有写入,读取将是不正确的。

InterSystems SQL自动使用这些函数创建临时全局索引(例如对未索引的字段进行排序)。

在全局变量中使用间接

通过间接方式,ObjectScript提供了一种在运行时创建全局变量引用的方法。 这对于在程序编译时不知道全局变量结构或名称的应用程序非常有用。

间接操作符@支持间接操作,它解除了对包含表达式的字符串的引用。 根据@操作符的使用方式,有几种间接类型。

下面的代码提供了一个名称间接引用的示例,在这个示例中,使用@操作符对包含全局引用的字符串进行解引用:

/// w ##class(PHA.TEST.Global).GlobalIndirect()
ClassMethod GlobalIndirect()
{
	Kill ^Data
	Set var = "^Data(100)"
	// 现在使用间接设置^Data(100)
	Set @var = "This data was set indirectly."
	// 现在直接显示值:
	Write "Value: ",^Data(100)
	q ""
}
DHC-APP> w ##class(PHA.TEST.Global).GlobalIndirect()
Value: This data was set indirectly.

也可以使用下标间接在间接语句中混合表达式(变量或文字值):

/// w ##class(PHA.TEST.Global).GlobalIndirect1()
ClassMethod GlobalIndirect1()
{

	Kill ^Data
	Set glvn = "^Data"
	For i = 1:1:10 {
		Set @glvn@(i) = "This data was set indirectly."
	}

	Set key = $Order(^Data(""))
	While (key '= "") {
		Write "Value ",key, ": ", ^Data(key),!
		Set key = $Order(^Data(key))
	}
	q ""
}
DHC-APP>w ##class(PHA.TEST.Global).GlobalIndirect1()
Value 1: This data was set indirectly.
Value 2: This data was set indirectly.
Value 3: This data was set indirectly.
Value 4: This data was set indirectly.
Value 5: This data was set indirectly.
Value 6: This data was set indirectly.
Value 7: This data was set indirectly.
Value 8: This data was set indirectly.
Value 9: This data was set indirectly.
Value 10: This data was set indirectly.

间接是ObjectScript的一个基本特性; 它并不局限于全局引用。

0
0 163
文章 姚 鑫 · 五月 6, 2021 5m read

第三章 使用多维存储(全局变量)(二)

遍历全局变量中的数据

有许多方法可以遍历(迭代)存储在全局变量中的数据。

$ORDER(下一个/上一个)函数

ObjectScript $Order函数允许顺序访问全局中的每个节点。

$ORDER函数返回给定级别(下标编号)的下一个下标的值。例如,假设定义了以下全局设置:

 Set ^Data(1) = ""
 Set ^Data(1,1) = ""
 Set ^Data(1,2) = ""
 Set ^Data(2) = ""
 Set ^Data(2,1) = ""
 Set ^Data(2,2) = ""
 Set ^Data(5,1,2) = ""

要查找第一个第一级下标,我们可以使用:

 SET key = $ORDER(^Data(""))

这将返回空字符串(“”)之后的第一个第一级下标。(空字符串用于表示第一个条目之前的下标值;作为返回值,它用于指示没有后续的下标值。)。在本例中,key现在将包含值1

我们可以通过在$ORDER表达式中使用1或键来查找下一个第一级下标:

 SET key = $ORDER(^Data(key))

如果key的初始值为1,则此语句将其设置为2(因为^Data(2)是下一个第一级下标)。再次执行此语句会将key设置为5,因为这是下一个第一级下标。请注意,即使没有直接存储在^Data(5)中的数据,也会返回5。再次执行此语句将把key设置为空字符串(“”),表示没有更多的一级下标。

通过将附加下标与$ORDER函数一起使用,可以迭代不同的下标级别。$order返回其参数列表中最后一个下标的下一个值。使用上述数据,该语句如下:

 SET key = $ORDER(^Data(1,""))

将关键字设置为1,因为^Data(1,1)是下一个二级下标。再次执行此语句会将KEY设置为2,因为这是下一个二级下标。再次执行此语句将把key设置为“”,表示在节点^Data(1)下没有更多的二级下标。

使用$ORDER循环

下面的ObjectScript代码定义了一个简单的全局变量,然后循环遍历其所有第一级子脚本:

/// w ##class(PHA.TEST.Global).ReadGlobalSimpleFor()
ClassMethod ReadGlobalSimpleFor()
{
	// 清除^Data,以防它有数据
	Kill ^Data

	// 使用示例数据填写^Data
	For i = 1:1:100 {
		// 将每个节点设置为随机人名
		Set ^Data(i) = ##class(%PopulateUtils).Name()
	}

	// 在每个节点上循环 查找第一个节点
	Set key = $Order(^Data(""))

	While (key '= "") {
		Write "#", key, " ", ^Data(key),!
		// F查找下一个节点
		Set key = $Order(^Data(key))
	}
	
	q ""
}
DHC-APP>w ##class(PHA.TEST.Global).ReadGlobalSimpleFor()
#1 Edwards,Barbara T.
#2 Ragon,Kevin K.
#3 Avery,Josephine U.
#4 Townsend,Buzz R.
#5 Joyce,Quentin V.
#6 Xenia,Ted F.
#7 Chadwick,Wilma N.
#8 Duquesnoy,Orson A.
#9 Uberoth,Orson X.
#10 Jones,Joe O.
#11 Hills,Barb R.
#12 Yakulis,Pat J.
#13 Tesla,Al P.
#14 Goncharuk,Sam J.
#15 Presley,Amanda D.
#16 Olsen,Kristen I.
#17 Roentgen,John T.
#18 Minichillo,Elmo N.
#19 Koivu,Patrick R.
#20 Harrison,Lawrence I.
#21 Page,Agnes P.
#22 Wijnschenk,Hannah L.
#23 Chesire,Bart S.
#24 Klingman,Liza K.
#25 Smyth,Imelda J.
#26 Alton,Filomena L.
#27 Minichillo,Charles U.
#28 Nichols,Jeff W.
#29 O'Rielly,Thelma X.
#30 Schaefer,Kristen G.
#31 Black,Filomena R.
#32 Vivaldi,Xavier B.
#33 Allen,Phyllis U.
#34 Mastrolito,Zelda Z.
#35 Quilty,Jane V.
#36 Zevon,Maureen H.
#37 O'Rielly,Maureen C.
#38 Olsen,Robert W.
#39 Page,Milhouse D.
#40 Nelson,Dick R.
#41 Ironhorse,Danielle I.
#42 Tweed,Rhonda T.
#43 Quincy,Terry L.
#44 Tsatsulin,Jocelyn C.
#45 Yeats,Michelle E.
#46 Jackson,Paul V.
#47 Humby,Dave I.
#48 Kelvin,Natasha R.
#49 Kelvin,Kyra R.
#50 Yoders,Agnes R.
#51 Tesla,Amanda F.
#52 Harrison,Christen T.
#53 Allen,Nataliya J.
#54 Xenia,Diane W.
#55 Xenia,Phyllis E.
#56 Isaksen,Pam D.
#57 Waterman,Charles M.
#58 Peters,Sophia N.
#59 Peterson,Bart B.
#60 Eastman,Edward S.
#61 Young,Belinda F.
#62 White,Fred G.
#63 Ubertini,Lola U.
#64 Uhles,Xavier T.
#65 Quine,Phyllis T.
#66 Hernandez,Umberto B.
#67 Allen,Zelda S.
#68 Harrison,David Z.
#69 Harrison,Danielle T.
#70 Ott,Dick D.
#71 Lennon,Joe Y.
#72 Quigley,Alfred M.
#73 Klausner,Mario J.
#74 Tsatsulin,Emily S.
#75 Anderson,Edward R.
#76 Lennon,Fred H.
#77 DeSantis,Molly J.
#78 Browne,Dave H.
#79 Cunningham,Buzz L.
#80 Ingersol,Edgar G.
#81 Paraskiv,Linda O.
#82 Beatty,Kim H.
#83 Quilty,Wilma P.
#84 Dunlap,Jules I.
#85 Waterman,Buzz D.
#86 Edison,Kim C.
#87 Eagleman,Michael N.
#88 Huff,Hannah K.
#89 Vanzetti,Maria E.
#90 Zampitello,Angela Q.
#91 Anderson,Angela Z.
#92 Isaacs,Charlotte Q.
#93 O'Donnell,Paul A.
#94 Underman,Zeke R.
#95 Schultz,James I.
#96 Chadbourne,Janice N.
#97 Lennon,William T.
#98 Vonnegut,Pam V.
#99 Miller,Patricia T.
#100 Hills,Charles C.

其他$ORDER参数

ObjectScript $ORDER函数接受可选的第二个和第三个参数。 第二个参数是一个方向标志,指示希望在哪个方向上遍历全局变量。 默认值1指定正向遍历,而-1指定反向遍历。

第三个参数(如果存在)包含一个局部变量名。 如果$ORDER找到的节点包含数据,则将找到的数据写入这个本地变量。 当您在一个全局循环中,并且您对节点值和下标值感兴趣时,这样操作更有效。

$QUERY函数

如果需要访问全局变量中的每个节点和子节点,在子节点上上下移动,请使用ObjectScript $Query函数。(或者,可以使用嵌套的$ORDER循环)。

$Query函数接受全局变量引用,并返回一个字符串,其中包含全局变量中下一个节点的全局引用(如果没有后续节点,则返回"")。若要使用$QUERY返回的值,必须使用ObjectScript间接运算符(@)

例如,假设定义了以下全局设置:

 Set ^Data(1) = ""
 Set ^Data(1,1) = ""
 Set ^Data(1,2) = ""
 Set ^Data(2) = ""
 Set ^Data(2,1) = ""
 Set ^Data(2,2) = ""
 Set ^Data(5,1,2) = ""

以下是对$QUERY的调用:

 SET node = $QUERY(^Data(""))

将节点设置为字符串“^Data(1)”,即全局中第一个节点的地址。然后,要获取全局中的下一个节点,请再次调用$QUERY并在节点上使用间接运算符:

此时,节点包含字符串“^Data(1,1)”

以下示例定义一组全局变量节点,然后使用$QUERY遍历它们,同时写入每个节点的地址:

/// w ##class(PHA.TEST.Global).ReadGlobalSimpleQuery()
ClassMethod ReadGlobalSimpleQuery()
{
	Kill ^Data // 确保^Data为空

	// 将一些数据放入^Data // 
	Set ^Data(1) = ""
	Set ^Data(1,1) = ""
	Set ^Data(1,2) = ""
	Set ^Data(2) = ""
	Set ^Data(2,1) = ""
	Set ^Data(2,2) = ""
	Set ^Data(5,1,2) = ""

	// 现在浏览^Data 查找第一个节点
	Set node = $Query(^Data(""))
	While (node '= "") {
		Write node,!
		// 获取下一个节点
		Set node = $Query(@node)
	}
	q ""
}
DHC-APP>w ##class(PHA.TEST.Global).ReadGlobalSimpleQuery()
^Data(1)
^Data(1,1)
^Data(1,2)
^Data(2)
^Data(2,1)
^Data(2,2)
^Data(5,1,2)
0
0 137
文章 姚 鑫 · 五月 5, 2021 6m read

第三章 使用多维存储(全局变量)(一)

本章描述了使用多维存储(全局变量)可以执行的各种操作。

以全局变量存储数据

在全局节点中存储数据很简单:像对待任何其他变量一样对待全局变量。 区别在于对全局变量的操作是自动写入数据库的。

创建全局变量

创建新的全局变量不需要设置工作;只需将数据设置为全局变量即可隐式创建新的全局结构。可以创建全局变量(或全局变量下标)并通过单个操作将数据放入其中,也可以创建全局变量(或下标)并通过将其设置为空字符串将其保留为空。在ObjectScript中,这些操作是使用SET命令完成的。

下面的例子定义了一个名为Color(如果还不存在)的全局变量,并将值“Red”与之关联。 如果已经存在一个名为Color的全局变量,那么这些示例将其修改为包含新信息。

在ObjectScript中:

 SET ^Color = "Red"

注意:在应用程序中使用直接全局访变量问时,应制定并遵守命名约定,以防止应用程序的不同部分相互“遍历”;这类似于为类、方法和其他变量开发命名约定。

在全局变量节点中存储数据

要在全局下标节点中存储值,只需像设置任何其他变量数组一样设置全局节点的值。如果指定的节点以前不存在,则会创建该节点。如果它确实存在,则其内容将替换为新值。

可以通过表达式(称为全局引用)指定全局内的节点。全局引用由脱字符(^)、全局名称和(如果需要)一个或多个下标值组成。下标(如果有)用括号“()”括起来,并用逗号分隔。每个下标值本身都是一个表达式:文字值、变量、逻辑表达式,甚至是全局引用。

设置全局节点的值是一个原子操作:它肯定会成功,不需要使用任何锁来确保并发性。

以下都是有效的全局引用:

在ObjectScript中:

   SET ^Data = 2
   SET ^Data("Color")="Red"
   SET ^Data(1,1)=100        /*第二级下标(1,1)设置为值100。第一级下标(^DATA(1))不存储任何值。 */   
   SET ^Data(^Data)=10       /*全局变量^data的值是下标的名称。 */
   SET ^Data(a,b)=50         /*局部变量a和b的值是下标的名称 */
   SET ^Data(a+10)=50       

此外,还可以在运行时使用间接方式构造全局引用。

在全局变量节点中存储结构化数据

每个全局节点可以包含最多32K个字符的单个字符串。

数据通常以以下方式之一存储在节点中:

  • 作为最多32K个字符的单个字符串(具体地说,32K - 1)。
  • 作为包含多条数据的字符分隔字符串。

要使用字符分隔符在节点中存储一组字段,只需使用连接操作符(_)将这些值连接在一起。下面的ObjectScript示例使用#字符作为分隔符:

   SET ^Data(id)=field(1)_"#"_field(2)_"#"_field(3)

检索数据时,可以使用$PIECE函数将字段拆分:

    SET data = $GET(^Data(id))
    FOR i=1:1:3 {
        SET field(i) = $PIECE(data,"#",i)
    }
    QUIT
  • 作为包含多条数据的$LIST编码字符串。

$LIST函数使用特殊的长度编码方案,不需要保留分隔符。(这是InterSystems IRIS对象和SQL使用的默认结构。)

要在节点中存储一组字段,请使用$LISTBUILD函数构造列表:

   SET ^Data(id)=$LISTBUILD(field(1),field(2),field(3))

检索数据时,可以使用$LIST$LISTGET函数将字段拆分:

    SET data = $GET(^Data(id))
    FOR i = 1:1:3 {
        SET field(i)=$LIST(data,i)
    }
    QUIT
  • 作为较大数据集(例如流或“BLOB”)的一部分。

由于单个节点的数据量限制在略低于32K,因此可以通过将数据存储在一组连续节点中来实现更大的结构(如流):

   SET ^Data("Stream1",1) = "First part of stream...."
   SET ^Data("Stream1",2) = "Second part of stream...."
   SET ^Data("Stream1",3) = "Third part of stream...."

获取流的代码(如%GlobalCharacterStream类提供的流)循环遍历结构中的连续节点,该结构将数据作为连续字符串提供

  • 作为一个位串。

如果正在实现位图索引(位字符串中的位对应表中的行的索引),应该将全局索引的节点值设置为位字符串。 请注意IRIS使用压缩算法来编码位串; 因此,位串只能使用IRIS $BIT函数来处理。

  • 作为一个空节点。

如果感兴趣的数据是由节点本身提供的,那么通常将实际下标设置为空字符串("")。 例如,将名称与ID值相关联的索引通常是这样的:

  SET ^Data("APPLE",1) = ""
  SET ^Data("ORANGE",2) = ""
  SET ^Data("BANANA",3) = ""

删除全局节点

要从数据库中删除一个全局节点、一组子节点或整个全局节点,请使用ObjectScript killZKILL命令。

Kill命令删除特定全局引用处的所有节点(数据及其在数组中的相应条目),包括任何子代节点。也就是说,所有以指定下标开头的节点都将被删除。

例如,ObjectScript语句:

  KILL ^Data

删除整个^Data全局变量。对此全局变量的后续引用将返回<UNDEFINED>错误。

ObjectScript语句:

   KILL ^Data(100)

删除^Data全局变量中节点100的内容。如果有子代节点,如^data(100,1)^data(100,2)^data(100,1,2,3),这些子节点也会被删除。

ObjectScript ZKILL命令用于删除指定的全局或全局下标节点。它不会删除子代子节点。

注意:在杀死一个大型全局变量之后,该全局变量曾经占用的空间可能没有完全释放,因为垃圾收集器守护进程在后台将这些块标记为空闲。因此,在终止大型全局变量之后立即调用SYS.Database类的ReturnUnusedSpace方法可能不会返回预期大小的空间,因为该全局占用的块可能尚未释放。

不能对全局变量使用new命令。

测试全变量局节点的存在

要测试特定全局变量(或其后代)是否包含数据,请使用$DATA函数。

$DATA返回一个值,该值指示指定的全局变量引用是否存在。可能的返回值包括:

状态值含义
0全局变量未定义。
1全局变量存在并包含数据,但没有子代。请注意,空字符串(“”)可用作数据。
10全局变量有后代(包含指向子节点的向下指针),但本身不包含数据。对此类变量的任何直接引用都将导<UNDEFINED>错误。例如,如果$data(^y)返回10,则SET x=^y将产生<UNDEFINED>错误。
11全局变量既包含数据,又有后代(包含指向子节点的向下指针)。

检索全局变量节点的值

要获取存储在特定全局变量节点中的值,只需使用全局引用作为表达式:

   SET color = ^Data("Color")    ; assign to a local variable
   WRITE ^Data("Color")          ; use as a command argument
   SET x=$LENGTH(^Data("Color")) ; use as a function parameter

$GET函数

还可以使用$GET函数获取全局节点的值:

   SET mydata = $GET(^Data("Color"))

这将检索指定节点的值(如果存在),如果该节点没有值,则返回空字符串(“”)。如果节点没有值,可以使用可选的第二个参数$get返回指定的默认值。

WRITEZWRITEZZDUMP命令

可以使用各种ObjectScript显示命令显示全局变量或全局变量子节点的内容。WRITE命令以字符串形式返回指定全局或子节点的值。ZWRITE命令返回全局变量的名称及其值,以及它的每个子代节点及其值。ZZDUMP命令以十六进制转储格式返回指定全局或子节点的值。

0
0 95
文章 姚 鑫 · 五月 4, 2021 7m read

第二章 全局变量结构(二)

全局变量物理结构

全局变量使用高度优化的结构存储在物理文件中。管理此数据结构的代码也针对运行InterSystems IRIS的每个平台进行了高度优化。这些优化确保全局操作具有高吞吐量(每单位时间的操作数)、高并发性(并发用户总数)、缓存的高效使用,并且不需要与性能相关的持续维护(例如频繁的重建、重新索引或压缩)。

用于存储全局变量的物理结构是完全封装的;应用程序不会以任何方式担心物理数据结构。

全局变量存储在磁盘上的一系列数据块中;每个块的大小(通常为8KB)是在创建物理数据库时确定的。为了提供对数据的高效访问,InterSystems IRIS维护了一种复杂的B树状结构,该结构使用一组指针块将相关数据块链接在一起。InterSystems IRIS维护一个缓冲池-经常引用的块的内存缓存-以降低从磁盘获取块的成本。

虽然许多数据库技术使用类似B树的结构来存储数据,但InterSystems IRIS在许多方面都是独一无二的:

  • 存储机构通过安全、易于使用的接口显露出来。
  • 压缩下标和数据以节省磁盘空间和宝贵的内存缓存空间。
  • 存储引擎针对事务处理操作进行了优化:插入、更新和删除都很快。与关系系统不同,InterSystems IRIS从不需要重建索引或数据来恢复性能。
  • 存储引擎针对最大并发访问进行了优化。
  • 数据会自动群集,以实现高效检索。

引用全局变量

全局变量驻留在特定的InterSystems IRIS数据库中。如果使用适当的映射,全局变量的部分可以驻留在不同的数据库中。数据库可以在物理上位于当前系统上,也可以位于通过ECP网络访问的远程系统上。术语数据集是指包含InterSystems IRIS数据库的系统和目录。

命名空间是共同构成一组相关信息的数据集和全局映射的逻辑定义。

简单的全局变量引用适用于当前选定的命名空间。名称空间定义可能导致它物理访问本地系统或远程系统上的数据库。不同的全局变量可以映射到不同的位置或数据集(其中数据集是指包含InterSystems IRIS数据库的系统和目录)。

例如,要在当前已映射到的命名空间中创建对全局顺序的简单引用,请使用以下语法:

^ORDER

设置全局变量映射

可以将全局变量和例程从一个数据库映射到相同或不同系统上的另一个数据库。这允许简单地引用可以存在于任何地方的数据,这是命名空间的主要特征。可以映射整个全局或部分全局;映射全局(或下标)的一部分称为下标级别映射(SLM)。因为可以映射全局下标,所以数据可以轻松地跨磁盘。

全局映射是分层应用的。例如,如果NSX命名空间有一个关联的DBX数据库,但将^x全局变量映射到DBY数据库,将^x(1)映射到DBZ数据库,则^x全局变量的任何下标形式(属于^x(1)层次结构的那些除外)都映射到DBY;属于^x(1)层次结构的那些全局变量映射到DBZ。下图说明了此层次结构:

image

在此图中,全局变量及其层次结构显示为灰色,它们映射到的数据库显示为黑色。

还可以将映射的、下标的全局的一部分映射到另一个数据库,甚至映射回初始全局映射到的数据库。假设前面的示例有^x(1,2)全局变量返回到DBY数据库的附加映射。这将如下所示:

image

同样,全局变量及其层次结构显示为灰色,它们映射到的数据库显示为黑色。

一旦将全局从一个命名空间映射到另一个命名空间,就可以引用映射的全局变量,就像它在当前命名空间中一样-只需一个简单的引用,如^Order^X(1)

重要提示:建立下标级别映射范围时,字符串下标的行为与整数下标的行为不同。对于字符串,第一个字符确定范围,而对于整数,范围使用数值。例如,下标范围("A"):("C") 不仅包含AA,还包含ACABCDEF;相比之下,下标范围(1):(2) 不包含11

使用全局和下标的不同范围

命名空间的每个映射必须引用不同范围的全局变量或下标。映射验证可防止建立任何类型的重叠。例如,如果使用管理门户创建与现有映射重叠的新映射,则门户会阻止这种情况发生,并显示一条错误消息。

记录更改

通过门户对映射的成功更改也会记录在messages.log中;不成功的更改不会记录。通过手动编辑配置参数(CPF)文件来建立映射的任何失败尝试都会记录在messages.log中.

扩展的全局变量引用

可以引用位于当前命名空间以外的命名空间中的全局变量。这称为扩展全局变量引用或简称为扩展引用。

有两种形式的扩展引用:

  • 显式命名空间引用-将全局所在命名空间的名称指定为全局变量引用语法的一部分。
  • 隐含名称空间引用-指定目录和系统名称(可选)作为全局变量引用语法的一部分。在这种情况下,不适用全局变量映射,因为物理数据集(目录和系统)是作为全局变量引用的一部分提供的。

最好使用显式名称空间,因为这允许在需求更改时在外部重新定义逻辑映射,而无需更改应用程序代码。

InterSystems IRIS支持两种形式的扩展引用:

  • 方括号语法,它用方括号([])将扩展引用括起来。
  • 环境语法,用竖线(||)括起扩展引用。

注意:扩展全局引用的示例使用Windows目录结构。实际上,此类引用的形式取决于操作系统。

方括号语法

可以使用方括号语法来指定具有显式命名空间或隐含命名空间的扩展全局引用:

显式命名空间:

^[nspace]glob

隐含命名空间:

^[dir,sys]glob

在显式名称空间引用中,nspace是全局全局当前尚未映射或复制到的已定义名称空间。在隐含的名称空间引用中,dir是目录(其名称包括尾随反斜杠:“\”),sysSYSTEMglob是该目录中的全局目录。如果将nspacedir指定为(“^”),则引用的是进程私有全局变量。

除非将目录和系统名称或命名空间名称指定为变量,否则必须在目录和系统名称或命名空间名称两边加上引号。目录和系统一起构成一个隐含的命名空间。隐含的命名空间可以引用以下任一项:

  • 指定系统上的指定目录。
  • 本地系统上的指定目录(如果未在引用中指定系统名称)。如果在隐含的命名空间引用中省略了系统名称,则必须在目录引用内提供双脱字符(^^)以指示省略的系统名称。

要在远程系统上指定隐式命名空间,请执行以下操作:

["dir","sys"]

在本地系统上指定一个隐含的命名空间:

["^^dir"]

例如,要访问名为SALES的计算机上的C:\BUSINESS\目录中的全局变量ORDER:

  SET x = ^["C:\BUSINESS\","SALES"]ORDER

要访问本地计算机上的C:\BUSINESS\目录中的全局ORDER:

   SET x = ^["^^C:\BUSINESS\"]ORDER

要访问定义的命名空间MARKETING中的全局ORDER:

   SET x = ^["MARKETING"]ORDER

要访问进程私有的全局ORDER:

   SET x = ^["^"]ORDER

注意:在创建涉及镜像数据库的隐含命名空间扩展引用时,可以使用镜像数据库路径,格式为:mirror:mirror_name:mirror_DB_name。 例如,当在镜像CORPMIR中引用镜像数据库名称为mirdb1的数据库时,可以形成如下的隐含引用:

["^^:mirror:CORPMIR:mirdb1"]

镜像数据库路径既可以用于本地数据库,也可以用于远程数据库。

环境语法

环境语法被定义为:

^|"env"|global

"env"可以有以下五种格式之一:

  • 空字符串("")-本地系统上的当前命名空间。
  • "namespace" -定义的命名空间,当前没有全局映射到。 命名空间名称不区分大小写。 如果namespace具有特殊值"^",则它是进程私有的全局变量。
  • "^^dir" -一个隐含的命名空间,它的默认目录是本地系统上的指定目录,其中dir包含一个末尾的反斜杠(" \ ")
  • "^system^dir"——一个隐含的命名空间,默认目录是指定的远程系统上的指定目录,其中dir包含一个结尾的反斜杠(" \ ")。
  • 省略-如果根本没有"env",它是进程私有的全局变量。

要访问当前系统上当前命名空间中的全局ORDER,如果没有为ORDER定义映射,请使用以下语法:

   SET x = ^|""|ORDER

这与简单的全局变量引用相同:

   SET x = ^ORDER

要访问映射到定义的命名空间MARKETING的全局ORDER:

   SET x = ^|"MARKETING"|ORDER

可以使用一个隐含的命名空间来访问本地系统上C:\BUSINESS\目录下的全局ORDER:

   SET x = ^|"^^C:\BUSINESS\"|ORDER

可以使用一个隐含的命名空间来访问一个名为SALES的远程系统上的目录C:\BUSINESS中的全局ORDER:

   SET x = ^|"^SALES^C:\BUSINESS\"|ORDER

要访问进程私有的全局ORDER:

   SET x = ^||ORDER
   SET x=^|"^"|ORDER
0
0 156
文章 姚 鑫 · 五月 3, 2021 6m read

第二章 全局变量结构(一)

本章描述全局变量的逻辑视图,并概述全局变量是如何在磁盘上物理存储的。

全局变量的逻辑结构

全局变量是存储在物理InterSystems IRIS®数据库中的命名多维数组。 在应用程序中,全局变量到物理数据库的映射基于当前名称空间——名称空间提供一个或多个物理数据库的逻辑统一视图。

全局命名约定和限制

全局名称指定其目标和用途。有两种类型的全局变量和一组单独的变量,称为“进程私有全局变量”:

  • 全局变量 - 这就是所谓的标准全局变量;通常,这些变量被简称为全局变量。它是驻留在当前命名空间中的永久性多维数组。
  • 扩展全局引用-这是位于当前命名空间以外的命名空间中的全局引用。
  • 进程私有全局变量-这是一个数组变量,只有创建它的进程才能访问。

全局变量的命名约定如下:

  • 全局变量名称以脱字符(^)前缀开头。这个插入符号区分全局变量和局部变量。
  • 全局变量名称中脱字符(^)前缀后的第一个字符可以是:
    • 字母或百分号字符(%)-仅适用于标准全局变量。对于全局变量名称,字母被定义为ASCII 65ASCII 255范围内的字母字符。如果全局名称以“%”开头(但不是“%Z”“%z”),则此全局名称供InterSystems IRIS系统使用。%GLOBAL通常存储在IRISSYS或IRISLIB数据库中。
    • 竖线(|)或左方括号([)-表示扩展全局引用或进程专用全局变量。使用取决于后续字符。
  • 全局变量名称的其他字符可以是字母、数字或句号(.)字符。 百分比(%)字符不能使用,除非作为全局名称的第一个字符。 “.”字符不能作为全局名称的最后一个字符。
  • 全局名称最长可达31个字符(不包括脱字符前缀)。可以指定更长的全局名称,但InterSystems IRIS只将前31个字符视为重要字符。
  • 全局名称区分大小写。
  • InterSystems IRIS对全局引用的总长度施加限制,而该限制又对任何下标值的长度施加限制。

在IRISSYS数据库中,InterSystems将除以“z”“Z”“%z”“%Z”开头的所有全局变量名称保留给自己。在所有其他数据库中,InterSystems保留所有以“ISC”开头的全局名称。和“%isc.”

示例全局名称及其用法

以下是各种全局名称的示例以及每种名称的用法:

  • ^globalname - 标准全局变量
  • ^|"environment"|globalname - 扩展全局变量引用的环境语法
  • ^||globalname - 进程私有全局变量
  • ^|"^"| - 进程私有全局变量
  • ^[namespace]globalname - 扩展全局变量引用中显式命名空间的括号语法
  • ^[directory,system]globalname - 扩展全局变量引用中隐含命名空间的括号语法
  • ^["^"]globalname - 进程私有全局变量
  • ^["^",""]globalname - 进程私有全局变量

注意:全局名称只能包含有效的标识符字符;默认情况下,这些字符如上所述。但是,NLS(国家语言支持)定义了一组不同的有效标识符字符集。全局名称不能包含Unicode字符。

因此,以下都是有效的全局名称:

   SET ^a="The quick "
   SET ^A="brown fox "
   SET ^A7="jumped over "
   SET ^A.7="the lazy "
   SET ^A1B2C3="dog's back."
   WRITE ^a,^A,^A7,!,^A.7,^A1B2C3
   KILL ^a,^A,^A7,^A.7,^A1B2C3 // keeps the database clean 

全局节点和下标简介

全局通常有多个节点,通常由一个下标或一组下标标识。下面是一个基本示例:

 set ^Demo(1)="Cleopatra"

此语句引用全局节点^Demo(1),它是^Demo全局节点中的一个节点。此节点由一个下标标识。

再举一个例子:

 set ^Demo("subscript1","subscript2","subscript3")=12

该语句指的是全局节点^Demo("subscript1","subscript2","subscript3"),它是同一全局中的另一个节点。此节点由三个下标标识。

再举一个例子:

 set ^Demo="hello world"

该语句引用不使用任何下标的全局节点^Demo

全局的节点形成分层结构。ObjectScript提供了利用此结构的命令。例如,可以删除节点或删除节点及其所有子节点。

全局变量下标

下标有以下规则:

  • 下标数值区分大小写。
  • 下标值可以是任何ObjectScript表达式,前提是该表达式的计算结果不是空字符串("")。

该值可以包括所有类型的字符,包括空格、非打印字符和Unicode字符。(请注意,非打印字符在下标数值中不太实用。)

  • 在解析全局引用之前,InterSystems IRIS计算每个下标的方式与计算任何其他表达式的方式相同。在下面的示例中,我们设置了^Demo全局的一个节点,然后以几种等效的方式引用该节点:
DHC-APP>s ^Demo(1+2+3)="a value"
 
DHC-APP>w ^Demo(3+3)
a value
DHC-APP>w ^Demo(03+03)
a value
DHC-APP>w ^Demo(03.0+03.0)
a value
DHC-APP>set x=6
 
DHC-APP>w ^Demo(x)
a value
  • InterSystems IRIS对全局引用的总长度施加限制,而该限制又对任何下标值的长度施加限制。

注意:上述规则适用于IRIS支持的所有排序规则。对于出于兼容性原因仍在使用的旧归类,如“pre-ISM-6.1”,下标的规则有更多限制。例如,字符下标不能以控制字符作为其初始字符;整数下标中可以使用的位数也有限制。

全局变量节点

在应用程序中,节点通常包含以下类型的结构:

  1. 字符串或数字数据,包括本机Unicode字符。
  2. 具有由特殊字符分隔的多个字段的字符串:
 SET ^Data(10) = "Smith^John^Boston"

可以使用ObjectScript $PIECE 函数来拆分这些数据。

  1. InterSystems IRIS $LIST 结构中包含多个字段。$LIST结构是包含多个长度编码值的字符串。它不需要特殊的分隔符。
  2. 空字符串 ("")。在下标本身用作数据的情况下,实际节点中不存储任何数据。
  3. 一个位串。如果全局变量用于存储位图索引的一部分,那么存储在节点中的值就是位字符串。位串是包含10值的逻辑压缩集的字符串。可以使用$BIT函数构造位串。
  4. 更大的数据集的一部分。例如,对象和SQL引擎将流(BLOB)存储为全局中连续的32K节点系列。通过流接口,流的用户不知道流是以这种方式存储的。

请注意,任何全局节点都不能包含长度超过字符串长度限制的字符串,字符串长度限制非常长。

全局变量排序规则

在全局中,节点按排序(排序)顺序存储。

应用程序通常通过将转换应用于用作下标的值来控制节点的排序顺序。例如,SQL引擎在为字符串值创建索引时,会将所有字符串值转换为大写字母,并在前面加上一个空格字符,以确保索引不区分大小写并且以文本形式排序(即使数值存储为字符串)。

全局变量引用的最大长度

全局变量引用(即对特定全局节点或子树的引用)的总长度限制为511个编码字符(少于511个键入字符)。

要保守地确定给定全局变量引用的大小,请使用以下准则:

  1. 全局变量名称:每个字符加1
  2. 对于纯数字下标:每个数字、符号或小数点加1
  3. 对于包含非数字字符的下标:为每个字符添加3

如果下标不是纯数字的,则根据用于编码字符串的字符集的不同,下标的实际长度会有所不同。一个多字节字符最多可以占用3个字节。

请注意,ASCII字符可能占用12字节。 如果排序规则进行大小写折叠,那么ASCII字符可以使用1个字节表示字符,1个字节表示消除歧义字节。 如果排序不执行大小写折叠,ASCII字符占用1字节。

  1. 每个下标加1

如果这些数字的总和大于511,则引用太长。

由于确定限制的方式,如果必须使用长下标或全局名称,这有助于避免使用大量下标级别。 相反,如果使用多个下标级别,则应避免长全局名称和长下标。 因为无法控制正在使用的字符集,所以保持全局名称和下标更短是很有用的。

当对特定引用有疑问时,创建与最长预期全局变量引用长度相等(甚至稍长一点)的全局变量引用的测试版本是有用的。 这些测试的数据为构建应用程序之前可能修订的命名约定提供了指导。

0
0 118
文章 姚 鑫 · 五月 2, 2021 5m read

第一章 简介global

InterSystems IRIS®的核心功能之一是其多维存储引擎。此功能允许应用程序以紧凑、高效的多维稀疏数组存储数据。这些数组称为全局数组。

本章介绍:

  • 什么是全局变量(globals ),以及可以对其执行的操作。
  • 全局变量的逻辑和物理结构,包括在分布式数据库体系结构中使用全局变量。
  • 如何使用全局变量在应用程序中存储和检索数据。
  • 如何使用全局变量。

特点

全局变量提供了一种在持久的多维数组中存储数据的易于使用的方法。

例如,可以使用名为^Settings的全局变量将值“Red”与键“Color”相关联:

SET ^Settings("Color")="Red"

可以利用全局变量的多维特性来定义更复杂的结构:

 SET ^Settings("Auto1","Properties","Color") = "Red"
 SET ^Settings("Auto1","Properties","Model") = "SUV"
 SET ^Settings("Auto2","Owner") = "Mo"
 SET ^Settings("Auto2","Properties","Color") = "Green"

全局变量具有以下功能:

  • 简单易用-全局变量和其他编程语言变量一样易于使用。
  • 多维-可以使用任意数量的下标指定全局内节点的地址。 例如,在 ^Settings("Auto2","Properties","Color")中,下标Color是全局设置中的第三级节点。 下标可以是整数、数字或字符串值,并且不需要是连续的。
  • 稀疏-用于寻址全局节点的下标高度压缩,不需要连续的值。
  • 高效-全局变量上的操作(插入、更新、删除、遍历和检索)都经过高度优化,可实现最高性能和并发性。还有用于特殊操作(如批量插入数据)的其他命令。有一组特殊的全局变量是为临时数据结构设计的(例如,用于对记录进行排序)。
  • 可靠-InterSystems IRIS数据库提供了许多机制来确保存储在全局数据库中的数据的可靠性,包括逻辑级和物理级日志记录。执行数据库备份操作时,将备份存储在全局数据库中的数据。
  • 分布式IRIS提供了多种方法来控制存储在全局数据库中的数据的物理位置。可以定义用于存储全局的物理数据库,或将全局的部分分布到多个数据库中。使用InterSystems IRIS的分布式数据库功能,可以在数据库和应用程序服务器系统网络中共享全局数据。此外,通过镜像技术,存储在一个系统上的全局数据库中的数据可以自动复制到另一个系统上。
  • 并发-全局支持多个进程之间的并发访问。在单个节点(数组元素)中设置和检索值始终是原子的:不需要锁定即可保证可靠的并发访问。此外,InterSystems IRIS支持一组强大的锁定操作,可用于为涉及多个节点的更复杂情况提供并发性。使用对象或SQL访问时,会自动处理此并发。
  • 事务性-InterSystems IRIS提供定义事务边界的命令;可以启动、提交或回滚事务。在回滚的情况下,事务内对全局变量所做的所有修改都将被撤消;数据库的内容将恢复到事务前的状态。通过将各种InterSystems IRIS锁定操作与事务结合使用,可以使用全局变量执行传统的ACID事务。(ACID事务提供原子性、一致性、隔离性和持久性。)。使用对象或SQL访问时,事务会自动处理。

注意:本文档中描述的全局变量不应与另一种类型的InterSystems IRIS数组变量混淆:进程私有全局变量。进程私有全局变量不是持久的;它们仅在创建它们程序期间持续。进程私有全局变量也不是并发的;它们只能由创建它们的进程访问。进程专用全局可以通过其多字符名称前缀:^||^|"^"|轻松地与全局区分开来。

例如

一个简单的例子就可以展示全局变量的易用性和性能。下面的程序示例创建一个10,000个节点的数组(如果存在,则首先将其删除)并将其存储在数据库中。可以尝试这样做,以了解全局变量的性能:

/// w ##class(PHA.TEST.Global).GlobalSimple()
ClassMethod GlobalSimple()
{
	Set start = $ZH  // get current time

	Kill ^Test.Global
	For i = 1:1:10000 {
	Set ^Test.Global(i) = i
	}

	Set elap = $ZH - start  // get elapsed time
	Write "Time (seconds): ",elap
	
	q ""
}
DHC-APP> w ##class(PHA.TEST.Global).GlobalSimple()
Time (seconds): .00307

我们还可以看到迭代和读取数组中的值需要多长时间(确保首先运行上面的示例来构建数组):

-读取持久数组-

/// w ##class(PHA.TEST.Global).ReadGlobalSimple()
ClassMethod ReadGlobalSimple()
{
	Set start = $ZH  // get current time
	Set total = 0
	Set count = 0

	// get key and value for first node
	Set i = $Order(^Test.Global(""),1,data)

	While (i '= "") {
		Set count = count + 1
		Set total = total + data

		// get key and value for next node
		Set i = $Order(^Test.Global(i),1,data)
	}

	Set elap = $ZH - start  // get elapsed time

	Write "Nodes:          ",count,!
	Write "Total:          ",total,!
	Write "Time (seconds): ",elap,!
	
	q ""
}
DHC-APP>w ##class(PHA.TEST.Global).ReadGlobalSimple()
Nodes:          10000
Total:          50005000
Time (seconds): .001879

在应用程序中使用

在InterSystems IRIS应用程序中,全局变量有多种使用方式,包括:

  • 作为对象和SQL引擎共享的底层存储机制。
  • 作为用于为对象和SQL数据提供各种索引(包括位图索引)的机制。
  • 作为用于执行不适合进程存储器的某些操作的工作空间。例如,当没有预先存在的索引可用于排序数据时,SQL引擎使用临时全局变量对数据进行排序。
  • 用于在对象或SQL访问方面难以表达或效率低下的持久性对象或SQL表上执行专用操作。例如,可以定义一个方法(或存储过程或Web方法)来对表中保存的数据执行专门的分析。通过使用方法,这样的操作是完全封装的;调用者只需调用该方法。
  • 实现特定于应用程序的自定义存储结构。许多应用程序需要存储难以用关系表示的数据。使用全局变量,可以定义自定义结构,并通过对象方法将其提供给外部客户端。
  • 用于InterSystems IRIS系统使用的各种特殊用途的数据结构,例如配置数据、类定义、错误消息和可执行代码。

全局变量不受关系模型的限制。 它们提供了开发针对特定应用程序优化的定制结构的自由。 对于许多应用程序来说,明智地使用全局变量可能是提供性能的秘密武器,而这种性能是关系应用程序开发人员梦寐以求的。

无论应用程序是否直接使用全局变量,了解它们的操作都是有用的。 理解全局及其功能将帮助设计更高效的应用程序,并为确定应用程序的最佳部署配置提供帮助。

0
0 274
文章 姚 鑫 · 四月 30, 2021 12m read

第八章 解释SQL查询计划(二)

SQL语句的详细信息

有两种方式显示SQL语句的详细信息:

  • 在SQL Statements选项卡中,通过单击左侧列中的Table/View/Procedure Name链接选择一个SQL Statement。 这将在单独的选项卡中显示SQL语句详细信息。 该界面允许打开多个选项卡进行比较。 它还提供了一个Query Test按钮,用于显示SQL Runtime Statistics页面。
  • 从表的Catalog Details选项卡(或SQL Statements选项卡)中,通过单击右边列中的Statement Text链接选择一个SQL语句。 这将在弹出窗口中显示SQL语句详细信息。

可以使用“SQL语句详细信息”显示来查看查询计划,并冻结或解冻查询计划。

“SQL语句详细信息”提供冻结或解冻查询计划的按钮。 它还提供了一个Clear SQL Statistics按钮来清除性能统计,一个Export按钮来将一个或多个SQL语句导出到一个文件,以及一个RefreshClose页面按钮。

SQL语句详细信息显示包含以下部分。 每个部分都可以通过选择部分标题旁边的箭头图标展开或折叠:

  • 语句详细信息,其中包括性能统计
  • 编译设置
  • 语句在以下例程中定义
  • 语句使用如下关系
  • 语句文本和查询计划(在其他地方描述)

声明的细节部分

  • 语句散列Statement hash:语句定义的内部散列表示形式,用作SQL语句索引的键(仅供内部使用)。 有时,看起来相同的SQL语句可能具有不同的语句散列项。 需要生成不同SQL语句的代码的设置/选项的任何差异都会导致不同的语句散列。 这可能发生在支持不同内部优化的不同客户端版本或不同平台上。
  • 时间戳Timestamp:最初,创建计划时的时间戳。 这个时间戳会在冻结/解冻之后更新,以记录计划解冻的时间,而不是重新编译计划的时间。 可能必须单击Refresh Page按钮来显示解冻时间戳。 将Plan Timestamp与包含该语句的例程/类的datetime值进行比较,可以知道,如果再次编译该例程/类,它是否使用了相同的查询计划。
  • 版本Version:创建计划的InterSystems IRIS版本。 如果“计划”状态是“冻结/升级”,则这是InterSystems IRIS的早期版本。 解冻查询计划时,“计划”状态变为“解冻”,“版本”变为当前的InterSystems IRIS版本。
  • 计划状态Plan state:冻结/显式、冻结/升级、解冻、解冻/并行。 Frozen/Explicit意味着该语句的计划已被显式用户操作冻结,无论生成此SQL语句的代码发生了什么变化,该冻结的计划都将是将要使用的查询计划。 冻结/升级意味着该语句的计划已被InterSystems IRIS版本升级自动冻结。 解冻意味着该计划目前处于解冻状态,可能被冻结。 Unfrozen/Parallel表示该计划被解冻,并使用%Parallel处理,因此不能被冻结。 NULL(空白)计划状态意味着没有关联的查询计划。
  • 自然查询Natural query:一个布尔标志,指示该查询是否是“自然查询”。 如果勾选此项,则该查询是自然查询,不会记录查询性能统计信息。 如果不检查,性能统计可能会被记录; 其他因素决定了统计数据是否真正被记录下来。 自然查询被定义为嵌入式SQL查询,它非常简单,记录统计数据的开销会影响查询性能。 将统计信息保存在自然查询上没有任何好处,因为查询已经非常简单了。 一个很好的自然查询示例是SELECT Name INTO:n FROM Table WHERE %ID=? 这个查询的WHERE子句是一个相等条件。 此查询不涉及任何循环或任何索引引用。 动态SQL查询(缓存查询)不会被标记为自然查询; 缓存查询的统计数据可能被记录,也可能不被记录。
  • 冻结计划不同Frozen plan different:冻结计划时,会显示该字段,显示冻结的计划与未冻结的计划是否不同。 冻结计划时,语句文本和查询计划将并排显示冻结的计划和未冻结的计划,以便进行比较。

本节还包括五个查询性能统计字段,将在下一节中进行描述。

性能统计数据

执行查询会将性能统计数据添加到相应的SQL语句。 此信息可用于确定哪些查询执行得最慢,哪些查询执行得最多。 通过使用这些信息,您可以确定哪些查询将通过优化提供显著的好处。

除了SQL语句名称、计划状态、位置和文本之外,还为缓存查询提供了以下附加信息:

  • 计数Count:运行此查询次数的整数计数。 如果对该查询产生不同的查询计划(例如向表中添加索引),则将重置该计数。
  • 平均计数Average count:每天运行此查询的平均次数。
  • 总时间Total time:运行此查询所花费的时间(以秒为单位)。
  • 平均时间Average time:运行此查询所花费的平均时间(以秒为单位)。 如果查询是缓存的查询,则查询的第一次执行所花费的时间很可能比从查询缓存中执行优化后的查询所花费的时间要多得多。
  • 标准差Standard deviation:总时间和平均时间的标准差。 只运行一次的查询的标准偏差为0。 运行多次的查询通常比只运行几次的查询具有更低的标准偏差。
  • 第一次看到的日期Date first seen:查询第一次运行(执行)的日期。 这可能与Last Compile Time不同,后者是准备查询的时间。

UpdateSQLStats任务会定期更新已完成的查询执行的查询性能统计数据。 这将最小化维护这些统计信息所涉及的开销。 因此,当前运行的查询不会出现在查询性能统计中。 最近完成的查询(大约在最近一个小时内)可能不会立即出现在查询性能统计中。

可以使用Clear SQL Statistics按钮清除这6个字段的值。

InterSystems IRIS不单独记录%PARALLEL子查询的性能统计数据。 %PARALLEL子查询统计信息与外部查询的统计信息相加。 由并行运行的实现生成的查询没有单独跟踪其性能统计信息。

InterSystems IRIS不记录“自然”查询的性能统计数据。 如果系统收集了统计信息,则会降低查询性能,而自然查询已经是最优的,因此没有进行优化的可能。

可以在“SQL语句”选项卡显示中查看多个SQL语句的查询性能统计信息。 您可以按任何列对SQL Statements选项卡列表进行排序。 这使得很容易确定,例如,哪个查询具有最大的平均时间。

还可以通过查询INFORMATION.SCHEMA.STATEMENTS类属性来访问这些查询性能统计数据,如查询SQL语句中所述。

编译设置部分

  • 选择模式Select mode:编译语句时使用的SelectMode。 对于DML命令,可以使用#SQLCompile Select; 默认为Logical。 如果#SQLCompile Select=Runtime,调用$SYSTEM.SQL.Util.SetOption()方法的SelectMode选项可以改变查询结果集的显示,但不会改变SelectMode值,它仍然是Runtime。
  • 默认模式Default schema(s):编译语句时设置的默认模式名。 这通常是在发出命令时生效的默认模式,尽管SQL可能使用模式搜索路径(如果提供的话)而不是默认模式名来解析非限定名称的模式。 但是,如果该语句是嵌入式SQL中使用一个或多个#Import宏指令的DML命令,则#Import指令指定的模式将在这里列出。
  • 模式路径Schema path:编译语句时定义的模式路径。 如果指定,这是模式搜索路径。 如果没有指定架构搜索路径,则此设置为空。 但是,对于在#Import宏指令中指定搜索路径的DML Embedded SQL命令,#Import搜索路径显示在默认模式设置中,并且该模式路径设置为空白。
  • 计划错误Plan Error:该字段仅在使用冻结计划时发生错误时出现。 例如,如果一个查询计划使用一个索引,则该查询计划被冻结,然后该索引从表中删除,就会出现如下的计划错误:Map 'NameIDX' not defined in table 'Sample.Person', but it was specified in the frozen plan for the query. 删除或添加索引将导致重新编译表,从而更改“最后编译时间”值。 一旦导致错误的条件得到纠正,Clear Error按钮可用于清除Plan Error字段——例如,通过重新创建缺失的索引。 在错误条件被纠正后使用“清除错误”按钮会导致“计划错误”字段和“清除错误”按钮消失。

例程和关系部分

语句在以下例程部分中定义:

  • 例程Routine:与缓存查询关联的类名(对于动态SQL DML),或者例程名(对于嵌入式SQL DML)。
  • 类型:类方法或MAC例程(对于嵌入式SQL DML)。
  • 上次编译时间Last Compile Time:例程的上次编译时间或准备时间。如果SQL语句解冻,重新编译MAC例程会同时更新此时间戳和Plan时间戳。如果SQL语句已冻结,则重新编译MAC例程仅更新此时间戳;在您解冻计划之前,Plan时间戳不会更改;然后Plan时间戳将显示计划解冻的时间。

语句使用以下关系部分列出了一个或多个用于创建查询计划的定义表。对于使用查询从另一个表提取值的INSERT,或者使用FROM子句引用另一个表的UPDATEDELETE,这两个表都在此处列出。每个表都列出了下列值:

  • 表或视图名称Table or View Name:表或视图的限定名称。
  • 类型Type:表或视图。
  • 上次编译时间Last Compile Time:表(持久化类)上次编译的时间。
  • Classname:与表关联的类名。

本节包括用于重新编译类的编译类选项。如果重新编译解冻计划,则所有三个时间字段都会更新。如果重新编译冻结的计划,则会更新两个上次编译时间字段,但不会更新计划时间戳。解冻计划并单击刷新页面按钮后,计划时间戳将更新为计划解冻的时间。

查询SQL语句

可以使用SQLTableStatements()存储查询返回指定表的SQL语句。下面的示例显示了这一点:

/// w ##class(PHA.TEST.SQL).SQLTableStatements()
ClassMethod SQLTableStatements()
{
	SET mycall = "CALL %Library.SQLTableStatements('Sample','Person')"
	SET tStatement = ##class(%SQL.Statement).%New()
	SET qStatus=tStatement.%Prepare(mycall)
	IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
	SET rset=tStatement.%Execute()
	IF rset.%SQLCODE '= 0 {WRITE "SQL error=",rset.%SQLCODE QUIT}
	DO rset.%Display()
}
DHC-APP>w ##class(PHA.TEST.SQL).SQLTableStatements()
 
 
Dumping result #1
SCHEMA  RELATION_NAME   PLAN_STATE      LOCATION        STATEMENT
SAMPLE  PERSON  0       %sqlcq.DHCdAPP.cls228.1 DECLARE C CURSOR FOR SELECT * INTO :%col(1) , :%col(2) , :%col(3) , :%col(4) , :%col(5) , :%col(6) , :%col(7) , :%col(8) , :%col(9) , :%col(10) , :%col(11) , :%col(12) , :%col(13) , :%col(14) , :%col(15) FROM SAMPLE . PERSON
SAMPLE  PERSON  0       Sample.Person.1 SELECT AGE , DOB , FAVORITECOLORS , HOME , NAME , OFFICE , SSN , SPOUSE , X__CLASSNAME , HOME_CITY , HOME_STATE , HOME_STREET , HOME_ZIP , OFFICE_CITY , OFFICE_STATE , OFFICE_STREET , OFFICE_ZIP INTO :%e ( ) FROM %IGNOREINDEX * SAMPLE . PERSON WHERE ID = :%rowid
...
CURSOR FOR SELECT P . NAME , P . AGE , E . NAME , E . AGE FROM %ALLINDEX SAMPLE . PERSON AS P LEFT OUTER JOIN SAMPLE . EMPLOYEE AS E ON P . NAME = E . NAME WHERE P . AGE > 21 AND %NOINDEX E . AGE < 65
SAMPLE  PERSON  0       PHA.TEST.SQL.1  SELECT NAME , SPOUSE INTO :name , :spouse FROM SAMPLE . PERSON WHERE SPOUSE IS NULL
SAMPLE  PERSON  0       PHA.TEST.ObjectScript.1 SELECT NAME , DOB , HOME INTO :n , :d , :h FROM SAMPLE . PERSON
 
70 Rows(s) Affected

可以使用INFORMATION_SCHEMA包表来查询SQL语句列表。InterSystems IRIS支持以下类:

  • INFORMATION_SCHEMA.STATEMENTS:包含当前名称空间中的当前用户可以访问的SQL语句索引项。
  • INFORMATION_SCHEMA.STATEMENT_LOCATIONS:包含调用SQL语句的每个例程位置:持久类名或缓存查询名。
  • INFORMATION_SCHEMA.STATEMENT_RELATIONS:包含SQL语句使用的每个表或视图条目。

以下是使用这些类的一些示例查询:

下面的示例返回命名空间中的所有SQL语句,列出哈希值(唯一标识规范化SQL语句的计算ID)、冻结状态标志(值0到3)、准备语句和保存计划时的本地时间戳以及语句文本本身:

SELECT Hash,Frozen,Timestamp,Statement FROM INFORMATION_SCHEMA.STATEMENTS

image

以下示例返回所有冻结计划的SQL语句,指示冻结的计划是否与未冻结的计划不同。请注意,解冻语句可以是Frozen=0Frozen=3。不能冻结的单行INSERT等语句在冻结列中显示NULL:

SELECT Frozen,FrozenDifferent,Timestamp,Statement FROM INFORMATION_SCHEMA.STATEMENTS
WHERE Frozen=1 OR Frozen=2

以下示例返回给定SQL表的所有SQL语句和语句所在的例程。(请注意,指定表名(SAMPLE.PERSON)时必须使用与SQL语句文本中相同的字母大小写:全部大写字母):

SELECT Statement,Frozen,STATEMENT_LOCATIONS->Location AS Routine,STATEMENT_LOCATIONS->Type AS RoutineType
       FROM INFORMATION_SCHEMA.STATEMENTS 
       WHERE STATEMENT_RELATIONS->Relation='SAMPLE.PERSON'

image

以下示例返回当前命名空间中具有冻结计划的所有SQL语句:

SELECT Statement,Frozen,Frozen_Different,STATEMENT_LOCATIONS->Location AS Routine,STATEMENT_LOCATIONS->Type AS RoutineType
       FROM INFORMATION_SCHEMA.STATEMENTS 
       WHERE Frozen=1 OR Frozen=2

以下示例返回当前命名空间中包含COUNT(*)聚合函数的所有SQL语句。(请注意,指定语句文本(COUNT(*))时必须使用与SQL语句文本相同的空格):

       SELECT Statement,Frozen,STATEMENT_LOCATIONS->Location AS Routine,STATEMENT_LOCATIONS->Type AS RoutineType
       FROM INFORMATION_SCHEMA.STATEMENTS 
       WHERE Statement [ ' COUNT ( * )

image

导出和导入SQL语句

可以将SQL语句作为XML格式的文本文件导出或导入。这使可以将冻结的计划从一个位置移动到另一个位置。SQL语句导出和导入包括关联的查询计划。

可以导出单个SQL语句,也可以导出命名空间中的所有SQL语句。

可以导入先前导出的包含一个或多个SQL语句的XML文件。

注意:将SQL语句作为XML导入不应与从文本文件导入和执行SQL DDL代码混淆。

导出SQL语句

导出单个SQL语句:

  • 使用SQL语句详细资料页导出按钮。在管理门户系统资源管理器SQL界面中,选择SQL语句选项卡,然后单击语句以打开SQL语句详细信息页。选择导出按钮。这将打开一个对话框,允许选择将文件导出到服务器(数据文件)或浏览器。

    • 服务器(默认):输入导出XML文件的完整路径名。第一次导出时,此文件的默认名称为statementexport.xml。当然,可以指定不同的路径和文件名。成功导出SQL语句文件后,上次使用的文件名将成为默认值。

    默认情况下,未选中在后台运行导出复选框。

    • Browser:将文件statementexport.xml导出到用户默认浏览器中的新页面。可以为浏览器导出文件指定其他名称,或指定其他软件显示选项。
  • 使用$SYSTEM.SQL.Statement.ExportFrozenPlans()方法。

导出命名空间中的所有SQL语句:

  • 使用管理门户中的导出所有对帐单操作。从管理门户系统资源管理器SQL界面中,选择操作下拉列表。从该列表中选择Export all Statements。这将打开一个对话框,允许您将命名空间中的所有SQL语句导出到服务器(数据文件)或浏览器。
    • 服务器(默认):输入导出XML文件的完整路径名。第一次导出时,此文件的默认名称为statementexport.xml。当然,可以指定不同的路径和文件名。成功导出SQL语句文件后,上次使用的文件名将成为默认值。

默认情况下,在后台运行导出复选框处于选中状态。这是导出所有SQL语句时的建议设置。选中在后台运行导出时,系统会为提供一个查看后台列表页面的链接,可以在该页面中查看后台作业状态。

  • Browser:将文件statementexport.xml导出到用户默认浏览器中的新页面。可以为浏览器导出文件指定其他名称,或指定其他软件显示选项。

使用$SYSTEM.SQL.Statement.ExportAllFrozenPlans()方法。

导入SQL语句

从先前导出的文件导入一条或多条SQL语句:

  • 使用管理门户中的导入对帐单操作。从管理门户系统资源管理器SQL界面中,选择操作下拉列表。从该列表中选择Import Statements。这将打开一个对话框,允许指定导入XML文件的完整路径名。

默认情况下,在后台运行导入复选框处于选中状态。这是导入SQL语句文件时的推荐设置。选中在后台运行导入时,系统会为您提供一个查看后台列表页面的链接,可以在该页面中查看后台作业状态。

使用$SYSTEM.SQL.Statement.ImportFrozenPlans()方法。

查看和清除后台任务

在管理门户系统操作选项中,选择后台任务,查看导出和导入后台任务的日志。可以使用清除日志按钮清除此日志。

0
0 182
文章 姚 鑫 · 四月 29, 2021 10m read

第九章 冻结计划

大多数SQL语句都有一个关联的查询计划。查询计划是在准备SQL语句时创建的。默认情况下,添加索引和重新编译类等操作会清除此查询计划。下次调用查询时,将重新准备查询并创建新的查询计划。冻结计划使可以跨编译保留(冻结)现有查询计划。查询执行使用冻结的计划,而不是执行新的优化并生成新的查询计划。

对系统软件的更改也可能导致不同的查询计划。通常,这些升级会带来更好的查询性能,但软件升级可能会降低特定查询的性能。冻结计划使可以保留(冻结)查询计划,以便查询性能不会因系统软件升级而改变(降级或提高)。

如何使用冷冻计划

使用冻结计划有两种策略-乐观策略和悲观策略:

  • 乐观:如果假设更改系统软件或类定义会提高性能,请使用此策略。运行查询并冻结计划。导出(备份)冻结的计划。解冻该计划。更改软件。重新运行查询。这会产生一个新的计划。比较这两个查询的性能。如果新计划没有提高性能,可以从备份文件中导入先前冻结的计划。
  • 悲观:如果假设系统软件或类定义的更改可能不会提高特定查询的性能,请使用此策略。运行查询并冻结计划。更改软件。使用%NOFPLAN关键字重新运行查询(这会导致冻结的计划被忽略)。比较这两个查询的性能。如果忽略冻结的计划没有提高性能,请保持冻结该计划并从查询中删除%NOFPLAN

软件版本升级自动冻结计划

将InterSystems IRIS®Data Platform升级到新的主要版本时,现有的查询计划将自动冻结。这可确保重大软件升级永远不会降低现有查询的性能。升级软件版本后,对性能关键型查询执行以下步骤:

  1. 执行计划状态为冻结/升级的查询,并监控性能。这是在软件升级之前创建的优化查询计划。
  2. %NOFPLAN关键字添加到查询中,然后执行并监视性能。这将使用软件升级提供的SQL优化器优化查询计划。它不会解冻现有的查询计划。
  3. 比较性能指标。
  • 如果%NOFPLAN性能更好,则软件升级改进了查询计划。解冻查询计划。删除%NOFPLAN关键字。
  • 如果%NOFPLAN性能较差,则软件升级会使查询计划降级。保持查询计划冻结状态,将查询计划从冻结/升级升级为冻结/显式。删除%NOFPLAN关键字。
  1. 测试性能关键型查询后,可以解冻所有剩余的冻结/升级计划。

当在比最初创建计划时使用的InterSystems软件版本更新的InterSystems软件版本下准备/编译查询时,会发生这种自动冻结。例如,考虑一条在系统软件版本xxxx.1下准备/编译的SQL语句。随后升级到版本xxxx.2,再次准备/编译SQL语句。系统将检测到这是SQL语句在新版本上的第一次准备/编译,并自动将计划状态标记为冻结/升级,并将现有计划用于新的准备/编译。这确保使用的查询计划不会比以前版本的查询计划差。

只有主要版本的InterSystems系统软件升级才会自动冻结现有查询计划。维护发布版本升级不会冻结现有查询计划。例如,主要版本升级(如从2018.1升级到2019.1)将执行此操作。维护版本升级(如2018.1.0到2018.1.1)不执行此操作。

在管理门户SQL界面中,SQL语句计划状态列将这些自动冻结的计划指示为冻结/升级,计划版本指示原始计划的系统间软件版本。

可以使用INFORMATION.SCHEMA.STATEMENTSFrozen=2属性列出当前命名空间中的所有冻结/升级计划。

可以使用以下$SYSTEM.SQL.Statement方法冻结单个查询计划或多个查询计划:FreezeStatement()用于单个计划;FreezeRelation()用于关系的所有计划;FreezeSchema()用于架构的所有计划;FreezeAll()用于当前命名空间中的所有计划。有相应的解冻方法。

  • 冻结方法可以提升(“冻结”)标记为冻结/升级到冻结/显式的查询计划。通常,可以使用此方法有选择地将适当的冻结/升级计划升级为冻结/显式,然后解冻所有剩余的冻结/升级计划。
  • 解冻方法可以解冻指定范围内的冻结/升级查询计划:命名空间、架构、关系(表)或单个查询。

冻结计划界面

冻结计划界面有两种,用途不同:

  • Management Portal SQL语句界面,用于冻结(或解冻)单个查询的计划。
  • $SYSTEM.SQL.Statement冻结和解冻方法,用于冻结或解冻命名空间、架构、表或单个查询的所有计划。

在Management Portal SQL界面中,选择Execute Query选项卡。编写查询,然后单击显示计划按钮以显示当前查询执行计划。如果计划被冻结,则查询计划部分的第一行是“冻结计划”。

在管理门户SQL界面中,选择SQL语句选项卡。这将显示SQL语句列表。此列表的计划状态列指定解冻、解冻/并行、冻结/显式或冻结/升级。(如果语句没有关联的查询计划,则计划状态列为空。)

可以使用INFORMATION.SCHEMA.STATEMENTS Frozen属性值列出当前命名空间中所有SQL语句的计划状态:UNFRECTED(0)Frozen/EXPLICIT(1)Frozen/Upgrade(2)UNFORMATED/PARALLEL(3)

要冻结或解冻计划,请在SQL语句文本列中选择SQL语句。这将显示“SQL语句详细信息”框。在此框的底部显示对帐单文本和查询计划。如果计划未冻结,则这些横断面的背景颜色为绿色,如果计划已冻结,则背景颜色为蓝色。在其正上方的对帐单操作下,可以根据需要选择冻结计划或解冻计划按钮。然后选择关闭。

  • 冻结计划按钮:单击此按钮将冻结此语句的查询优化计划。冻结计划并编译该SQL语句时,SQL编译将使用冻结的计划信息并跳过查询优化阶段。
  • 解冻计划按钮:点击该按钮将删除该语句冻结的计划,该语句的新编译将进入查询优化阶段,以确定要使用的最佳计划。

还可以使用$SYSTEM.SQL.Statement冻结和解冻方法冻结或解冻一个或多个计划。通过指定适当的方法,可以指定冻结或解冻操作的范围:单个计划的FreezeStatement();关系的所有计划的FreezeRelation();架构的所有计划的FreezeSchema();当前命名空间中的所有计划的FreezeAll()。有相应的解冻方法。

权限

用户只能查看他们具有EXECUTE权限的那些SQL语句。这既适用于Management Portal SQL语句列表,也适用于INFORMATION.SCHEMA.STATEMENTS类查询。

管理门户SQL语句访问要求对%Development资源具有“USE”权限。任何可以在管理门户中看到SQL语句的用户都可以冻结或解冻该语句。

对于SQL语句的目录访问,如果您具有执行该语句的权限或对%Development资源具有“Use”权限,则可以看到这些语句。

对于$SYSTEM.SQL.Statement冻结或解冻方法调用,必须对%Developer资源拥有“U”权限。

冻结计划不同

如果计划被冻结,可以确定解冻该计划是否会导致不同的计划,而无需实际解冻该计划。此信息可以帮助您确定哪些SQL语句值得使用%NOFPLAN进行测试,以确定解冻计划是否会带来更好的性能。

可以使用INFORMATION.SCHEMA.STATEMENTSFrozenDifferent属性列出当前命名空间中此类型的所有冻结计划。

冻结的计划可能会因以下任一操作而与当前计划不同:

  • 重新编译该表或该表引用的表
  • 使用SetMapSelecability()激活或停用索引
  • 在表上运行TuneTable
  • 升级InterSystems软件版本

重新编译会自动清除现有的缓存查询。对于其他操作,必须手动清除现有缓存查询才能使新查询计划生效。

这些操作可能会也可能不会产生不同的查询计划。有两种方法可以确定它们是否这样做:

  • 手工检查个别冻结计划
  • 每天自动扫描所有冻结计划

如果计划尚未由这两个操作中的任何一个检查,或者计划未冻结,则列出新计划的SQL语句列为空。解冻选中的冻结计划会将新建计划列重置为空。

手动冻结计划检查

在冻结计划的SQL语句详细资料页的顶部有一个检查冻结按钮。按此按钮将显示解冻不同计划复选框。如果选中此框,则解冻计划将导致不同的查询计划。

对冻结计划执行此检查冻结测试后:

  • 如果选中解冻计划不同框,则列出新计划的SQL语句列包含“1”。这表明解冻计划将导致不同的计划。
  • 如果未选中解冻计划不同框,则列出新计划的SQL语句列将包含“0”。这表明解冻计划不会产生不同的计划。
    • 已冻结的缓存查询的New Plan为“0”;清除缓存查询,然后解冻该计划会导致SQL语句消失。
    • 已冻结的Natural查询在New Plan列中为空。

执行此测试后,检查冻结按钮消失。如果要重新测试冻结的计划,请选择刷新页面按钮。这将重新显示检查冻结按钮。

日冻结计划自动检查

InterSystems SQL每晚2:00自动扫描SQL语句清单中的所有冻结语句。这次扫描最多持续一个小时。如果扫描未在一小时内完成,系统会记下它停止的位置,并从该点继续进行下一次每日扫描。可以使用管理门户监视此每日扫描或强制其立即扫描:选择系统操作、任务管理器、任务计划,然后选择扫描冻结计划任务。

此扫描检查所有冻结的计划:

  • 如果冻结的计划具有与当前版本相同的InterSystems软件版本,InterSystems IRIS®Data Platform将计算两个计划的引用表和时间戳的散列,以创建可能已更改的内部计划列表。对于这个子集,它然后执行两个计划的逐个字符串比较,以确定哪些计划实际上不同。如果两个计划之间有任何不同(无论有多小),它都会在列出New Plan列的SQL语句中用“1”标记SQL语句。这表明解冻计划将导致不同的查询计划。
  • 如果冻结的计划具有与当前版本相同的InterSystems IRIS版本,并且两个计划的逐字符串比较完全匹配,则它会将列出新计划的SQL语句列中的SQL语句标记为“0”。这表明解冻计划不会导致不同的查询计划。
  • 如果冻结的计划具有与当前版本(冻结/更新)不同的InterSystems软件版本,InterSystems IRIS将确定对SQL优化器逻辑的更改是否会导致不同的查询计划。如果是,它将用“1”标记“SQL Statements Listing New Plan”列中的SQL语句。否则,它会用“0”标记SQL语句New Plan列。

可以通过调用INFORMATION.SCHEMA.STATEMENTS来检查此扫描的结果。以下示例返回所有冻结计划的SQL语句,指示冻结的计划是否与未冻结的计划不同。请注意,解冻语句可以是Frozen=0Frozen=3

SELECT Frozen,FrozenDifferent,Timestamp,Statement FROM INFORMATION_SCHEMA.STATEMENTS 
WHERE Frozen=1 OR Frozen=2

冻结计划出错

如果语句的计划被冻结,并且计划使用的定义发生了某些更改,从而导致计划无效,则会发生错误。例如,如果从语句PLAN使用的类中删除了索引:

  • 该声明的计划仍处于冻结状态。
  • 在“SQL语句详细信息”页上,“编译设置”区域显示“计划错误”字段。例如,如果查询计划使用索引名indxdob,然后您修改了类定义以删除索引indxdob,则会显示如下消息: Map 'indxdob' not defined in table 'Sample.Mytable', but it was specified in the frozen plan for the query.
  • 在SQL语句详细资料页上,查询计划区域显示由于冻结计划中的错误而无法确定计划。

如果在冻结计划处于错误状态时重新执行查询,则InterSystems IRIS不使用冻结计划。相反,系统会创建一个新的查询计划,该计划将在给定当前定义的情况下工作,并执行查询。此查询计划被分配了与前一个查询计划相同的缓存查询类名。

在计划解冻或修改定义以使计划返回有效状态之前,出错的计划将一直处于错误状态。

如果修改定义以使计划返回有效状态,请转到SQL语句详细资料页,然后按清除错误按钮以确定是否已更正错误。如果更正,计划错误字段将消失;否则将重新显示计划错误消息。如果已更正定义,则不必显式清除计划错误,SQL即可开始使用冻结计划。如果已更正定义,则清除错误按钮会使SQL语句详细资料页的冻结查询计划区域再次显示执行计划。

计划错误可能是 “soft error.”。当计划使用索引,但查询优化器当前无法选择该索引时,可能会出现这种情况,因为SetMapSelecability()已将其可选择性设置为0。这样做可能是为了[重建]索引。当InterSystems IRIS遇到具有冻结计划的语句的软错误时,查询处理器会尝试自动清除错误并使用冻结计划。如果该计划仍然出错,则该计划将再次标记为出错,并且查询执行将尽可能使用最佳计划。

%NOFPLAN关键字

可以使用%NOFPLAN关键字覆盖冻结的计划。包含%NOFPLAN关键字的SQL语句将生成新的查询计划。冻结的计划将保留,但不会使用。这允许测试生成的计划行为,而不会丢失冻结的计划。

DECLARE <cursor name> CURSOR FOR SELECT %NOFPLAN ...  
SELECT %NOFPLAN ....  
INSERT [OR UPDATE] %NOFPLAN ...  
DELETE %NOFPLAN ...  
UPDATE %NOFPLAN 

SELECT语句中,%NOFPLAN关键字只能在查询中的第一个SELECT之后立即使用:它只能与UNION查询的第一个分支一起使用,不能在子查询中使用。%NOFPLAN关键字必须紧跟在SELECT关键字之后,位于DISTINCTTOP等其他关键字之前。

导出和导入冻结计划

可以将SQL语句作为XML格式的文本文件导出或导入。这使可以将冻结的计划从一个位置移动到另一个位置。SQL语句导出和导入包括关联查询计划的编码版本和指示该计划是否冻结的标志。

0
0 144
文章 姚 鑫 · 四月 28, 2021 11m read

第八章 解释SQL查询计划(一)

SQL语句

这个SQL语句列表为每个表提供了SQL查询和其他操作的记录,包括插入、更新和删除。 这些SQL语句链接到一个查询计划,该链接提供冻结该查询计划的选项。

系统为每个SQL DML操作创建一条SQL语句。 这提供了一个按表、视图或过程名称列出的SQL操作列表。 如果更改表定义,可以使用此SQL Statements列表来确定每个SQL操作的查询计划是否会受到此DDL更改的影响,以及/或是否需要修改某个SQL操作。 然后,可以:

  • 确定每个SQL操作使用哪个查询计划。 可以决定使用反映对表定义所做更改的修改后的查询计划。 或者可以冻结当前查询计划,保留在更改表定义之前生成的查询计划。
  • 根据对表定义所做的更改,确定是否对对该表执行SQL操作的例程进行代码更改。

注意:SQL语句是一个SQL例程列表,它们可能会受到表定义更改的影响。 它不应该用作表定义或表数据更改的历史记录。

创建SQL语句操作

下面的SQL操作会创建相应的SQL语句:

数据管理(DML)操作包括对表的查询、插入、更新和删除操作。 每个数据管理(DML)操作(动态SQL和嵌入式SQL)在执行时都会创建一个SQL语句。

  • 动态SQL SELECT命令在准备查询时创建SQL语句。 此外,在管理门户缓存查询列表中创建了一个条目。
  • 嵌入式SQL基于指针的SELECT命令在OPEN命令调用声明的查询时创建SQL语句。管理门户缓存查询列表中不会创建单独的条目。

如果查询引用多个表,则在名称空间的SQL语句中创建一条SQL语句,该语句列出表/视图/过程名列中的所有被引用表,并且对于每个单独的被引用表,该表的SQL语句列表都包含该查询的条目。

SQL语句是在第一次准备查询时创建的。如果多个客户端发出相同的查询,则只记录第一次准备。例如,如果JDBC发出一个查询,然后ODBC发出一个相同的查询,那么SQL语句索引将只有关于第一个JDBC客户端的信息,而不是关于ODBC客户端的信息。

大多数SQL语句都有关联的查询计划。 创建该查询计划时,将解冻该查询计划; 可以随后将该查询计划指定为冻结计划。 带有查询计划的SQL语句包括涉及SELECT操作的DML命令。 下面的“计划状态”部分列出了没有查询计划的SQL语句。

注意:SQL语句只列出SQL操作的最新版本。 除非冻结SQL语句,否则InterSystems IRIS®数据平台将用下一个版本替换它。 因此,在例程中重写和调用SQL代码将导致旧的SQL代码从SQL语句中消失。

其他SQL语句操作

下面的SQL命令执行更复杂的SQL语句操作:

  • CREATE TRIGGER: 在定义触发器的表中,无论是在定义触发器还是在提取触发器时,都不会创建SQL语句。 但是,如果触发器对另一个表执行DML操作,那么定义触发器将在被触发器代码修改过的表中创建一个SQL语句。 Location指定在其中定义触发器的表。 在定义触发器时定义SQL语句; 删除触发器将删除SQL语句。 触发触发器不会创建SQL语句。
  • CREATE VIEW 不创建SQL语句,因为没有编译任何内容。 它也不会更改源表的SQL语句的Plan Timestamp。 然而,为视图编译DML命令会为该视图创建一个SQL语句。

List SQL语句

本节介绍使用Management Portal界面列出SQL语句的详细信息。 也可以使用^rINDEXSQL全局返回SQL语句的索引列表。 注意,这个SQL语句List可能包含过时的(不再有效的)List

从Management Portal SQL界面可以列出如下SQL语句:

  • SQL语句选项卡:此选项卡列出名称空间中的所有SQL语句,先按模式排序,然后按每个模式中的表名/视图名排序。此列表仅包括当前用户拥有权限的那些表/视图。如果SQL语句引用多个表,则表/视图/过程名列将按字母顺序列出所有被引用的表。
  • 通过单击列标题,可以按表/视图/过程名、计划状态、位置、SQL语句文本或列表中的任何其他列对SQL语句列表进行排序。这些可排序列使能够快速查找,例如,所有冻结计划(计划状态)、所有缓存查询(位置)或最慢的查询(平均时间)。
  • 可以使用此选项卡提供的Filter选项将列出的SQL语句缩小到指定的子集。 指定的筛选器字符串筛选SQL语句列表中的所有数据,最有用的是模式或模式。 表名、例程位置或SQL语句文本中找到的子字符串。 过滤字符串不区分大小写,但必须紧跟语句文本标点空格(name , age, not name,age)。 如果查询引用了多个表,如果它选择了表/视图/过程名称列中的任何引用表,则Filter包括SQL语句。 过滤选项是用户自定义的。
  • 最大行选项默认为1,000。 最大值为10,000。 最小值为10。 要列出超过10,000条SQL语句,请使用INFORMATION_SCHEMA.STATEMENTS。 页面大小和最大行选项是用户自定义的。
  • Catalog Details选项卡:选择一个表并显示其Catalog详细信息。 此选项卡提供了一个表的SQL语句按钮,用于显示与该表关联的SQL语句。 注意,如果一个SQL语句引用了多个表,那么它将在表的SQL语句列表中列出每个被引用的表,但只有当前选择的表在表名列中列出。

通过单击列标题,可以根据列表的任何列对表的SQL语句列表进行排序。

可以使用SQLTableStatements()目录查询或INFORMATION_SCHEMA。 语句,列出根据各种条件选择的SQL语句,如下面的查询SQL语句中所述。

列表列

SQL语句选项卡列出名称空间中的所有SQL语句。目录详细信息选项卡表的SQL语句按钮列出了所选表的SQL语句。这两个列表都包含以下列标题:

  • #:列表行的顺序编号。这些数字与特定的SQL语句没有关联。
  • 表/视图/过程名:限定的SQL表(或视图或过程)名:schema.name。如果SQL语句查询引用了多个表或视图,则所有这些表或视图都会在此处列出。
  • 计划状态:请参阅下面的计划状态。
  • 新计划:见“冻结计划”一章中不同的新计划。
  • 自然查询:请参阅下面的语句详细信息部分。
  • 计数:请参阅下面的性能统计数据。
  • 平均计数:请参阅下面的性能统计数据。
  • 总时间:请参阅下面的性能统计数据。
  • 平均时间:请参阅下面的性能统计数据。
  • 标准开发人员:请参阅下面的性能统计数据。
  • Location(S):编译查询的位置,例程名称(对于嵌入式SQL)或缓存查询名称(对于动态SQL)。如果包名为%sqlcq,则SQL语句为缓存查询。
  • SQL语句文本:规范化格式的SQL语句文本(截断为128个字符),可能与以下SQL语句文本中指定的命令文本不同。

计划状态

计划状态列出以下内容之一:

  • 解冻Unfrozen:未冻结,可冻结。
  • 解冻/平行Unfrozen/Parallel::未冻结,不能冻结。
  • 冻结/显式Frozen/Explicit:由用户动作冻结,可以解冻。
  • Frozen/Upgrade:被InterSystems IRIS版本升级冻结,可以解冻。
  • blank:没有关联的查询计划:
    • INSERT... VALUES() 命令创建的SQL语句没有关联的查询计划,因此无法解冻或冻结(计划状态列为空)。尽管此SQL命令不会生成查询计划,但它在SQL语句中的列表仍然很有用,因为它允许快速定位针对该表的所有SQL操作。例如,如果向表中添加一列,则可能需要找出该表的所有SQL插入的位置,以便可以更新这些命令以包括此新列。
    • 基于游标的UPDATEDELETE命令没有关联的查询计划,因此不能解冻或冻结(“计划状态”列为空)。对已声明的游标执行OPEN命令会生成一条带有关联查询计划的SQL语句。使用该游标的嵌入式SQL语句(FETCH cursor, UPDATE...WHERE CURRENT OF cursor, DELETE...WHERE CURRENT OF cursor, and CLOSE cursor)不生成单独的SQL语句。即使基于游标的UPDATEDELETE不会产生查询计划,但SQL语句中列出的查询计划仍然很有用,因为它允许快速定位针对该表的所有SQL操作。

SQL语句文本

SQL语句文本通常不同于SQL命令,因为SQL语句生成规范化了字母和空格。 其他差异如下:

如果从Management Portal接口或SQL Shell接口发出查询,所得到的SQL语句与在SELECT语句前面加上DECLARE QRS CURSOR FOR(其中“QRS”可以是各种生成的游标名称)的查询不同。 这允许语句文本与Dynamic SQL缓存的查询相匹配。

如果SQL命令指定了一个非限定的表或视图名,那么生成的SQL语句将使用模式搜索路径(如果提供了DML)或默认模式名来提供模式。

SQL语句文本在1024个字符之后被截断。 要查看完整的SQL语句文本,请显示SQL语句详细信息。

一个SQL命令可能会产生多个SQL语句。 例如,如果一个查询引用一个视图,SQL Statements将显示两个语句文本,一个列在视图名称下,另一个列在基础表名称下。 冻结任意一条语句都会导致两个语句的Plan State为Frozen。

当通过xDBC准备SQL语句时,如果需要这些选项来生成语句索引散列,则SQL语句生成会向语句文本添加SQL Comment Options (# Options)。 如下面的例子所示:

DECLARE C CURSOR FOR SELECT * INTO :%col(1) , :%col(2) , :%col(3) , :%col(4) , :%col(5) FROM SAMPLE . COMPANY /*#OPTIONS {"xDBCIsoLevel":0} */ 

陈旧的SQL语句

删除与SQL语句关联的例程或类时,不会自动删除SQL语句列表。这种类型的SQL语句列表称为陈旧。由于访问此历史信息以及与SQL语句相关联的性能统计信息通常很有用,因此这些过时的条目将保留在管理门户SQL语句列表中。

可以使用Clean Stale(清除陈旧)按钮删除这些陈旧条目。清除陈旧删除关联例程或类(表)不再存在或不再包含SQL语句查询的所有非冻结SQL语句。清除陈旧不会删除冻结的SQL语句。

可以使用$SYSTEM.SQL.Statement.Clean()方法执行相同的清除陈旧操作。

如果删除与SQL语句关联的表(持久化类),则会修改表/视图/过程名称列,如下例所示:SAMPLE.MYTESTTABLE - Deleted??; ;已删除表的名称将转换为全部大写字母,并标记为“DELETED??”。或者,如果SQL语句引用了多个表:SAMPLE.MYTESTTABLE - Deleted?? Sample.Person.

  • 对于动态SQL查询,删除表时Location列为空,因为与该表关联的所有缓存查询都已自动清除。CLEAN STALE删除SQL语句。
  • 对于嵌入式SQL查询,Location列包含用于执行查询的例程的名称。当更改例程使其不再执行原始查询时,位置列为空。CLEAN STALE删除SQL语句。删除查询使用的表时,该表被标记“Deleted??”;Clean Stale不会删除SQL语句。

注:系统任务在所有名称空间中每小时自动运行一次,以清除任何可能过时或具有过时例程引用的SQL语句的索引。执行此操作是为了维护系统性能。此内部清理不会反映在管理门户SQL语句列表中。可以使用管理门户监视此每小时一次的清理或强制其立即执行。要查看此任务上次完成和下次调度的时间,请依次选择系统操作、任务管理器、任务调度,然后查看清理SQL语句索引任务。可以单击任务名称查看任务详细信息。在Task Details(任务详细信息)显示中,可以使用Run(运行)按钮强制立即执行任务。请注意,这些操作不会更改SQL语句清单;必须使用Clean Stale来更新SQL语句清单。

数据管理(DML)SQL语句

创建SQL语句的数据管理语言(DML)命令包括:INSERTUPDATEINSERTUPDATEDELETETRUNCATE TABLESELECTOPEN CURSOR(用于声明的基于游标的SELECT)。可以使用动态SQL或嵌入式SQL来调用DML命令。可以为表或视图调用DML命令,InterSystems IRIS将创建相应的SQL语句。

注意:系统在准备动态SQL或打开嵌入式SQL游标时(而不是在执行DML命令时)创建SQL语句。SQL语句时间戳记录此SQL代码调用的时间,而不是查询执行的时间(或是否)。因此,SQL语句可能表示从未实际执行的表数据更改。

准备动态SQL DML命令将创建相应的SQL语句。与此SQL语句关联的位置是缓存查询。动态SQL是在从管理门户SQL界面、SQL Shell界面执行SQL或从.txt文件导入时准备的。清除未冻结的缓存查询会将相应的SQL语句标记为清除陈旧删除。清除冻结的缓存查询会删除相应SQL语句的位置值。解冻SQL语句会将其标记为Clean Stale删除。

执行非游标嵌入式SQL数据管理语言(DML)命令将创建相应的SQL语句。每个嵌入式SQL DML命令都会创建相应的SQL语句。如果一个例程包含多个嵌入式SQL命令,则每个嵌入式SQL命令都会创建一个单独的SQL语句。(某些嵌入式SQL命令会创建多条SQL语句。)。SQL语句清单的Location列指定包含嵌入式SQL的例程。通过这种方式,SQL语句维护每个嵌入式SQL DML命令的记录。

打开基于游标的嵌入式SQL数据管理语言(DML)例程将创建带有查询计划的SQL语句。 关联的嵌入式SQL语句(FETCH游标、CLOSE游标)不会生成单独的SQL语句。 在FETCH游标之后,一个关联的UPDATE table WHERE CURRENT OF cursor 或DELETE FROM table WHERE CURRENT OF cursor会生成一个单独的SQL语句,但不会生成单独的Query Plan

插入文字值的INSERT命令将创建一个“计划状态”列为空的SQL语句。 由于该命令不会创建查询计划,因此无法冻结SQL语句。

select命令

调用查询将创建相应的SQL语句。 它可以是一个简单的SELECT操作,也可以是一个基于指针的SELECT/FETCH操作。 可以对表或视图发出查询。

  • 包含JOIN的查询为每个表创建相同的SQL语句。 Location是清单中存储的每个表的相同查询。 如SQL语句详细信息例程和关系部分所述,该语句使用以下关系列出所有表。
  • 包含选择项子查询的查询为每个表创建相同的SQL语句。 Location是清单中存储的每个表的相同查询。 如SQL语句详细信息例程和关系部分所述,该语句使用以下关系列出所有表。
  • 引用外部(链接)表的查询不能被冻结。
  • 一个包含FROM子句%PARALLEL关键字的查询可以创建多个SQL语句。 你可以通过调用来显示这些生成的SQL语句:

image

这将显示包含原始查询的语句哈希的Statement列和包含生成的查询版本的语句哈希的ParentHash列。

%PARALLEL查询的SQL语句的计划状态为“未冻结/并行”,不能被冻结。

  • 不包含FROM子句(因此不引用任何表)的查询仍然创建SQL语句。 例如:SELECT $LENGTH('this string')创建一个SQL语句,表列值%TSQL_sys.snf
0
0 154
文章 姚 鑫 · 四月 27, 2021 5m read

第七章 解释SQL查询计划

本章介绍由ShowPlan生成的InterSystems SQL查询访问计划中使用的语言和术语。

存储在映射中的表

SQL表存储为一组映射。 每个表都有一个包含表中所有数据的主映射; 表还可以有其他的映射,如索引映射和位图。 每个映射可以被描绘成一个多维全局,其中一些字段的数据在一个或多个下标中,其余字段存储在节点值中。 下标控制要访问的数据。

  • 对于主映射,RowIDIDKEY字段通常用作映射下标。
  • 对于索引映射,通常将其他字段用作前导下标,将RowID/IDKEY字段用作附加的较低级别的下标。
  • 对于位图,可以将位图层视为附加的RowID下标级别。但是,位图只能用于为正整数的RowID

发展计划

编译SQL查询会生成一组指令来访问和返回查询指定的数据。 这些指令表示为. int例程中的ObjectScript代码。

指令及其执行顺序受到SQL编译器中有关查询中涉及的表的结构和内容的数据的影响。 编译器尝试使用表大小和可用索引等信息,以使指令集尽可能高效。

查询访问计划(ShowPlan)是对结果指令集的可读翻译。 查询的作者可以使用这个查询访问计划来查看将如何访问数据。 虽然SQL编译器试图最有效地利用查询指定的数据,但有时查询的作者对存储的数据的某些方面的了解要比编译器清楚得多。 在这种情况下,作者可以利用查询计划修改原始查询,为查询编译器提供更多的信息或更多的指导。

阅读计划

“ShowPlan”的结果是一系列关于访问和显示查询中指定的数据的处理的语句。 下面提供了关于如何解释ShowPlan语句的信息。

访问映射

一个查询计划可以访问多个表。 当访问一个表时,计划可以访问单个映射(索引或主映射)、两个映射(索引映射后面跟着主映射),或者,对于多索引计划,可以访问多个映射。

在通过映射访问数据时,计划指示使用的下标。 它还指示实际的下标值是什么:一个给定值、一组给定值、一个值范围,或该下标在表中显示的所有值。 选择哪一个取决于查询中指定的条件。 显然,访问单个或几个下标值要比访问该下标级别上的所有值快得多。

条件和表达式

当查询运行时,将测试查询指定的各种条件。 除了前面提到的某些限制下标的条件外,ShowPlan输出没有显式地指示条件的测试。 尽早测试条件总是最好的。 测试各种条件的最佳地点可以从计划细节中推断出来。

类似地,ShowPlan不详细描述表达式和子表达式的计算。 除了简单之外,主要原因是在大多数数据库环境中,表和索引访问构成了处理的更重要方面; 检索表数据的成本占总体查询成本的主要地位,因为磁盘访问速度仍然比CPU处理慢几个数量级。

循环

当访问一个表中的数据时,经常需要迭代地检查多个行。 这样的访问是通过一个循环来指示的。 每一次传递要执行的指令称为循环体。 它们可以通过缩进直观地显示出来。 涉及多个表的数据库访问通常需要循环中的循环。 在这种情况下,每个循环级别都通过与前一个级别相比的进一步缩进表示。

临时文件

定义

查询计划还可能指示需要构建和使用中间临时文件(TEMP-FILE)。这是本地数组中的“临时”区域。它用于保存临时结果以用于各种目的,如排序。就像映射一样,临时文件有一个或多个下标,可能还有节点数据。

使用

一些临时文件包含处理单个表的数据。在这种情况下,可以将构建临时文件视为对该表中的数据进行预处理。在读取这样的临时文件之后,可以访问源表的主映射,也可以不访问源表的主映射。在其他情况下,临时文件可能包含处理多个表的结果。在其他情况下,临时文件用于存储分组的聚合值、检查DISTINCT等。

模块

临时文件的构建,以及其他处理,可以委托给一个称为模块的独立工作单元。 每个模块都被命名。 当列出单独的模块时,该计划将指明调用每个模块的位置。 当模块执行结束时,处理将在模块调用之后的下一条语句中继续进行。

发送给处理的查询

对于通过ODBC或JDBC网关连接链接的外部表,该计划显示发送到远程SQL gateway connection的查询文本,以从远程表检索所请求的数据。

对于并行查询处理和分片,该计划显示发送到并行处理或在分片上处理的各种查询。 还将显示用于每个查询的计划。

子查询、连接和联合

给定查询中的一些子查询(和视图)也可以单独处理。 它们的计划在单独的子查询部分中指定。 在计划中没有指明子查询部分被调用的精确位置。 这是因为它们经常作为条件或表达式处理的一部分被调用。

对于指定OUTER JOIN的查询,如果没有找到匹配的行,该计划可能指示可能生成的null行,以满足外部连接语义的要求。

对于UNION,该计划可能指示将来自不同UNION子查询的结果行组合到一个单独的模块中,在该模块中可以对这些结果行进行进一步处理。

计划分析

在分析给定查询的计划时,应用程序开发人员有时可能会觉得不同的计划会更有效率。 应用程序开发人员有多种方法来影响计划。

首先,计划将受到在包含实际应用程序数据的环境中正确运行调优表的影响。 在类源定义中手动定义一些Tune Table通常计算的值——例如表EXTENTSIZE、字段SELECTIVITY和映射BlockCount——也可以用于实现所需的计划。

此外,分析计划可能表明对类定义的某些更改可能导致更有效的计划,例如:

添加一个索引

在某些情况下(尽管不总是),使用一个临时文件进行预处理可能意味着向原始表添加一个与临时文件具有相同或类似结构的索引将消除构建临时文件的需要。 从查询计划中删除这个处理步骤显然可以使查询运行得更快,但这必须与更新表时维护索引所需的工作量进行平衡。

添加字段到索引数据

当计划显示正在使用的索引,然后是对主映射的访问时,这意味着将查询中使用的主映射字段添加到索引节点数据可能会为该查询生成更快的计划。 同样,这必须与额外的更新时间以及添加到处理使用该索引的其他查询的额外时间进行平衡,因为索引会更大,因此需要更多的读取时间。

添加连接索引

当计划显示以特定顺序连接两个表时(例如,首先检索t1,然后使用连接条件t1.a=t2.b连接到t2),可能相反的表顺序会产生一个更快的计划。例如,如果t2有额外的条件,可以显著限制符合条件的行数。 在这种情况下,在t1上添加一个t1索引。 a将允许这样一个连接顺序。

0
0 166
文章 姚 鑫 · 四月 26, 2021 9m read

第六章 SQL性能分析工具包

本章介绍可用于主动分析特定SQL语句的分析工具。这些工具收集有关这些SQL语句执行的详细信息。使用这些信息,开发人员可以采取措施提高低效SQL语句的性能。

根据请求的详细程度,此活动分析可能会显著增加服务器上的负载。因此,SQL性能分析工具包旨在进行协调一致的代码分析工作。它不是用来连续监视执行代码的。

分析工具界面

SQL性能分析工具包为开发人员和支持专家提供了分析特定SQL语句或语句组的能力。通过在执行特定SQL语句期间使用这些工具,它们可以收集详细信息,这些信息可用于单独或跨活动工作负载分析有问题的语句。

要记录的细节级别是可配置的,最细粒度的设置在模块级别收集信息,为语句的查询计划中的不同“步骤”提供信息。

  • %SYSTEM.SQL.PTools类方法。提供以下方法来收集以下各项的性能统计信息:
    • 整个系统:setSQLStatsFlag()
    • 特定的命名空间:setSQLStatsFlagByNS()
    • 当前进程或JOB:setSQLStatsFlagJob()
    • 指定的进程或JOB:setSQLStatsFlagByPID()`
    • 可以在SELECTINSERTUPDATEDELETE语句中使用%PROFILE关键字(等效于setSQLStatsFlagJob(2))或%PROFILE_ALL关键字(等效于setSQLStatsFlagJob(3))来仅收集该语句的性能分析统计信息。

使用性能分析工具包方法

可以使用%SYSTEM.SQL.PTools类方法执行以下操作:

  • 激活SQL性能统计信息。
  • 获取当前的SQL统计信息设置。
  • 导出收集的SQL性能统计信息。显示或导出到文件。
  • 删除SQL性能统计信息。

本节还包含使用这些方法的程序示例。

注意:%SYSTEM.SQL.PTools类方法是所有新编码的首选类接口。较旧的%SYS.PTools.StatsSQL类方法类似,并将继续受到支持。

激活统计信息收集

可以使用%SYSTEM.SQL.PTools类方法激活统计信息(Stats)代码生成以收集性能统计信息。提供以下方法来收集以下各项的性能统计信息:

  • 整个系统:setSQLStatsFlag()
  • 特定的命名空间:setSQLStatsFlagByNS()
  • 当前进程或JOB:setSQLStatsFlagJob()
  • 指定的进程或JOB:setSQLStatsFlagByPID()。如果第一个参数未指定,或指定为$JOB或空字符串(“”),则调用setSQLStatsFlagJob()。因此,SET SQLStatsFlag=$SYSTEM.SQL.SetSQLStatsFlagByPID($JOB,3)等同于SET SQLStatsFlag=$SYSTEM.SQL.SetSQLStatsFlagJob(3).

这些方法采用整数操作选项。它们返回一个冒号分隔的字符串,其第一个元素是先前的统计操作选项。可以使用getSQLStatsFlag()getSQLStatsFlagByPID()方法确定当前设置。

可以从ObjectScript或SQL调用这些方法,如以下示例所示:

  • 从ObjectScript:rtn=##class(%SYSTEM.SQL.PTools).setSQLStatsFlag(2,,8)
  • 从SQL : SELECT %SYSTEM_SQL.PTools_setSQLStatsFlag(2,,8)

操作选项

对于setSQLStatsFlag()setSQLStatsFlagByNS(),可以指定以下Action选项之一:0关闭统计信息代码生成;1打开所有查询的统计信息代码生成,但不收集统计信息(默认设置);2只记录查询外部循环的统计信息(在查询打开和关闭时收集统计信息);3记录查询的所有模块级别的统计信息。模块可以嵌套。如果是,则主模块统计信息是包含数字,即完整查询的总体结果。

对于setSQLStatsFlagJob()setSQLStatsFlagByPID(),操作选项略有不同。它们是:-1关闭此job的统计信息;0使用系统设置值。选项1、2和3与setSQLStatsFlag()相同,并覆盖系统设置。默认值为0。

要收集SQL Stats数据,需要在启用统计代码生成的情况下编译(准备)查询(选项1,默认设置):

  • 从0到1:更改SQL Stats选项后,需要编译包含SQL的例程和类以执行统计代码生成。对于xDBC和动态SQL,必须清除缓存查询以强制重新生成代码。
  • 要从1变为2:只需更改SQL Stats选项即可开始收集统计信息。这使可以在运行的生产环境中启用SQL性能分析,并将中断降至最低。
  • 从1到3(或从2到3):更改SQL Stats选项后,需要编译包含SQL的例程和类,以记录所有模块级别的统计信息。对于xDBC和动态SQL,必须清除缓存查询以强制重新生成代码。选项3通常仅用于非生产环境中已识别的性能较差的查询。
  • 从1、2或3变为0:要关闭统计代码生成,不需要清除缓存的查询。

收集选项

如果操作选项为2或3,则在调用这些方法之一时,可以指定收集选项值,以指定要收集的性能统计信息。默认情况下收集所有统计信息。

通过将与要收集的每种类型的统计信息相关联的整数值相加,可以指定收集选项。默认值为15(1+2+4+8)

这些方法将此Collect选项的前值作为第二个冒号分隔的元素返回。可以使用getSQLStatsFlag()getSQLStatsFlagByPID()方法确定当前设置。默认情况下,收集所有统计信息,返回15作为第二个元素值。

终止选项

统计数据收集将继续,直到终止。默认情况下,收集将无限期继续,直到通过发出另一个setSQLStatsFlag[nnn]()方法终止收集。或者,如果Action选项是1、2或3,可以指定setSQLStatsFlag[nnn]()Terminate选项,可以是经过的时间段(以分钟为单位),也可以是指定的时间戳。然后指定在该时间段过后重新设置操作选项。例如,字符串“M:120:1”将M(已用分钟)设置为120分钟,结束时操作选项将重置为1。所有其他选项将重置为适用于该操作选项的默认值。

这些方法将此终止选项值的先前值作为第五个冒号分隔的元素作为编码值返回。请参见获取统计信息设置。

获取统计信息设置

第一个冒号分隔值是操作选项设置。第二个冒号分隔值是Collect选项。第三个和第四个冒号分隔值用于特定于名称空间的统计信息收集。第五个冒号分隔值编码终止选项。第六个冒号分隔值指定FlagType:0=系统标志,1=进程/作业标志。

  KILL
  DO ##class(%SYSTEM.SQL.PTools).clearSQLStatsALL("USER")
  DO ##class(%SYSTEM.SQL.PTools).setSQLStatsFlagByNS("USER",3,,7,"M:5:1")
DisplaySettings
  SET SQLStatsFlag = ##class(%SYSTEM.SQL.PTools).getSQLStatsFlag(0,.ptInfo)
  WRITE "ptInfo array of SQL Stats return value:",!
  ZWRITE ptInfo,SQLStatsFlag

导出查询性能统计信息

可以使用%SYSTEM.SQL.PTools的exportStatsSQL()方法将查询性能统计信息导出到文件。此方法用于将统计数据从%SYSTEM.SQL.PTools类导出到文件。

可以调用exportSQLStats(),如以下示例所示:

  • 从对象脚本:SET status=##class(%SYSTEM.SQL.PTools).exportSQLStats("$IO")(默认为格式T)。
  • 从 SQL: CALL %SYSTEM_SQL.PTools_exportSQLStats('$IO') (默认为格式H).

如果不指定文件名参数,此方法将导出到当前目录。默认情况下,此文件名为PT_StatsSQL_exportSQLStats_,后跟当前本地日期和时间YYYYMMDD_HHMMSS。可以指定$IO将数据输出到终端或管理门户显示。如果指定FileName参数,此方法将在当前命名空间的Mgr子目录或您指定的路径位置中创建一个文件。此导出仅限于当前命名空间中的数据。

可以将输出文件格式指定为P(文本)、D(逗号分隔数据)、X(XML标记)、H(HTML标记)或Z(用户定义分隔符)。

默认情况下,此方法导出查询性能统计信息。可以指定将其改为导出SQL查询文本或SQL查询计划数据,如以下示例所示:

  • 查询文本: CALL %SYSTEM_SQL.PTools_exportStatsSQL('$IO',,0,1,0)
  • 查询计划:CALL %SYSTEM_SQL.PTools_exportStatsSQL('$IO',,0,1,1)

ExportSQLStats()通过去除注释并执行文字替换来修改查询文本。

ExportSQLQuery()可以返回相同的查询文本和查询计划数据。

统计值

返回以下统计信息:

  • RowCount-给定查询在主模块中返回的总行数。
  • RunCount-自上次编译/准备查询以来已运行的总次数。
  • ModuleCount-在查询运行期间输入给定模块的总次数。
  • TimeToFirstRow-将给定查询的第一个结果集行返回到主模块所用的总时间。
  • TimeSpent-给定查询在给定模块中花费的总时间。
  • GlobalRefs-在给定模块中为给定查询完成的全局引用总数。
  • LinesOfCode-在给定模块中为给定查询执行的ObjectScript代码的总行数。
  • DiskWait(也称为磁盘延迟)-在给定模块中等待给定查询的磁盘读取所花费的总毫秒数。

删除查询性能统计信息

可以使用clearSQLStatsALL()方法删除性能统计信息。默认情况下,它删除为当前命名空间中的所有例程收集的统计信息。可以指定不同的命名空间,和/或将删除限制为特定例程。

性能统计示例

以下示例收集当前进程准备的查询主模块(操作选项2)的性能统计信息,然后使用exportSQLStats()将性能统计信息显示给终端。

  DO ##class(%SYSTEM.SQL.PTools).clearSQLStatsALL()
  DO ##class(%SYSTEM.SQL.PTools).setSQLStatsFlagJob(2)
  SET myquery = "SELECT TOP 5 Name,DOB FROM Sample.Person"
  SET tStatement = ##class(%SQL.Statement).%New()
  SET qStatus = tStatement.%Prepare(myquery)
    IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
  SET pStatus = ##class(%SYSTEM.SQL.PTools).exportSQLStats("$IO")
    IF pStatus'=1 {WRITE "Performance stats display failed:"
       DO $System.Status.DisplayError(qStatus) QUIT}

下面的示例收集当前进程准备的查询(操作选项3)的所有模块的性能统计信息,然后从嵌入式SQL调用exportSQLStats()以向终端显示性能统计信息:

  DO ##class(%SYSTEM.SQL.PTools).clearSQLStatsALL()
  DO ##class(%SYSTEM.SQL.PTools).setSQLStatsFlagJob(3)
  SET myquery = "SELECT TOP 5 Name,DOB FROM Sample.Person"
  SET tStatement = ##class(%SQL.Statement).%New()
  SET qStatus = tStatement.%Prepare(myquery)
    IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
  &sql(CALL %SYSTEM_SQL.PTools_exportSQLStats('$IO'))

以下示例收集当前进程准备的查询主模块(操作选项2)的性能统计信息,然后使用StatsSQLView查询显示这些统计信息:

  DO ##class(%SYSTEM.SQL.PTools).clearSQLStatsALL()
  DO ##class(%SYSTEM.SQL.PTools).setSQLStatsFlagJob(2)
  SET myquery = "SELECT TOP 5 Name,DOB FROM Sample.Person"
  SET tStatement = ##class(%SQL.Statement).%New()
  SET qStatus = tStatement.%Prepare(myquery)
   IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
GetStats
  SET qStatus = tStatement.%Prepare("SELECT * FROM %SYS_PTools.StatsSQLView")
   IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
  SET rsstats = tStatement.%Execute()
  DO rsstats.%Display()
  WRITE !!,"End of SQL Statistics"

以下示例收集用户命名空间中所有查询的所有模块(操作选项3)的性能统计信息。当统计信息收集时间在1分钟后到期时,它将重新设置为操作选项2,并且所有命名空间的收集范围默认为15(所有统计信息):

  DO ##class(%SYSTEM.SQL.PTools).clearSQLStatsALL("USER")
  DO ##class(%SYSTEM.SQL.PTools).setSQLStatsFlagByNS("USER",3,,7,"M:1:2")
  SET myquery = "SELECT TOP 5 Name,DOB FROM Sample.Person"
  SET tStatement = ##class(%SQL.Statement).%New()
  SET qStatus = tStatement.%Prepare(myquery)
   IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
GetStats
  SET qStatus = tStatement.%Prepare("SELECT * FROM %SYS_PTools.StatsSQLView")
   IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
  SET rsstats = tStatement.%Execute()
  DO rsstats.%Display()
  WRITE !!,"End of SQL Statistics",!
TerminateResetStats
  WRITE "returns:  ",##class(%SYSTEM.SQL.PTools).getSQLStatsFlag(),!
  HANG 100
  WRITE "reset to: ",##class(%SYSTEM.SQL.PTools).getSQLStatsFlag()
0
0 118
文章 姚 鑫 · 四月 25, 2021 10m read

第五章 优化查询性能(四)

注释选项

可以在SELECTINSERTUPDATEDELETETRUNCATE表命令中为查询优化器指定一个或多个注释选项。 注释选项指定查询优化器在编译SQL查询期间使用的选项。 通常,注释选项用于覆盖特定查询的系统范围默认配置。

语法

语法/*#OPTIONS */(在/*#之间没有空格)指定了一个注释选项。 注释选项不是注释; 它为查询优化器指定一个值。 注释选项使用JSON语法指定,通常是“key:value”对,例如: /*#OPTIONS {"optionName":value} */。 支持更复杂的JSON语法,比如嵌套值。

注释选项不是注释; 除了JSON语法之外,它可能不包含任何文本。 包含非json文本在/* ... */分隔符导致SQLCODE -153错误。 InterSystems SQL不验证JSON字符串的内容。

#OPTIONS关键字必须用大写字母指定。 JSON的大括号语法中不应该使用空格。 如果SQL代码用引号括起来,比如动态SQL语句,JSON语法中的引号应该是双引号。 例如:myquery="SELECT Name FROM Sample.MyTest /*#OPTIONS {""optName"":""optValue""} */".

可以在SQL代码中任何可以指定注释的地方指定/*#OPTIONS */ comment选项。 在显示的语句文本中,注释选项总是作为注释显示在语句文本的末尾。

你可以在SQL代码中指定多个/*#OPTIONS */ comment选项。 它们按照指定的顺序显示在返回的语句文本中。 如果为同一个选项指定了多个注释选项,则使用last指定的选项值。

以下的注释选项被记录在案:

  • /*#OPTIONS {"BiasAsOutlier":1} */

  • /*#OPTIONS {"DynamicSQLTypeList":"10,1,11"}

  • /*#OPTIONS {"NoTempFile":1} */

显示

/*#OPTIONS */ comment选项显示在SQL语句文本的末尾,而不管它们是在SQL命令中指定的位置。 一些显示的/*#OPTIONS */ comment选项没有在SQL命令中指定,而是由编译器的预处理器生成的。 例如 /*#OPTIONS {"DynamicSQLTypeList": ...} */

/*#OPTIONS */ comment选项显示在Show Plan语句文本、缓存的查询查询文本和SQL语句语句文本中。

为仅在/*#OPTIONS */ comment选项中不同的查询创建一个单独的缓存查询。

并行查询处理

并行查询提示指示系统在多处理器系统上运行时执行并行查询处理。 这可以极大地提高某些类型查询的性能。 SQL优化器确定一个特定的查询是否可以从并行处理中受益,并在适当的时候执行并行处理。 指定并行查询提示并不强制对每个查询进行并行处理,只强制那些可能从并行处理中受益的查询。 如果系统不是多处理器系统,则此选项无效。 要确定当前系统上的处理器数量,请使用 %SYSTEM.Util.NumberOfCPUs() 方法。

可以通过两种方式指定并行查询处理:

  • 在系统范围内,通过设置auto parallel选项。
  • 在每个查询的FROM子句中指定%PARALLEL关键字。

并行查询处理应用于SELECT查询。 它不应用于插入、更新或删除操作。

系统范围的并行查询处理

可以使用以下选项之一来配置系统范围的自动并行查询处理:

  • 在管理门户中选择System Administration,然后选择Configuration,然后选择SQL和对象设置,最后选择SQL。 查看或更改在单个进程中执行查询复选框。 注意,该复选框的默认值是未选中的,这意味着并行处理在默认情况下是激活的。
  • 调用$SYSTEM.SQL.Util.SetOption()方法,如下: SET status=$SYSTEM.SQL.Util.SetOption("AutoParallel",1,.oldval). 默认值是1(自动并行处理激活)。 要确定当前的设置,调用$SYSTEM.SQL.CurrentSettings(),它会显示为%PARALLEL选项启用自动提示。

注意,更改此配置设置将清除所有名称空间中的所有缓存查询。

当激活时,自动并行查询提示指示SQL优化器对任何可能受益于这种处理的查询应用并行处理。 在IRIS 2019.1及其后续版本中,自动并行处理是默认激活的。 从IRIS 2018.1升级到IRIS 2019.1的用户需要明确激活自动并行处理。

SQL优化器用于决定是否对查询执行并行处理的一个选项是自动并行阈值。 如果激活了系统范围的自动并行处理(默认),可以使用$SYSTEM.SQL.Util.SetOption()方法将自动并行处理的优化阈值设置为整数值,如下所示: SET status=$SYSTEM.SQL.Util.SetOption("AutoParallelThreshold",n,.oldval)n阈值越高,将此特性应用于查询的可能性就越低。 此阈值用于复杂的优化计算,但可以将此值视为必须驻留在已访问映射中的元组的最小数量。 默认值为3200。 最小值为0。 要确定当前的设置,调用$SYSTEM.SQL.CurrentSettings(),它显示%PARALLEL选项的自动提示阈值。

当自动并行处理被激活时,在分片环境中执行的查询将始终使用并行处理执行,而不管并行阈值是多少。

针对特定查询的并行查询处理

可选的%PARALLEL关键字在查询的FROM子句中指定。 它建议跨系统的IRIS使用多个处理器(如果适用的话)并行处理查询。 这可以显著提高使用一个或多个COUNTSUMAVGMAXMIN聚合函数和/groupby子句的查询的性能,以及许多其他类型的查询。 这些通常是处理大量数据并返回小结果集的查询。 例如,SELECT AVG(SaleAmt) FROM %PARALLEL User.AllSales GROUP BY Region都可使用并行处理。

仅指定聚合函数、表达式和子查询的“一行”查询执行并行处理,无论是否带有GROUP BY子句。 但是,同时指定单个字段和一个或多个聚合函数的“多行”查询不会执行并行处理,除非它包含GROUP BY子句。 例如,SELECT Name,AVG(Age) FROM %PARALLEL Sample.Person不执行并行处理,但是 SELECT Name,AVG(Age) FROM %PARALLEL Sample.Person GROUP BY Home_State 执行并行处理。

如果在运行时模式下编译指定%PARALLEL的查询,则所有常量都被解释为ODBC格式。

指定%PARALLEL可能会降低某些查询的性能。 在一个有多个并发用户的系统上运行%PARALLEL查询可能会降低整体性能。

在查询视图时可以执行并行处理。 但是,即使显式地指定了%parallel关键字,也不会对指定%VID的查询执行并行处理。

%PARALLEL的子查询

%PARALLEL用于SELECT查询及其子查询。 插入命令子查询不能使用%PARALLEL

当应用于与外围查询相关的子查询时,%PARALLEL将被忽略。 例如:

SELECT name,age FROM Sample.Person AS p 
WHERE 30<(SELECT AVG(age) FROM %PARALLEL Sample.Employee where Name = p.Name)

image

当应用于包含复杂谓词的子查询,或优化为复杂谓词的谓词时,%PARALLEL将被忽略。 被认为复杂的谓词包括FOR SOMEFOR SOME %ELEMENT谓词。

并行查询处理被忽略

无论AUTO PARALLEL选项设置如何,或者FROM子句中是否存在%PARALLEL关键字,某些查询都可能使用线性处理,而不是并行处理。InterSystems IRIS在优化查询后决定是否对该查询使用并行处理,并应用其他查询优化选项(如果指定)。RIS可以确定优化形式的查询不适合并行处理,即使用户指定的形式的查询似乎受益于并行处理。可以使用Show Plan确定InterSystems IRIS是否以及如何对查询进行分区以进行并行处理。

在以下情况下,指定%PARALLEL不会执行并行处理。查询成功执行,没有发出错误,但没有执行并行化:

  • 该查询包含FOR某些谓词。
  • 该查询包含一个TOP子句和一个ORDER BY子句。 这种子句组合优化了不使用并行处理的最快时间到第一行。 添加FROM子句%NOTOPOPT optimization -option关键字可优化以最快速度检索完整结果集。 如果查询不包含聚合函数,%PARALLEL%NOTOPOPT的组合将执行查询的并行处理。
  • 包含左外连接或内连接(其中ON子句不是相等条件)的查询。例如, FROM %PARALLEL Sample.Person p LEFT OUTER JOIN Sample.Employee e ON p.dob > e.dob. 这是因为SQL优化将这种类型的连接转换为完整的外部连接。 对于完整的外部连接,%PARALLEL将被忽略。
  • %PARALLEL%INORDER优化不能同时使用; 如果两者都指定,%PARALLEL将被忽略。
  • 查询引用一个视图并返回一个视图ID (%VID)。
  • 如果表有BITMAPEXTENT索引,COUNT(*)不使用并行处理。
  • %PARALLEL用于使用标准数据存储定义的表。 可能不支持将其与自定义存储格式一起使用。 %PARALLEL不支持全局临时表或具有扩展全局引用存储的表。
  • %PARALLEL用于可以访问一个表的所有行的查询,使用行级安全(ROWLEVELSECURITY)定义的表不能执行并行处理。
  • %PARALLEL用于存储在本地数据库中的数据。 它不支持映射到远程数据库的全局节点。

共享内存的考虑

对于并行处理,IRIS支持多个进程间队列(IPQ)。 每个IPQ处理单个并行查询。 它允许并行工作单元子流程将数据行发送回主流程,这样主流程就不必等待工作单元完成。 这使得并行查询能够尽可能快地返回第一行数据,而不必等待整个查询完成。 它还改进了聚合函数的性能。

并行查询执行使用来自通用内存堆(gmheap)的共享内存。 如果使用并行SQL查询执行,用户可能需要增加gmheap大小。 一般来说,每个IPQ的内存需求是4 x 64k = 256k。 InterSystems IRIS将一个并行SQL查询拆分为可用的CPU核数。 因此,用户需要分配这么多额外的gmheap:

<Number of concurrent parallel SQL requests> x <Number cores> x 256 = <required size increase (in kilobytes) of gmheap>

注意,这个公式不是100%准确的,因为一个并行查询可以产生同样是并行的子查询。 因此,明智的做法是分配比这个公式指定的更多的额外gmheap

分配足够的gmheap失败将导致错误报告给messages.log。 SQL查询可能会失败。 其他子系统尝试分配gmheap时也可能出现其他错误。

要查看一个实例的gmheap使用情况,特别是IPQ使用情况,请在管理门户的主页上选择System Operation,然后选择System usage,然后单击Shared Memory Heap usage链接;

image

要更改通用内存堆或gmheap(有时称为共享内存堆或SMH)的大小,请从管理门户的主页选择“系统管理”,然后是“配置”,然后是“附加设置”,最后是“高级内存”;

image

image

缓存查询注意事项

如果你正在运行一个缓存的SQL查询,使用%PARALLEL,当这个查询被初始化时,你做了一些事情来清除缓存的查询,那么这个查询可能会从一个工人作业报告一个<NOROUTINE>错误。 导致缓存查询被清除的典型情况是调用$SYSTEM.SQL.Purge()或重新编译该查询引用的类。 重新编译类将自动清除与该类相关的任何缓存查询。

如果发生此错误,再次运行查询可能会成功执行。 从查询中删除%PARALLEL可以避免出现此错误。

SQL语句和计划状态

使用%PARALLEL的SQL查询可以产生多条SQL语句。 这些SQL语句的计划状态是Unfrozen/Parallel。 计划状态为“已冻结”/“并行”的查询不能通过用户操作进行冻结。

生成报告

可以使用生成报告工具向InterSystems Worldwide Response Center (WRC) customer support提交查询性能报告,以便进行分析。 可以使用以下任意一种方式从管理门户运行生成报告工具:

  1. 必须首先从WRC获得WRC跟踪号。可以使用每个管理门户页面顶部的Contact按钮从管理门户联系WRC。在WRC编号区域中输入此跟踪编号。可以使用此跟踪编号来报告单个查询或多个查询的性能。
  2. 在“SQL语句”区域中,输入查询文本。右上角将显示一个X图标。可以使用此图标清除SQL语句区。查询完成后,选择保存查询按钮。系统生成查询计划并收集指定查询的运行时统计信息。无论系统范围的运行时统计信息设置如何,生成报告工具始终使用收集选项3:记录查询的所有模块级别的统计信息进行收集。由于在此级别收集统计信息可能需要时间,因此强烈建议您选中“在后台运行保存查询进程”复选框。默认情况下,此复选框处于选中状态。

当后台任务启动时,该工具显示“请等待……”,禁用页面上的所有字段,并显示一个新的视图进程按钮。 单击View Process按钮将在新选项卡中打开Process Details页面。 在流程详细信息页面,您可以查看该流程,并可以“暂停”、“恢复”或“终止”该流程。 进程的状态反映在Save查询页面上。 当流程完成时,当前保存的查询表将被刷新,View process按钮将消失,页面上的所有字段将被启用。

  1. 对每个查询执行步骤2。 每个查询将被添加到当前保存的Queries表中。 注意,该表可以包含具有相同WRC跟踪号的查询,也可以包含具有不同跟踪号的查询。 完成所有查询后,继续步骤4。

对于列出的每个查询,可以选择Details链接。 该链接将打开一个单独的页面,其中显示完整的SQL语句、属性(包括WRC跟踪号和IRIS软件版本),以及包含每个模块的性能统计信息的查询计划。

  • 要删除单个查询,请从“当前保存的查询”表中选中这些查询的复选框,然后单击“清除”按钮。
  • 要删除与WRC跟踪编号关联的所有查询,请从当前保存的查询表中选择一行。WRC编号显示在页面顶部的WRC编号区域。如果您随后单击清除按钮,则对该WRC编号的所有查询都将被删除。
  1. 使用查询复选框选择要报告给WRC的查询。要选择与WRC跟踪编号关联的所有查询,请从当前保存的查询表中选择一行,而不是使用复选框。在这两种情况下,都可以选择Generate Report按钮。生成报告工具创建一个XML文件,其中包括查询语句、具有运行时统计信息的查询计划、类定义以及与每个所选查询相关联的SQL int文件。

如果选择与单个WRC跟踪编号关联的查询,则生成的文件将具有默认名称,如WRC12345.xml。如果选择与多个WRC跟踪编号关联的查询,则生成的文件将具有默认名称WRCMultiple.xml

将出现一个对话框,要求指定保存报告的位置。保存报告后,可以单击Mail to链接将报告发送给WRC客户支持。使用邮件客户端的附加/插入功能附加文件。

0
0 162
公告 Louis Lu · 四月 23, 2021

InterSystems IRIS、IRIS for Health和HealthShare Health Connect的2021.1版本的预览版现已发布。

由于这是一个预览版,我们希望在下个月的通用版本发布之前了解您对这个新版本的体验。请通过开发者社区分享您的反馈,以便我们能够共同打造一个更好的产品。

InterSystems IRIS数据平台2021.1是一个扩展维护(EM)版本。自2020.1(上一个EM版本)以来,在持续交付(CD)版本中增加了许多重要的新功能和改进。请参考2020.2、2020.3和2020.4的发布说明,了解这些内容的概况。

这个版本的增强功能为开发人员提供了更大的自由度,可以用他们选择的语言构建快速和强大的应用程序,并使用户能够通过新的和更快的分析功能更有效地处理大量的信息。

通过InterSystems IRIS 2021.1,客户可以部署InterSystems IRIS Adaptive Analytics,这是一个附加产品,它扩展了InterSystems IRIS,为分析终端用户提供了更强大的易用性、灵活性、可扩展性以及效率,而不管他们选择何种商业智能(BI)工具。它能够定义一个利于分析的业务模型,并通过在后台自主构建和维护临时数据结构,透明地加速针对该模型运行分析查询时的工作负载。

这个版本中的其他重点新功能包括

0
0 498
文章 姚 鑫 · 四月 23, 2021 6m read

第五章 优化查询性能(二)

使用索引

索引通过维护常见请求数据的排序子集,提供了一种优化查询的机制。 确定哪些字段应该被索引需要一些思考:太少或错误的索引和关键查询将运行太慢; 太多的索引会降低插入和更新性能(因为必须设置或更新索引值)。

什么索引

要确定添加索引是否会提高查询性能,请从管理门户SQL接口运行查询,并在性能中注意全局引用的数量。 添加索引,然后重新运行查询,注意全局引用的数量。 一个有用的索引应该减少全局引用的数量。 可以通过在WHERE子句或ON子句条件前使用%NOINDEX关键字来防止使用索引。

应该为联接中指定的字段(属性)编制索引。左外部联接从左表开始,然后查看右表;因此,应该为右表中的字段建立索引。在下面的示例中,应该为T2.f2编制索引:

   FROM Table1 AS T1 LEFT OUTER JOIN Table2 AS T2 ON T1.f1 = T2.f2

内部联接应该在两个ON子句字段上都有索引。

执行“显示计划”,然后找到第一张map。 如果查询计划中的第一个项目是“Read master map”,或者查询计划调用的模块的第一个项目是“Read master map”,则查询的第一个映射是主映射,而不是索引映射。 因为主映射读取数据本身,而不是数据索引,这总是表明查询计划效率低下。 除非表相对较小,否则应该创建一个索引,以便在重新运行该查询时,查询计划的第一个映射表示“读取索引映射”。

应该索引在WHERE子句equal条件中指定的字段。

可能希望索引在WHERE子句范围条件中指定的字段,以及GROUP BYORDER BY子句中指定的字段。

在某些情况下,基于范围条件的索引可能会使查询变慢。如果绝大多数行满足指定的范围条件,则可能会发生这种情况。例如,如果将QUERY子句WHERE Date < CURRENT_DATE 用于大多数记录来自以前日期的数据库,则在DATE上编制索引实际上可能会降低查询速度。这是因为查询优化器假定范围条件将返回相对较少的行数,并针对此情况进行优化。可以通过在范围条件前面加上%noindex来确定是否发生这种情况,然后再次运行查询。

如果使用索引字段执行比较,则比较中指定的字段的排序规则类型应与其在相应索引中的排序规则类型相同。例如,SELECTWHERE子句或联接的ON子句中的Name字段应该与为Name字段定义的索引具有相同的排序规则。如果字段排序规则和索引排序规则之间存在不匹配,则索引可能效率较低或可能根本不使用。

image

image

索引配置选项

以下系统范围的配置方法可用于优化查询中索引的使用:

  • 要将主键用作IDKey索引,请设置$SYSTEM.SQL.Util.SetOption()方法,如下所示 SET status=$SYSTEM.SQL.Util.SetOption("DDLPKeyNotIDKey",0,.oldval). 默认为1
  • 要将索引用于SELECT DISTINCT查询,请设置$SYSTEM.SQL.Util.SetOption()方法,如下所示: SET status=$SYSTEM.SQL.Util.SetOption("FastDistinct",1,.oldval). 默认为1

索引使用情况分析

可以使用以下任一方法按SQL缓存查询分析索引使用情况:

  • 管理门户索引分析器SQL性能工具。
  • %SYS.PTools.UtilSQLAnalysis方法indexUsage()tableScans()tempIndices()joinIndices()outlierIndices()。、

索引分析

可以使用以下任一方法从管理门户分析SQL查询的索引使用情况:

  • 选择系统资源管理器,选择工具,选择SQL性能工具,然后选择索引分析器。
  • 选择系统资源管理器,选择SQL,然后从工具下拉菜单中选择索引分析器。

索引分析器提供当前命名空间的SQL语句计数显示和五个索引分析报告选项。 image

image

image

SQL语句计数

在SQL索引分析器的顶部有一个对命名空间中的所有SQL语句进行计数的选项。按收集SQL语句按钮。SQL索引分析器显示“正在收集SQL语句...”当计票进行时,然后“完成!”当清点完毕后。SQL语句分为三类进行计数:缓存查询计数、类方法计数和类查询计数。这些计数针对整个当前命名空间,不受架构选择选项的影响。

对应的方法是%SYS.PTools.UtilSQLAnalysis类中的getSQLStmts()

可以使用清除语句按钮删除当前命名空间中收集的所有语句。该按钮调用clearSQLStatements()方法。

报告选项

可以检查当前命名空间中选定架构的缓存查询报告,也可以(通过不选择架构)检查当前命名空间中所有缓存查询的报告。可以在此分析中跳过或包括系统类查询、INSERT语句和/或IDKEY索引。“架构选择”和“跳过选项”复选框是用户自定义的。

指数分析报告选项包括:

  • 索引使用:此选项获取当前名称空间中的所有缓存查询,为每个查询生成显示计划,并记录每个查询使用每个索引的次数以及名称空间中所有查询对每个索引的总使用量。这可用于显示未使用的索引,以便可以删除或修改这些索引以使其更有用。结果集从最少使用的索引到最常使用的索引排序。

对应的方法是%SYS.PTools.UtilSQLAnalysis类中的indexUsage()。要导出此方法生成的分析数据,请使用exportIUAnalysis()方法。

  • 使用表扫描的查询:此选项标识当前名称空间中执行表扫描的所有查询。如果可能,应避免表扫描。表扫描并不总是可以避免的,但是如果一个表有大量的表扫描,那么应该检查为该表定义的索引。通常,表扫描列表和临时索引列表会重叠;修复其中一个会删除另一个。结果集按从最大块计数到最小块计数的顺序列出表格。提供了显示计划链接以显示对帐单文本和查询计划。

对应的方法是%SYS.PTools.UtilSQLAnalysis类中的tableScans()。要导出此方法生成的分析数据,请使用exportTSAnalysis()方法。

  • 带临时索引的查询:此选项标识当前名称空间中构建临时索引以解析SQL的所有查询。有时,使用临时索引会有所帮助并提高性能,例如,基于范围条件构建一个小索引,然后InterSystems IRIS可以使用该索引按顺序读取主映射。有时,临时索引只是不同索引的子集,可能非常有效。其他情况下,临时索引会降低性能,例如,扫描master may以在具有条件的特性上构建临时索引。这种情况表明缺少所需的索引;应该向与临时索引匹配的类添加索引。结果集按从最大块计数到最小块计数的顺序列出表格。提供了显示计划链接以显示对帐单文本和查询计划。

对应的方法是%SYS.PTools.UtilSQLAnalysis类中的tempIndices()。要导出此方法生成的分析数据,请使用exportTIAnalysis()方法。

  • 缺少联接索引的查询:此选项检查当前名称空间中具有联接的所有查询,并确定是否定义了支持该联接的索引。它将可用于支持联接的索引从0(不存在索引)排序到4(索引完全支持联接)。外部联接需要一个单向索引。内联接需要双向索引。默认情况下,结果集只包含JoinIndexFlag<4的行。JoinIndexFlag=4表示有完全支持联接的索引。

相应的方法是%SYS.PTools.UtilSQLAnalysis类中的joinIndices(),它提JoinIndexFLAG值的描述。要导出此方法生成的分析数据,请使用exportJIAnalysis()方法。默认情况下,exportJIAnalysis()不会列出JoinIndexFlag=4值,但可以选择列出这些值。

  • 带离群值索引的查询:此选项标识当前名称空间中具有离群值的所有查询,并确定是否定义了支持该离群值的索引。它将可用于支持离群值的索引从0(不存在索引)到4(索引完全支持离群值)进行排序。默认情况下,结果集只包含OutlierIndexFlag<4的行。OutlierIndexFlag=4表示存在完全支持离群值的索引。

对应的方法是%SYS.PTools.UtilSQLAnalysis类中的outlierIndices()。要导出此方法生成的分析数据,请使用exportOIAnalysis()方法。默认情况下,exportOIAnalysis()不会列出OutlierIndexFlag=4值,但可以选择列出这些值。

当选择其中一个选项时,系统自动执行操作并显示结果。 第一次选择一个选项或调用相应的方法时,系统生成结果数据; 如果选择该选项或再次调用该方法,InterSystems IRIS将重新显示相同的结果。 要生成新的结果数据,必须使用Gather SQL Statements按钮重新初始化Index Analyzer结果表。 为%SYS.PTools生成新的结果数据。 在UtilSQLAnalysis方法中,必须调用gettsqlstmts()来重新初始化索引分析器结果表。 更改“跳过所有系统类和例程”或“跳过插入语句”复选框选项也会重新初始化索引分析器结果表。

IndexUsage()方法

下面的示例演示indexUsage()方法的用法:

/// w ##class(PHA.TEST.SQL).CountingCachedQueries2()
ClassMethod CountingCachedQueries2()
{
	DO ##class(%SYS.PTools.UtilSQLAnalysis).indexUsage(1,1)
	SET utils = "SELECT %EXACT(Type), Count(*) As QueryCount "_
	          "FROM %SYS_PTools.UtilSQLStatements GROUP BY Type"
	SET utilresults = "SELECT SchemaName, Tablename, IndexName, UsageCount "_
	                "FROM %SYS_PTools.UtilSQLAnalysisDB ORDER BY UsageCount"
	SET tStatement = ##class(%SQL.Statement).%New()
	SET qStatus = tStatement.%Prepare(utils)
	IF qStatus'=1 {
		WRITE "%Prepare 失败:" DO $System.Status.DisplayError(qStatus) QUIT
	}
	SET rset = tStatement.%Execute()
	DO rset.%Display()
	WRITE !,"实用程序结束数据",!!
	SET qStatus = tStatement.%Prepare(utilresults)
	IF qStatus'=1 {
		WRITE "%Prepare 失败:" DO $System.Status.DisplayError(qStatus) QUIT
	}
	SET rset = tStatement.%Execute()
	DO rset.%Display()
	WRITE !,"实用程序结束数据"
}

注意,由于结果是按UsageCount排序的,因此带有UsageCount > 0的索引列在结果集的末尾。

可以从ObjectScript或SQL调用或SELECT命令调用该类中的方法。 SQL命名约定是指定包名%SYS_PTools,然后在以小写字母开头的方法名前加上前缀“PT_”。 如下面的例子所示:

ObjectScript:

  DO ##class(%SYS.PTools.UtilSQLAnalysis).indexUsage()

SQL:

  CALL %SYS_PTools.PT_indexUsage()
  SELECT %SYS_PTools.PT_indexUsage()

索引优化选项

默认情况下,InterSystems SQL查询优化器使用复杂而灵活的算法来优化涉及多个索引的复杂查询的性能。在大多数情况下,这些默认值可提供最佳性能。但是,在极少数情况下,可能希望通过指定OPTIMIZE-OPTION关键字为查询优化器提供“提示”。

FROM子句支持%ALLINDEX%IGNOREINDEX OPTIME-OPTION关键字。这些优化选项关键字控制查询中使用的所有索引。

可以使 %NOINDEX条件级别提示指定对特定条件使用索引的例外情况。 %NOINDEX提示放在每个不应使用索引的条件之前。例如,WHERE %NOINDEX hiredate < ?当绝大多数数据被条件选中(或未选中)时,这是最常用的。对于小于(<)或大于(>)的条件,使用%NOINDEX条件级别提示通常是有益的。对于相等条件,使用%NOINDEX条件级别提示不会带来任何好处。使用联接条件时,ON子句联接支持%NOINDEX

%NOINDEX关键字可用于覆盖在FROM子句中建立的索引优化。在下面的示例中,%ALLINDEX优化关键字适用于除E.Age条件之外的所有条件测试:

  SELECT P.Name,P.Age,E.Name,E.Age
  FROM %ALLINDEX Sample.Person AS P LEFT OUTER JOIN Sample.Employee AS E
       ON P.Name=E.Name
  WHERE P.Age > 21 AND %NOINDEX E.Age < 65

image

0
0 147
文章 姚 鑫 · 四月 22, 2021 8m read

第五章 优化查询性能(一)

InterSystems SQL自动使用查询优化器创建在大多数情况下提供最佳查询性能的查询计划。该优化器在许多方面提高了查询性能,包括确定要使用哪些索引、确定多个AND条件的求值顺序、在执行多个联接时确定表的顺序,以及许多其他优化操作。可以在查询的FROM子句中向此优化器提供“提示”。本章介绍可用于评估查询计划和修改InterSystems SQL将如何优化特定查询的工具。

InterSystems IRIS®Data Platform支持以下优化SQL查询的工具:

  • SQL Runtime Statistics用于生成查询执行的运行时性能统计信息
  • 索引分析器,用于显示当前命名空间中所有查询的各种索引分析器报告。这显示了InterSystems SQL将如何执行查询,可以全面了解索引是如何使用的。此索引分析可能表明应该添加一个或多个索引以提高性能。
  • 查询执行计划:显示SQL查询(查询计划)的最佳(默认)执行计划,并可选地显示该SQL查询的备用查询计划以及统计信息。用于显示查询计划的工具包括SQL EXPLAIN命令、$SYSTEM.SQL.ExPlan()方法以及管理门户和SQL Shell中的各种Show Plan工具。查询计划和统计数据是在准备查询时生成的,不需要执行查询。

可以使用以下选项来指导查询优化器,方法是设置配置默认值或在查询代码中编码优化器“提示”:

  • 管理所有条件的子句选项中提供的索引优化选项,或单个条件前面的%NOINDEX
  • SQL代码中指定的注释选项,使优化器覆盖该查询的系统范围编译选项。
  • 在每个查询或系统范围的基础上可用的并行查询处理允许多处理器系统在处理器之间划分查询执行。

以下SQL查询性能工具将在本手册的其他章节中介绍:

  • 缓存查询,使动态SQL查询能够重新运行,而无需在每次执行查询时准备查询的开销。
  • SQL语句来保留最新编译的嵌入式SQL查询。在“SQL语句和冻结计划”一章中。
  • 冻结计划以保留嵌入式SQL查询的特定编译。使用此编译,而不是使用较新的编译。在“SQL语句和冻结计划”一章中。

以下工具用于优化表数据,因此可以对针对该表运行的所有查询产生重大影响:

  • 定义索引可以显著提高对特定索引字段中数据的访问速度。
  • ExtentSizeSelectiveBlockCount用于在用数据填充表之前指定表数据估计;此元数据用于优化未来的查询。
  • Tune Table用于分析已填充的表中的代表性表数据;生成的元数据用于优化未来的查询。

本章还介绍如何将查询优化计划写入文件,以及如何生成SQL故障排除报告以提交给InterSystems WRC。

管理门户SQL性能工具

IRIS管理门户提供对以下SQL性能工具的访问。有两种方式可以从管理门户系统资源管理器选项访问这些工具:

  • 选择工具,然后选择SQL性能工具。
  • 选择SQL,然后选择工具下拉菜单。

从任一界面中您都可以选择以下SQL性能工具之一:

  • SQL运行时统计信息,以生成查询执行的性能统计信息。
  • 索引分析器,用于显示当前命名空间中所有查询的各种索引分析器报告。这显示了InterSystems SQL将如何执行查询,可以全面了解索引是如何使用的。此索引分析可能表明应该添加一个或多个索引以提高性能。
  • 备用显示计划:显示SQL查询的可用备用查询计划以及统计信息。
  • 生成报告以向InterSystems Worldwide Response Center(WRC)客户支持部门提交SQL查询性能报告。要使用此报告工具,必须首先从WRC获得WRC跟踪号。
  • 导入报告允许查看SQL查询性能报告。

SQL运行时统计信息

可以使用SQL运行时统计信息来衡量系统上运行的SQL查询的性能。SQL运行时统计信息衡量SELECTINSERTUPDATEDELETE操作(统称为查询操作)的性能。SQL运行时统计信息(SQL Stat)是在准备查询操作时收集的。请参阅使用SQL运行时统计信息工具。

默认情况下,SQL运行时统计信息的收集处于关闭状态。必须激活统计信息收集。强烈建议指定超时以结束统计信息收集。激活统计信息收集后,必须重新编译(准备)现有的动态SQL查询,并重新编译包含嵌入式SQL的类和例程。

性能统计信息包括ModuleNameModuleCount(模块被调用的次数)、RowCount(返回的行数)、TimeSpent(执行性能,单位为秒)、GlobalRefs(全局引用数)、LinesOfCode(执行的行数)和ReadLatency(磁盘读取访问时间,单位为毫秒)。

可以显式清除SQL Stats数据。清除缓存查询会删除所有相关的SQL统计数据。删除表或视图会删除所有相关的SQL Stats数据。

注意:系统任务在所有名称空间中每小时自动运行一次,以将特定于进程的SQL查询统计信息聚合到全局统计信息中。因此,全局统计信息可能不会反映一小时内收集的统计信息。可以使用管理门户监视此每小时一次的聚合或强制其立即发生。要查看此任务上次完成和下次调度的时间,请依次选择系统操作、任务管理器、任务调度,然后查看更新SQL查询统计信息任务。可以单击任务名称查看任务详细信息。在Task Details(任务详细信息)显示中,可以使用Run(运行)按钮强制立即执行任务。

使用SQL运行时统计信息工具

可以使用以下任一方法从管理门户显示系统范围内的SQL查询的性能统计信息:

  • 选择系统资源管理器,选择工具,选择SQL性能工具,然后选择SQL运行时统计信息。

image

  • 选择系统资源管理器,选择SQL,然后从工具下拉菜单中选择SQL运行时统计信息。

Settings

“设置”选项卡显示当前系统范围的SQL运行时统计信息设置以及此设置的过期时间。

Change Settings(更改设置)按钮允许设置以下统计信息收集选项:

  • 收集选项:可以将统计信息收集选项设置为0、1、2或3.0。0=关闭统计信息代码生成;1=为所有查询打开统计信息代码生成,但不收集统计信息;2=仅记录查询外部循环的统计信息(在主模块打开和关闭时收集统计信息);3=记录查询的所有模块级别的统计信息。
    • 从0到1:更改SQL Stats选项后,需要编译包含SQL的例程和类以执行统计代码生成。对于xDBC和动态SQL,必须清除缓存查询以强制重新生成代码。
    • 要从1变为2:只需更改SQL Stats选项即可开始收集统计信息。这使可以在运行的生产环境中启用SQL性能分析,并将中断降至最低。
    • 从1到3(或从2到3):更改SQL Stats选项后,需要编译包含SQL的例程和类,以记录所有模块级别的统计信息。对于xDBC和动态SQL,必须清除缓存查询以强制重新生成代码。选项3通常仅用于非生产环境中已识别的性能较差的查询。
    • 从1、2或3变为0:要关闭统计代码生成,不需要清除缓存的查询。
  • 超时选项:如果收集选项为2或3,可以按已用时间(小时或分钟)或按完成日期和时间指定超时。可以用分钟或小时和分钟指定运行时间;该工具将指定的分钟值转换为小时和分钟(100分钟=1小时40分钟)。默认值为50分钟。日期和时间选项默认为当天午夜(23:59)之前。强烈建议指定超时选项。
  • 重置选项:如果收集选项为2或3,则可以指定超时值到期时要重置为的收集选项。可用选项为0和1。

查询测试

查询测试选项卡允许输入SQL查询文本(或从历史记录中检索),然后显示该查询的SQL统计信息和查询计划。查询测试包括查询的所有模块级别的SQL统计信息,而与收集选项设置无关。

输入一个SQL查询文本,或使用Show History按钮检索一个。 可以通过单击右边的圆形“X”圆来清除查询文本字段。

使用Show Plan With SQL Stats按钮执行。

默认情况下,后台复选框中的“运行Show Plan进程”未被选中,这是大多数查询的首选设置。 仅对长时间、运行缓慢的查询选择此复选框。 当这个复选框被选中时,你会看到一个进度条显示“请等待…”的消息。 当运行一个长查询时,带有SQL Stats和Show History按钮的Show Plan消失,而显示一个View Process按钮。 单击View Process将在新选项卡中打开流程详细信息页面。 在流程详细信息页面中,可以查看该流程,并可以暂停、恢复或终止该流程。 流程的状态应该反映在显示计划页面上。 当流程完成后,显示计划会显示结果。 View Process按钮消失,带有SQL Stats的Show PlanShow History按钮重新出现。

使用查询测试显示的语句文本包括注释,不执行文字替换。

查看统计信息

View Stats(查看统计信息)选项卡为提供了在此系统上收集的运行时统计信息的总体视图。

可以单击任何一个View Stats列标题对查询统计信息进行排序。然后,可以单击SQL语句文本以查看所选查询的详细查询统计信息和查询计划。

使用此工具显示的语句文本包括注释,不执行文字替换。ExportStatsSQL()Show Plan显示的语句文本会去掉注释并执行文字替换。

清除统计信息按钮

清除统计信息按钮清除当前名称空间中所有查询的所有累积统计信息。它会在SQL运行时统计信息页上显示一条消息。如果成功,则会显示一条消息,指示已清除的统计信息数量。如果没有统计信息,则会显示无要清除的消息。如果清除不成功,则会显示一条错误消息。

运行时统计信息和显示计划

SQL运行时统计信息工具可用于显示包含运行时统计信息的查询的显示计划。

可以使用Alternate Show Plans工具将显示计划与统计数据进行比较,从而显示查询的运行时统计信息。备用显示计划工具在其显示计划选项中显示查询的估计统计信息。如果激活了收集运行时统计信息,则其Compare Show Plans with Stats选项将显示实际的运行时统计信息;如果运行时统计信息未处于活动状态,则此选项将显示估计统计信息。

0
0 208
文章 姚 鑫 · 四月 21, 2021 13m read

第四章 缓存查询(二)

运行时计划选择

运行时计划选择(RTPC)是一个配置选项,它允许SQL优化器利用运行时(查询执行时)的离群值信息。运行时计划选择是系统范围的SQL配置选项。

RTPC被激活时,准备查询包括检测查询是否包含具有离群值的字段上的条件。如果PREPARE检测到一个或多个异常值字段条件,则不会将查询发送到优化器。相反,SQL会生成一个运行时计划选择存根。在执行时,优化器使用此存根选择要执行的查询计划:忽略离群值状态的标准查询计划,或针对离群值状态进行优化的替代查询计划。如果有多个异常值条件,优化器可以从多个备选运行时查询计划中进行选择。

  • 准备查询时,SQL将确定它是否包含离群值字段条件。如果是这样,它将推迟选择查询计划,直到执行查询。在准备时,它创建一条标准SQL语句和(对于动态SQL)相应的缓存查询,但将选择是使用此查询计划还是创建不同的查询计划,直到查询执行。在准备时,它创建看起来像是标准SQL语句的内容,如下所示:DECLARE QRS CURSOR FOR SELECT Top ? Name,HaveContactInfo FROM Sample.MyTest WHERE HaveContactInfo=?,用问号表示文字替代变量。但是,如果查看SQL语句详细资料,则查询计划在准备时包含语句“执行可能导致创建不同的计划”,动态SQL查询还会创建看似标准的缓存查询;但是,缓存查询显示计划选项使用SELECT %NORUNTIME关键字显示查询文本,表明这是不使用RTPC的查询计划。
  • 执行查询(在嵌入式SQL中打开)时,SQL将创建第二个SQL语句和相应的缓存查询。SQL语句具有散列生成的名称并生成RTPC存根,如下所示: DECLARE C CURSOR FOR %NORUNTIME SELECT Top :%CallArgs(1) Name,HaveContactInfo FROM Sample.MyTest WHERE HaveContactInfo=:%CallArgs(2).然后,优化器使用它来生成相应的缓存查询。如果优化器确定离群值信息没有提供性能优势,它将创建一个与准备时创建的缓存查询相同的缓存查询,并执行该缓存查询。但是,如果优化器确定使用离群值信息可提供性能优势,则它会创建一个缓存查询,以禁止对缓存查询中的离群值字段进行文字替换。例如,如果HaveContactInfo字段是异常值字段(绝大多数记录的值为‘Yes’),查询SELECT Name,HaveContactInfo FROM t1 WHERE HaveContactInfo=?将导致缓存查询:SELECT Name,HaveContactInfo FROM t1 WHERE HaveContactInfo=(('Yes')).

请注意,RTPC查询计划的显示根据SQL代码的源代码而有所不同:

管理门户SQL界面显示计划按钮可能会显示另一个运行时查询计划,因为此显示计划从SQL界面文本框中获取其SQL代码。

选中该SQL语句后,将显示包括查询计划的语句详细资料。此查询计划不显示替代运行时查询计划,而是包含文本“执行可能导致创建不同的计划”,因为它从语句索引中获取其SQL代码。

如果RTPC未激活,或者查询不包含适当的离群值字段条件,优化器将创建标准SQL语句和相应的缓存查询。

如果一个RTPC存根被冻结,那么所有相关的备用运行时查询计划也会被冻结。 即使关闭了RTPC配置选项,对于冻结的查询,RTPC处理仍然是活动的。

在写查询时,可以通过指定圆括号来手动抑制文字替换: SELECT Name,HaveContactInfo FROM t1 WHERE HaveContactInfo=(('Yes')).如果在条件中抑制离群值字段的文字替换,则RTPC不会应用于查询。 优化器创建一个标准的缓存查询。

激活RTPC

可以使用管理门户或类方法在系统范围内配置RTPC。 注意,更改RTPC配置设置将清除所有缓存的查询。

使用管理门户,根据参数值SQL设置配置系统范围的优化查询。 该选项将运行时计划选择(RTPC)优化和作为离群值(BQO)优化的偏差查询设置为合适的组合。 选择系统管理、配置、SQL和对象设置、SQL来查看和更改此选项。 可用的选择有:

  • 假设查询参数值不是字段离群值(BQO=OFF, RTPC=OFF,初始默认值)
  • 假设查询参数值经常匹配字段离群值(BQO=ON, RTPC=OFF)
  • 在运行时优化实际查询参数值(BQO=OFF, RTPC=ON)

要确定当前设置,调用$SYSTEM.SQL.CurrentSettings()

$SYSTEM.SQL.Util.SetOption()方法可以在系统范围内激活所有进程的RTPC,如下所示:SET status=$SYSTEM.SQL.Util.SetOption("RTPC",flag,.oldval)flag参数是一个布尔值,用于设置(1)或取消设置(0)RTPC。 oldvalue参数以布尔值的形式返回之前的RTPC设置。

应用RTPC

系统对SELECTCALL语句应用RTPC。 它不应用RTPC插入、更新或删除语句。

当在以下查询上下文中指定了一个离群值时,系统将RTPC应用于调优表确定的任何字段。

在与文字比较的条件中指定离群值字段。 这个比较条件可以是:

  • 使用相等(=)、非相等(!=)、IN%INLIST谓词的WHERE子句条件。
  • 具有相等(=)、非相等(!=)、IN%INLIST谓词的ON子句连接条件。

如果应用了RTPC,优化器将在运行时确定是应用标准查询计划还是备选查询计划。

如果查询中包含unresolved ? 输入参数。

如果查询指定了用双括号括起来的文字值,则不应用RTPC,从而抑制了文字替换。

如果文字是由子查询提供给离群字段条件的,则RTPC不会被应用。 但是,如果子查询中存在离群字段条件,则应用RTPC

Overriding RTPC

通过指定%NORUNTIMErestrict关键字,可以覆盖特定查询的RTPC。如果查询SELECT Name,HaveContactInfo FROM t1 WHERE HaveContactInfo=? 会导致RTPC处理,查询 SELECT %NORUNTIME Name,HaveContactInfo FROM t1 WHERE HaveContactInfo=?将覆盖RTPC,从而产生一个标准的查询计划。

缓存查询结果集

当执行缓存的查询时,它会创建一个结果集。 缓存的查询结果集是一个对象实例。 这意味着为文字替换输入参数指定的值被存储为对象属性。 这些对象属性使用i%PropName语法引用。

List缓存查询

计算缓存查询

通过调用%Library.SQLCatalog类的GetCachedQueryTableCount()方法,可以确定表的当前缓存查询数。下面的示例显示了这一点:

/// w ##class(PHA.TEST.SQL).CountingCachedQueries()
ClassMethod CountingCachedQueries()
{
	SET tbl="Sample.Person"
	SET num=##class(%Library.SQLCatalog).GetCachedQueryTableCount(tbl)
	IF num=0 {
		WRITE "没有缓存的查询 ",tbl 
	} ELSE {
		WRITE tbl," 与以下内容相关联 ",num," 缓存查询" 
	}
	q ""
}
DHC-APP>w ##class(PHA.TEST.SQL).CountingCachedQueries()
Sample.Person 与以下内容相关联 2 缓存查询

请注意,引用多个表的查询将创建单个缓存查询。但是,这些表中的每一个都单独计算该缓存查询的数量。因此,按表计数的缓存查询数可能大于实际缓存查询数。

显示缓存的查询

可以使用IRIS管理门户查看(和管理)查询缓存的内容。从系统资源管理器中,选择SQL。使用页面顶部的切换选项选择一个命名空间;这将显示可用命名空间的列表。在屏幕左侧打开Cached Queries文件夹。选择其中一个缓存查询将显示详细信息。

查询类型可以是下列值之一:

  • %SQL.Statement Dynamic SQL:使用%SQL.Statement的动态SQL查询。
  • Embedded cached SQL :嵌入式缓存SQL
  • ODBC/JDBC Statement:来自ODBC或JDBC的动态查询。

成功准备SQL语句后,系统会生成一个实现该语句的新类。如果已经设置了Retention Cached Query Source-System-wide配置选项,那么这个生成的类的源代码将被保留,并且可以使用Studio打开以供检查。要执行此操作,请转到IRIS管理门户。从系统管理中,依次选择配置、SQL和对象设置、SQL。在此屏幕上,可以设置保留缓存的查询源选项。如果未设置此选项(默认设置),系统将生成并部署类,并且不保存源代码。

也可以使用$SYSTEM.SQL.Util.SetOption()方法设置这个系统范围的选项,如下所示:SET status=$SYSTEM.SQL.Util.SetOption("CachedQuerySaveSource",flag,.oldval)Flag参数是一个布尔值,用于在编译缓存查询后保留(1)或不保留(0)查询源代码;默认值为0。要确定当前设置,请调用$SYSTEM.SQL.CurrentSettings()

使用^rINDEXSQL列出缓存查询

  ZWRITE ^rINDEXSQL("sqlidx",2)

此列表中的典型全局变量如下所示:

^rINDEXSQL("sqlidx",2,"%sqlcq.USER.cls4.1","oRuYrsuQDz72Q6dBJHa8QtWT/rQ=")="".

第三个下标是位置。例如,"%sqlcq.USER.cls4.1"是用户名称空间中的缓存查询;"Sample.MyTable.1"是一条SQL语句。第四个下标是语句散列。

将缓存查询导出到文件

以下实用程序将当前名称空间的所有缓存查询列出到文本文件中。

ExportSQL^%qarDDLExport(file,fileOpenParam,eos,cachedQueries,classQueries,classMethods,routines,display)
  • file 要列出缓存查询的文件路径名。指定为带引号的字符串。如果该文件不存在,系统将创建该文件。如果该文件已存在,则InterSystems IRIS会覆盖该文件。
  • fileOpenParam 可选-文件的打开模式参数。指定为带引号的字符串。默认值为“WNS”“W”指定正在打开文件以进行写入。“N”指定如果该文件不存在,则使用此名称创建一个新的顺序文件。“S”指定以回车符、换行符或换页符作为默认终止符的流格式。
  • eos 可选-用于分隔清单中各个缓存查询的语句结尾分隔符。指定为带引号的字符串。默认值为“GO”
  • cachedQueries 可选—从查询缓存导出所有SQL查询到文件。一个布尔标志。默认值为1。
  • classQueries 可选-从SQL类查询导出所有SQL查询到文件。一个布尔标志。默认值为1。
  • classMethods 可选-从类方法导出嵌入式SQL查询到文件。一个布尔标志。默认值为1。
  • routines 可选-从MAC例程导出嵌入式SQL查询到文件。这个清单不包括系统例程、缓存查询或生成的例程。一个布尔标志。默认值为1。
  • display 可选-在终端屏幕上显示导出进度。一个布尔标志。默认值为0。

下面是一个调用这个缓存查询导出工具的示例:

  DO ExportSQL^%qarDDLExport("C:\temp\test\qcache.txt","WNS","GO",1,1,1,1,1)

当在终端命令行中执行display=1时,导出进度显示在终端屏幕上,示例如下:

Export SQL Text for Cached Query: %sqlcq.USER.cls14..                Done
Export SQL Text for Cached Query: %sqlcq.USER.cls16..                Done
Export SQL Text for Cached Query: %sqlcq.USER.cls17..                Done
Export SQL Text for Cached Query: %sqlcq.USER.cls18..                Done
Export SQL Text for Cached Query: %sqlcq.USER.cls19..                Done
Export SQL statement for Class Query: Cinema.Film.TopCategory...        Done
Export SQL statement for Class Query: Cinema.Film.TopFilms...           Done
Export SQL statement for Class Query: Cinema.FilmCategory.CategoryName...Done
Export SQL statement for Class Query: Cinema.Show.ShowTimes...          Done

20 SQL statements exported to script file C:\temp\test\qcache.txt

创建的导出文件包含如下条目:

  -- SQL statement from Cached Query %sqlcq.USER.cls30
  SELECT TOP ? Name , Home_State , Age , AVG ( Age ) AS AvgAge FROM Sample . Person ORDER BY Home_State
GO
  -- SQL statement from Class Query Cinema.Film.TopCategory
#import Cinema
SELECT TOP 3 ID, Description, Length, Rating, Title, Category->CategoryName
  FROM Film
  WHERE (PlayingNow = 1) AND (Category = :P1)
  ORDER BY TicketsSold DESC
GO
  -- SQL statement(s) from Class Method Aviation.EventCube.Fact.%Count
#import Aviation.EventCube
SELECT COUNT(*) INTO :tCount FROM Aviation_EventCube.Fact
GO

这个缓存的查询列表可以用作查询优化计划实用程序的输入。

执行缓存查询

  • 从动态SQL:%SQL.Statement准备操作(%PrepareClassQuery()%ExecDirect())创建缓存查询。使用同一实例的动态SQL%Execute()方法执行最近准备的缓存查询。
  • 从终端:可以使用$SYSTEM.SQL类的ExecuteCachedQuery()方法直接执行缓存查询。此方法允许指定输入参数值并限制要输出的行数。可以从终端命令行执行动态SQL%SQL.Statement缓存查询或xDBC缓存查询。此方法主要用于测试有限数据子集上的现有缓存查询。
  • 在管理门户SQL界面中:按照上面的“显示缓存的查询”说明进行操作。从所选缓存查询的目录详细资料选项卡中,单击执行链接。

缓存查询锁

在更新缓存的查询元数据时,发出PREPAREPURCESS语句会自动请求独占的系统范围锁。SQL支持$SYSTEM.SQL.Util.SetOption()方法的系统范围CachedQueryLockTimeout选项。此选项控制在尝试获取对缓存查询元数据的锁定时的锁定超时。默认值为120秒。这比标准的SQL锁定超时(默认为10秒)要长得多。系统管理员可能需要在具有大量并发准备和清除操作的系统上修改此缓存查询锁定超时,尤其是在执行涉及大量(数千)缓存查询的批量清除的系统上。

SET status=$SYSTEM.SQL.Util.SetOption("CachedQueryLockTimeout",seconds,.oldval)方法设置系统范围的超时值:

SetCQTimeout
   SET status=$SYSTEM.SQL.Util.SetOption("CachedQueryLockTimeout",150,.oldval)
   WRITE oldval," initial value cached query seconds",!!
SetCQTimeoutAgain
   SET status=$SYSTEM.SQL.Util.SetOption("CachedQueryLockTimeout",180,.oldval2)
   WRITE oldval2," prior value cached query seconds",!!
ResetCQTimeoutToDefault
   SET status=$SYSTEM.SQL.Util.SetOption("CachedQueryLockTimeout",oldval,.oldval3)

CachedQueryLockTimeout设置系统范围内所有新进程的缓存查询锁定超时。它不会更改现有进程的缓存查询锁定超时。

清除缓存的查询

每当修改(更改或删除)表定义时,基于该表的任何查询都会自动从本地系统上的查询缓存中清除。如果重新编译持久类,则使用该类的任何查询都会自动从本地系统上的查询缓存中清除。

可以使用清除缓存查询选项之一通过管理门户显式清除缓存查询。可以使用SQL命令PURGE Cached Queries显式清除缓存查询。可以使用SQL Shell清除命令显式清除缓存查询。

可以使用$SYSTEM.SQL.Push(N)方法显式清除最近未使用的缓存查询。指定n天数将清除当前命名空间中在过去n天内未使用(准备)的所有缓存查询。将n值指定为0“”将清除当前命名空间中的所有缓存查询。例如,如果在2018年5月11日发出$SYSTEM.SQL.Push(30)方法,则它将仅清除在2018年4月11日之前最后准备的缓存查询。不会清除恰好在30天前(在本例中为4月11日)上次准备的缓存查询。

还可以使用以下方法清除缓存的查询:

  • $SYSTEM.SQL.PurgeCQClass()按名称清除当前命名空间中的一个或多个缓存查询。可以将缓存的查询名称指定为逗号分隔的列表。缓存查询名称区分大小写;命名空间名称必须以全大写字母指定。指定的缓存查询名称或缓存查询名称列表必须用引号引起来。
  • $SYSTEM.SQL.PurgeForTable()清除当前命名空间中引用指定表的所有缓存查询。架构和表名称不区分大小写。
  • $SYSTEM.SQL.PurgeAllNamespaces()清除当前系统上所有名称空间中的所有缓存查询。请注意,删除命名空间时,不会清除与其关联的缓存查询。执行PurgeAllNamespaces()检查是否有任何与不再存在的名称空间相关联的缓存查询;如果有,则清除这些缓存查询。

要清除当前命名空间中的所有缓存查询,请使用管理门户清除此命名空间的所有查询选项。

清除缓存的查询还会清除相关的查询性能统计信息。

清除缓存的查询还会清除相关的SQL语句列表条目。管理门户中列出的SQL语句可能不会立即清除,可能需要按清除陈旧按钮才能从SQL语句列表中清除这些条目。

注意:当您更改系统范围的默认架构名称时,系统会自动清除系统上所有名称空间中的所有缓存查询。

远程系统

在本地系统上清除缓存的查询不会清除该缓存查询在镜像系统上的副本。 必须手动清除远程系统上已清除的缓存查询的副本。

当修改和重新编译持久性类时,基于该类的本地缓存查询将被自动清除。 IRIS不会自动清除远程系统上缓存的查询的副本。 这可能意味着远程系统上缓存的一些查询是“过时的”(不再有效)。 但是,当远程系统尝试使用缓存的查询时,远程系统会检查查询引用的任何持久类是否已重新编译。 如果重新编译了本地系统上的持久化类,则远程系统在尝试使用它之前会自动清除并重新创建过时的缓存查询。

没有缓存的SQL命令

以下非查询SQL命令不会缓存;它们在使用后会立即清除:

  • 数据定义语言(DDL):CREATE TABLE, ALTER TABLE, DROP TABLE, CREATE VIEW, ALTER VIEW, DROP VIEW, CREATE INDEX, DROP INDEX, CREATE FUNCTION, CREATE METHOD, CREATE PROCEDURE, CREATE QUERY, DROP FUNCTION, DROP METHOD, DROP PROCEDURE, DROP QUERY, CREATE TRIGGER, DROP TRIGGER, CREATE DATABASE, USE DATABASE, DROP DATABASE
  • 用户、角色和权限:CREATE USER, ALTER USER, DROP USER, CREATE ROLE, DROP ROLE, GRANT, REVOKE, %CHECKPRIV
  • 锁 :LOCK TABLE, UNLOCK TABLE
  • 其他: SAVEPOINT, SET OPTION

请注意,如果从管理门户执行查询界面发出这些SQL命令之一,性能信息将包括如下文本:缓存查询:%sqlcq.USER.cls16。这将显示在中,表示已分配缓存的查询名称。但是,此缓存查询名称不是链接。未创建缓存查询,并且未保留增量缓存查询编号.cls16。 SQL将此缓存的查询号分配给下一个发出的SQL命令。

0
0 167