清除过滤器
文章
王喆 👀 · 九月 24, 2022
在医院但凡接触“数据”和“指标”的人,对以下场景应该是深有感触。同样的指标、同样的时间,有可能是同一个部门出的,最后“数据不一致”。除了“匪夷所思”,更有“深恶痛绝”。那么,如何解决这个问题?我的答案是商业智能(BI)。随着技术和市场的发展,有很多公司开始研发直接面向业务用户的敏捷BI工具,FineBI就是这样的一款BI工具。这个也是我接触的第一款国产BI。
项目的前提条件和设置
操作系统:Windows 64
IRIS产品:HealthConnect 2020
BI工具:FineBI
JDK: JDK1.8
FineBI、JDK、HealthConnect、FineBI的相关安装步骤网上有很多,在此就暂时略过。要在FineBI上创建仪表板,我们需要一点数据,在此我使用的是开发过程中的一些测试数据,相关测试数据和互联互通的69个交互服务息息相关,有需要可以自己模拟的造。
将FineBI和IRIS连接
我们首先找到HealthConnect的安装文件,我们需要找到intersystems-jdbc-3.2.0.jar这样一个文件。大概在【安装目录】\InterSystems\HealthConnect\dev\java\lib\JDK18,如图所示:
将文件复制到FineBI的安装目录\webapps\webroot\WEB-INF\lib,如图所示:
这个时候我们启动FineBI
看到此说明FineBI启动成功,如果启动失败重启一下电脑或者检查以下JDK的环境变量是否配置
在浏览器上打开控制面板
点击【管理系统】--【数据连接】-【新建数据连接】-【数据连接管理】--【其他】--【其他JDBC】
填入内容
连接成功
创建仪表板
通过编写SQL的方式在FineBI中建立业务表:
按照此方式在【数据准备】-【数据列表】中建立多个业务表,如图所示:
新建仪表板
通过添加组件,选择表,选择样式
最终编辑成如图所示仪表板
总结
从上面演示的例子,我们可以看出,极短的时间我们就把FineBI连接到了IRIS的HealthConnect上,然后创建一些数据上的可视化的功能和东西。作为开发人员来说这很便捷,我们可以很快的完成一些数据展示的工作。同时如果把这些东西开放给一些精通业务但是对技术很生疏的人,就可以十分快速的把数据按照业务上需要的方式展示出来,一个部门乃至多个部分对出具的数据也有了更清晰的认识,在可视化的帮助下,那些乱起八糟的数据也开始变的更接近真实。
本文只展示了FineBI的使用,相信一些可以使用JDBC或者ODBC作为连接的BI工具都可以,比如最经典的PowerBI。
文章
姚 鑫 · 七月 1, 2021
# 第二十四章 执行XSLT转换
# 执行XSLT转换
要执行`XSLT`转换,请执行以下操作:
- 如果使用的是`Xalan`处理器(对于`XSLT 1.0`),请使用`%XML.XSLT.Transformer`的以下类方法之一:
- `TransformFile()`——转换给定XSLT样式表的文件。
- `TransformFileWithCompiledXSL()`——转换一个文件,给定一个已编译的XSLT样式表。
- `TransformStream()`——转换给定XSLT样式表的流。
- `TransformStreamWithCompiledXSL()`——转换一个流,给定一个已编译的XSLT样式表。
- `TransformStringWithCompiledXSL()`——转换给定已编译XSLT样式表的字符串。
- 如果使用`Saxon`处理器(用于XSLT 2.0),请使用`%XML.XSLT2.Transformer`的以下类方法之一:
- `TransformFile()`——转换给定XSLT样式表的文件。
- `TransformFileWithCompiledXSL()`——转换一个文件,给定一个已编译的XSLT样式表。
- `TransformStream()`——转换给定XSLT样式表的流。
- `TransformStreamWithCompiledXSL()`——转换一个流,给定一个已编译的XSLT样式表。
这些方法具有相似的签名。这些方法的参数列表按顺序如下:
- pSource—要转换的源XML。请参见此列表后面的表。
- pXSL -样式表或编译样式表。请参阅此列表后面的表格。
- pOutput -作为输出参数返回的结果XML。请参阅此列表后面的表格。
- pErrorHandler -一个可选的自定义错误处理程序。请参阅本章后面的“自定义错误处理”。如果不指定自定义错误处理程序,该方法将使用%XML.XSLT.ErrorHandler的新实例(对于两个类)。
- pParms -一个可选的InterSystems IRIS多维数组,包含要传递给样式表的参数。
- pCallbackHandler -定义`XSLT`扩展函数的可选回调处理程序。
- pResolver -一个可选的实体解析器。
8. (仅适用于`%XML.XSLT2.Transformer`)网关-`%Net.Remote.Gateway`的可选实例。如果要重用XSLT网关连接以获得更好的性能,请指定此参数;
作为参考,下表显示了这些方法的前三个参数,并进行了对比:
XSLT变换方法的比较
Method | pSource (Input XML) |pXSL (Stylesheet) |pOutput(Output XML)
---|---|---|---
TransformFile() |String that gives a file name| String that gives a file name |String that gives a file name
TransformFileWithCompiledXSL()| String that gives a file name |Compiled style sheet| String that gives a file name
TransformStream()| Stream| Stream |Stream, returned by reference
TransformStreamWithCompiledXSL()| Stream |Compiled style sheet |Stream, returned by reference
TransformStringWithCompiledXSL()| String |Compiled style sheet| String that gives a file name
# 示例
本节使用以下代码(但不同的输入文件)展示了几种转换:
```java
Set in="c:\0test\xslt-example-input.xml"
Set xsl="c:\0test\xslt-example-stylesheet.xsl"
Set out="c:\0test\xslt-example-output.xml"
Set tSC=##class(%XML.XSLT.Transformer).TransformFile(in,xsl,.out)
Write tSC
```
## 示例1:简单替换
在本例中,我们从以下输入XML开始:
```xml
Content
```
我们使用以下样式表:
```xml
Content Replaced
```
在这种情况下,输出文件将如下所示:
```xml
Content Replaced
```
## 示例2:内容提取
在本例中,我们从以下输入XML开始:
```xml
Some text
Some more text
```
我们使用以下样式表:
```xml
:
```
在这种情况下,输出文件将如下所示:
```java
13: Some text
14: Some more text
```
## 其他示例
InterSystems IRIS提供了以下附加示例:
- 对于`XSLT 1.0`,请参阅`%XML.XSLT.Transformer`中的`Example()`、`Example2()`和其它方法。
- 对于`XSLT 2.0`,请参见`Samples`命名空间中的类`XSLT2.Examples`。
文章
Michael Lei · 三月 12, 2022
在最近的大规模基准测试活动中,我们看到过多的%sys CPU时间,对应用程序的扩展产生了负面影响。
问题
我们发现,由于TZ环境变量没有被设置,很多时间都花在了localtime()系统调用上。 我们创建了一个简单的测试程序来证实这一观察结果,设置了TZ与未设置TZ的时间差和所需的CPU资源都是惊人的。 我们发现,当TZ没有设置时,从localtime()继承使用stat()系统调用到/etc/local_time是成本很高。
建议
InterSystems强烈建议在任何Linux安装中,无论是x86还是Linux on Power,都要确认TZ环境变量的设置是否适当,以获得最佳性能。 更多详情请见。"man tzset"。
目前的Caché 2016.1现场测试包含了对日期和时间功能的优化,初步测试表明有很大的改进。 下面是测试新的内部函数调用的样本输出,在这两种情况下,在Power上的Linux都没有设置TZ。
在Caché 2016.1 FT之前:
real 0m22.60s
user 0m1.64s
sys 0m20.89s
带有Caché 2016.1 FT:
real 0m0.40s
user 0m0.37s
sys 0m0.00s
请在评论区说明你的应用程序在TZ环境变量方面的任何经验,以及Caché 2016.1现场测试在设置或不设置TZ时的影响。谢谢!
问题
Yufeng Li · 十二月 23, 2021
在IRIS 上怎么实现读写分离? IRIS的镜像(Mirroring)支持多个报告类型(Reporting Asyncs)的异步成员,这些异步成员可以用于查询、报表运行、BI等多种场景。 我们现在是用前端调用rest api,这种情况下要怎么区分调用读服务器还是写服务器? 可以封装一个REST服务用于前端api调用,这个REST服务根据不同api操作和路径,在后台调用不同服务器上的REST API或SQL操作。或者也可以考虑使用InterSystems API管理器做这个事。 相关文章请参考:https://cn.community.intersystems.com/smartsearch?search=API 如果是指数据库层的读写分离,可以使用Sharding技术,利用Sharding技术中的计算结点和数据结点,搭建负载均衡+读写分离的数据库集群,参考:
https://docs.intersystems.com/irisforhealthlatest/csp/docbook/DocBook.UI.Page.cls?KEY=GSCALE_sharding#GSCALE_sharding_reference_plan_qs
如果服务器/实例资源有限,又想实现读负载与写负载的分离,那么基于@Qiao Peng指出的镜像异步成员,在API层(即应用程序层)通过业务流程来控制查询/写入操作的分发则是成本较低的方案。 请参考这个文档链接[Deploy Compute Nodes for Workload Separation and Increased Query Throughput](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GSCALE_sharding#GSCALE_sharding_options_qs)
文章
Qiao Peng · 六月 11, 2023
数据平台一直在进化:从数据中心到数据中台,离散的数据资产得到进一步梳理和整合、按业务封装数据和操作数据的方法,并逐步提供了企业统一的访问、更新、检索、查询等数据服务。
然而市场上不乏听到数据平台的成功案例,却鲜见这些案例得到大规模推广。原因是什么呢?
一. 传统数据平台建设的挑战
传统数据平台的数据模型基于各自厂商的理解,缺乏统一行业数据模型和行业语义。可供参考的国内卫生信息数据元、数据集标准并非完整的行业语义,例如没有业务实体模型和数据元关系定义。传统的数据平台建设通常根据业务域,围绕数据应用需求组织数据。经常看到按业务域划分为CDR(临床数据中心)、ODR(运营数据中心)、RDR(科研数据中心)......
这造成了几个挑战:
1. 按业务域、而非业务实体来划分数据,虽然方便相应的业务域数据分析,但跨业务域重叠的业务实体数据,例如患者,需要跨数据中心同步。这些同步由于数据模型上的差异,往往非全息拷贝。随着同步次数越多,跨数据中心的数据越失真,造成数据资产多源不统一、数据资产一致性问题和时效性问题。
2. 数据平台产品语义表达上参差不齐,业务用户依赖数据工程师对数据理解和操作,无论是统计分析还是机器学习,海量的实施工作无法满足业务敏捷性要求;
3. 数据平台及数据应用建设依赖单一厂商的能力,而建设成果,包括数据工具、分析指标和应用都无法跨数据平台复用。往往项目都在做低水平重复建设。
4. 数据互操作标准化程度低,数据的同步、迁移困难。在缺乏数据层互操作性的情况下,各类数据中心建设的依然是数据孤岛。
5. 由于数据中心往往忽视互操作建设,数据缺乏流动,进入数据平台后,往往成为死水一潭。
二. 如何应对挑战
如何解决这些数据平台建设困境?应该如何建设数据平台?
数据资产不是仅为分析服务的,更重要的是作为生产要素在生产全过程中发挥价值- 这就涉及到数据生成、采集、交换、决策… 在这个全过程链条上的数据互操作能力尤为重要。
HIMSS将互操作定义为4级:基础级、结构级、语义级和组织级,并认为只有到达语义级,才是标准的、才能实现广泛的互操作能力。要达到语义级的互操作,需要进行五位一体的标准化:词汇/术语标准、内容标准、传输标准、隐私和安全标准、标识符标准。
随着我们越来越依赖于机器处理数据、发掘数据背后的知识,对数据资产的开放性和互操作性的要求达到了更高的水平 - 实现机器可以理解的互操作。2016年发表在Scientific Data针对科学数据管理和监管,提出了数据的可发现(Findable)、可访问(Accessible)、可互操作(Interoperable)、可复用(Reusable)的FAIR指导原则。
这些原则的核心是让机器可以理解数据所需的语义层面的要求,尤其是可互操作和可复用两部分提到的语义级要求 - 广泛使用的语言、词汇表、元数据引用、符合相关领域的社区标准...
大家都不约而同地指向了统一行业语义。传统数据中心面临的上述挑战,正是因为缺乏统一的行业语义、缺乏统一的语义级互操作。
那什么是统一语义?
三. 统一语义数据平台
圣经记载人类曾经联合起来兴建能通往天堂的高塔 - 巴别塔、也称通天塔。上帝为了阻止人类的计划,让人类说不同的语言。人类相互之间不能沟通,造塔计划因此失败。
统一语言是数据能够互相理解、并利用数据的前提。
语言包含2个层面:
1. 语义:真实世界事物及其关系的表达方法。例如不同电子病历系统对疑似肺癌的记录,可能记录为以下三种之一:
A。问题: 癌症 身体部位:肺 确定程度:疑似
B。问题: 肺癌 确定程度:疑似
C。问题: 疑似肺癌
这三种语义表达不统一。没有统一的语义就像图里的电源插座,每个国家规格都不同,是不可能互联互通的。
2. 语法:语言的结构规则,包括词法和句法。而词法和句法都可能有歧义,就像图中示例的那样。
行业数据需要通过统一语义达到互联互通。对数据而言,统一语义不仅在数据模型(语义)、也在数据使用方式(语法)上。不仅数据语义是统一的,操作/互操作数据的方法也是统一的,并且需要能避免词法和句法歧义,才能达到语义级互操作能力!
是不是一定要统一语义?要看数据用途:对于特定的、简单的数据任务,简化的数据模型和数据处理方法可能已经足够,但对于复杂的、跨领域的数据任务,如广泛的自然语言处理、知识图谱构建、大规模机器学习等,统一语义是非常有价值的。
显然,对于数据平台这类多用途平台,应该统一数据语义。
四. 如何建设行业统一语义数据平台
数据平台建设向统一语义迈进,而统一的行业语义模型,应该针对行业用户友好:直观、完整、语义简单、没有二义性,易于数据探索与使用。
统一语义是指要统一物理数据模型和操作数据的语言吗?是要限定到特定的技术栈吗?
先看一下数据库的结构化查询语言(SQL):众多的关系型数据库、甚至很多非关系型数据库都支持ANSI SQL语言。SQL定义了自己的语义 - 表、字段、视图、存储过程... 和自己的语法 - 数据定义语言(DDL)、数据操作语言(DML),但它并没有定义任何数据的物理存储方案!也正因如此,任何数据库厂商、任何数据物理存储方案,都可以通过自己的SQL编译器来支持SQL和SQL客户端,从而屏蔽数据库物理层差异,使用相同的SQL语言共同建设SQL生态。这也是SQL生态壮大的原因之一。
SQL的成功告诉我们,统一行业语义是对行业数据的逻辑表达层的要求,它不应对任何数据库技术底层做要求,也就是不应限定任何技术栈。
前面提到统一的数据操作/互操作能力是统一语义的一部分,是要用单一的数据操作方法吗?数据有多种操作方式,每种操作方式都有自己适用的场景,如下:
对同一份数据提供多模型的操作能力,会极大提升语义层的操作/互操作的便捷性,是非常重要的统一语义特性。重要的是可以针对同一份语义数据进行多种模型的操作/互操作,而不是建立针对每种模型的多套语义,并进行数据复制。
也就是说统一语义,并不是数据只能有一种操作/互操作方式,而应提供对同一份统一语义数据的多种操作/互操作方式。
五. InterSystems统一语义数据平台建设
基于上面的建设思路,InterSystems的医疗信息统一语义平台通过对行业语义的理解和其智能数据编织能力,提供医疗信息数据基座。
5.1 行业语义选择 - FHIR
行业语义应具有开放性、成熟性、准确性、完整性、灵活性、简单性、非二义性、可互操作性、机器可理解,并被广泛接受与认可。纵观医疗信息行业,虽然有不少通用数据模型,但目前最满足上述条件的是HL7 FHIR。它的资源模型覆盖面广,不仅是临床、还包括管理、科研等;不仅包括通用数据模型 - FHIR资源模型,还有对其统一的互操作方法 - FHIR API;按80/20原则设计,允许对资源模型和API进行扩展;资源模型和API简单、并有详细的用例指南;FHIR资源模型、API、扩展都可以被计算机理解;FHIR拥有庞大的用例,并且其触角不断扩展到医疗信息应用的各个层面和各个方向。
另外,更重要的是,FHIR的定位就是行业语义标准 - 逻辑层的标准,任何厂商只需要提供自己的FHIR服务器,就可以利用任何技术栈发布统一的FHIR资源和FHIR API,而屏蔽底层不同类型的数据存储方案、数据模型和数据操作方法。因而它是一个强大的生态标准,所有厂商和用户都可以参与其中。
InterSystems的解决方案选择FHIR作为统一语义,在支持FHIR的6种互操作范式的基础上,提供对FHIR资源的SQL投射 - 无需数据拷贝,就可以使用SQL大规模查询FHIR资源,对统计分析、机器学习提供简单易用的数据操作能力。
5.2 利用数据编织技术,无需推倒重来
如果正在规划数据平台,应考虑按统一语义建设。如果已经建设有各类数据中心,并不需要将已有的建设成果推倒重来。InterSystems的解决方案通过数据编织技术,将数据源编织在一起,并建立逻辑上的统一语义层。原有数据中心和其各类应用继续运行,通过统一语义层来支撑新的数据利用和应用创新。
InterSystems利用数据编织技术,提供针对所有数据源、数据模型、互操作标准的接入能力和适配器。现有的数据中心被视为数据源,只需接入而无需推倒现有建设成果。
InterSystems的多模型能力,将这些离散的数据源统一转换、表达,将多数据源的数据,以FHIR资源这个统一语义模型,发布多种数据模型的数据服务:包括FHIR JSON模型、FHIR对象模型、FHIR SQL模型,满足多种应用场景对统一语义数据的最佳操作方式。
InterSystems数据引擎,为统一语义层提供高性能、横向可扩展的持久化层,满足不同规模的数据用户所需的性能和弹性。
InterSystems提供FHIR与互联互通、HL7 V2、CDA等通用模型的开箱即用的转换能力和对用户自定义模型的自定义转换能力,提供全方位的统一语义互操作能力。
文章
Hao Ma · 一月 10, 2021
在本文中,我想谈一谈规范优先的 REST API 开发方式。
传统的代码优先 REST API 开发是这样的:
* 编写代码
* 使其支持 REST
* 形成文档(成为 REST API)
规范优先遵循同样的步骤,不过是反过来的。 我们先制定规范(同时兼做文档),然后根据它生成一个样板 REST 应用,最后编写一些业务逻辑。
这是有好处的,因为:
* 对于想要使用你的 REST API 的外部或前端开发者,你总是有相关且有用的文档
* 使用 OAS (Swagger) 创建的规范可以导入各种工具,从而进行编辑、客户端生成、API 管理、单元测试和自动化,或者许多其他任务的简化
* 改进了 API 架构。 在代码优先的方式中,API 是逐个方法开发的,因此开发者很容易失去对整体 API 架构的跟踪,但在规范优先的方式中,开发者被强制从 API 使用者的角度与 API 进行交互,这通常有助于设计出更简洁的 API 架构
* 更快的开发速度 - 由于所有样板代码都是自动生成的,你无需编写代码,只需开发业务逻辑。
* 更快的反馈循环 - 使用者可以立即查看 API,并且只需修改规范即可轻松提供建议
让我们以规范优先的方式开发 API 吧!
### 计划
1. 使用 swagger 制定规范
* Docker
* 本地
* 在线
2. 将规范加载到 IRIS 中
* API 管理 REST API
* ^REST
* 类
3. 我们的规范会怎样?
4. 实现
5. 进一步开发
6. 注意事项
* 特殊参数
* CORS
7. 将规范加载到 IAM 中
### 制定规范
勿庸置疑,第一步是编写规范。 InterSystems IRIS 支持 Open API 规范 (OAS):
> **OpenAPI 规范** (以前称为 Swagger 规范)是 REST API 的 API 描述格式。 OpenAPI 文件允许描述整个 API,包括:
>
> * 可用端点 (`/users`) 和每个端点上的操作(`GET /users`、`POST /users`)
> * 每次操作的操作参数输入和输出
> * 身份验证方法
> * 联系信息、许可证、使用条款和其他信息。
>
> API 规范可以使用 YAML 或 JSON 编写。 格式易于学习,并且对人和机器都可读。 完整的 OpenAPI 规范可在 GitHub 上找到:[OpenAPI 3.0 规范](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md)
- 来自 Swagger 文档。
我们将使用 Swagger 编写 API。 使用 Swagger 有几种方法:
* [在线](https://editor.swagger.io/)
* Docker:`docker run -d -p 8080:8080 swaggerapi/swagger-editor`
* [本地安装](https://swagger.io/docs/open-source-tools/swagger-editor/)
安装/运行 Swagger 后,你应该在 Web 浏览器中看到以下窗口:
在左侧编辑 API 规范,在右侧可以立即看到渲染的 API 文档/测试工具。
我们将第一个 API 规范加载到其中(使用 [YAML](https://en.wikipedia.org/wiki/YAML))。 这是一个简单的 API,包含一个 GET 请求 - 返回指定范围内的随机数。
Math API 规范
swagger: "2.0"
info:
description: "Math"
version: "1.0.0"
title: "Math REST API"
host: "localhost:52773"
basePath: "/math"
schemes:
- http
paths:
/random/{min}/{max}:
get:
x-ISC_CORS: true
summary: "Get random integer"
description: "Get random integer between min and max"
operationId: "getRandom"
produces:
- "application/json"
parameters:
- name: "min"
in: "path"
description: "Minimal Integer"
required: true
type: "integer"
format: "int32"
- name: "max"
in: "path"
description: "Maximal Integer"
required: true
type: "integer"
format: "int32"
responses:
200:
description: "OK"
以下是其包含的内容。
有关 API 和使用的 OAS 版本的基本信息。
swagger: "2.0"
info:
description: "Math"
version: "1.0.0"
title: "Math REST API"
服务器主机、协议(http、https)和 Web 应用程序名称:
host: "localhost:52773"
basePath: "/math"
schemes:
- http
接下来指定路径(完整的 URL 是 `http://localhost:52773/math/random/:min/:max`)和 HTTP 请求方法(get、post、put、delete):
paths:
/random/{min}/{max}:
get:
之后,指定有关请求的信息:
x-ISC_CORS: true
summary: "Get random integer"
description: "Get random integer between min and max"
operationId: "getRandom"
produces:
- "application/json"
parameters:
- name: "min"
in: "path"
description: "Minimal Integer"
required: true
type: "integer"
format: "int32"
- name: "max"
in: "path"
description: "Maximal Integer"
required: true
type: "integer"
format: "int32"
responses:
200:
description: "OK"
在此部分中,我们定义请求:
* 为 CORS 启用此路径(稍后将详细介绍)
* 提供 _summary_ 和 _description_
* _operationId_ 允许规范内引用,它也是我们的实现类中生成的方法名
* _produces_ - 响应格式(例如文本、xml、json)
* _parameters_ 指定输入参数(在 URL 或正文中),在我们的示例中,我们指定 2 个参数 - 随机数生成器的范围
* _responses_ 列出服务器的可能响应
如你所见,这种格式并不是特别有挑战性,虽然还有很多可用功能。这里是[规范](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md)。
最后,我们将定义导出为 JSON。 转到“文件 → 转换”并另存为 JSON。 规范应如下所示:
Math API 规范
{
"swagger": "2.0",
"info": {
"description": "Math",
"version": "1.0.0",
"title": "Math REST API"
},
"host": "localhost:52773",
"basePath": "/math",
"schemes": [
"http"
],
"paths": {
"/random/{min}/{max}": {
"get": {
"x-ISC_CORS": true,
"summary": "Get random integer",
"description": "Get random integer between min and max",
"operationId": "getRandom",
"produces": [
"application/json"
],
"parameters": [
{
"name": "min",
"in": "path",
"description": "Minimal Integer",
"required": true,
"type": "integer",
"format": "int32"
},
{
"name": "max",
"in": "path",
"description": "Maximal Integer",
"required": true,
"type": "integer",
"format": "int32"
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
}
}
}
### 将规范加载到 IRIS 中
现在我们有了规范,我们可以在 InterSystems IRIS 中为此 REST API 生成样板代码。
要进入此阶段,我们需要三个东西:
* REST 应用程序名称:我们生成的代码的包(假定为 `math)
JSON 格式的 OAS 规范:我们刚刚在上一步中创建
Web 应用程序名称:用于访问我们的 REST API 的基本路径(我们的示例中为 /math`)
有三种方法使用我们的规范来生成代码,它们本质上是相同的,只是提供了多种访问相同功能的方式
1. 调用 `^%REST` 例程(在交互式终端会话中 `Do ^%REST`), [参见文档](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GREST_routine)。
2. 调用 `%REST` 类(`Set sc = ##class(%REST.API).CreateApplication(applicationName, spec)`,非交互式),[参见文档](https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=GREST_objectscriptapi)。
3. 使用 API 管理 REST API,[参见文档](https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=GREST_apimgmnt)。
我认为文档足以描述所需的步骤,因此选择一个即可。 我再补充两点说明:
* 在第 1 种和第 2 种方法中,可以向动态对象传递文件名或 URL
* 在第 2 种和第 3 种方法中,**必须** 进行一个额外的调用才能创建 Web 应用程序:`set sc = ##class(%SYS.REST).DeployApplication(restApp, webApp, authenticationType)`,所以在我们的示例中为 `set sc = ##class(%SYS.REST).DeployApplication("math", "/math")`,从 `%sySecurity` 包含文件获取 `authenticationType` 参数的值,相关条目为 `$$$Authe*`,因此对于未经身份验证的访问,传递 `$$$AutheUnauthenticated`。 如果省略,该参数默认为密码身份验证。
### 我们的规范会怎样?
如果你已成功创建应用,新的 `math` 包应该包含三个类:
* _Spec_ - 按原样存储规范。
* _Disp_ - 在调用 REST 服务时直接调用。 它封装 REST 处理并调用实现方法。
* _Impl_ - 保存 REST 服务的实际内部实现。 你只应该编辑此类。
[文档](https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=GREST_intro#GREST_intro_classes)包含有关这些类的更多信息。
### 实现
最初,我们的实现类 `math.impl` 只包含一个方法,对应于我们的 `/random/{min}/{max}` 操作:
/// Get random integer between min and max
/// The method arguments hold values for:
/// min, Minimal Integer
/// max, Maximal Integer
ClassMethod getRandom(min As %Integer, max As %Integer) As %DynamicObject
{
//(Place business logic here)
//Do ..%SetStatusCode()
//Do ..%SetHeader(,)
//Quit (Place response here) ; response may be a string, stream or dynamic object
}
让我们从简单的实现开始:
ClassMethod getRandom(min As %Integer, max As %Integer) As %DynamicObject
{
quit {"value":($random(max-min)+min)}
}
最后,我们可以通过在浏览器中打开此页面来调用我们的 REST API:`http://localhost:52773/math/random/1/100`
输出应该是:
{
"value": 45
}
此外,在 Swagger 编辑器中按 `Try it out`(试用)按钮并填写请求参数也会发送同样的请求:
恭喜! 我们使用规范优先的方式创建的第一个 REST API 现在已经生效!
### 进一步开发
当然,我们的 API 不是静态的,我们需要添加新路径等等。 在规范优先的开发中,首先要修改规范,然后更新 REST 应用程序(调用与创建应用程序相同),最后编写代码。 请注意,规范更新是安全的:你的代码不会受到影响,即使从规范中删除路径,在实现类中也不会删除方法。
### 注意事项
更多说明!
#### 特殊参数
InterSystems 向 swagger 规范添加了特殊参数,如下所示:
名称
数据类型
默认值
位置
描述
x-ISC_DispatchParent
类名
%CSP.REST
信息
调度类的超类。
x-ISC_CORS
布尔
false
操作
一个标志,指示对此端点/方法组合的 CORS 请求应该获得支持。
x-ISC_RequiredResource
数组
操作
以逗号分隔的已定义资源及其访问模式(资源:模式)的列表,这些资源和模式是访问 REST 服务的此端点所必需的。 示例:["%Development:USE"]
x-ISC_ServiceMethod
字符串
操作
后端调用的用于维护此操作的类方法的名称;默认值为 operationId,通常就很合适。
#### CORS
有三种方法启用 CORS 支持。
1. 在逐条路由的基础上,将 `x-ISC_CORS` 指定为 true。 我们的 Math REST API 中就是这样做的。
2. 在每个 API 的基础上,添加
Parameter HandleCorsRequest = 1;
然后重新编译该类。 规范更新后它也会保留。
3. (推荐)在每个 API 的基础上,实现自定义调度器超类(应该扩展 `%CSP.REST`)并编写 CORS 处理逻辑。 要使用此超类,请将 `x-ISC_DispatchParent` 添加到规范中。
### 将规范加载到 IAM 中
最后,我们将规范添加到 IAM中,以便将其发布给其他开发者。
如果您尚未开始使用 IAM,请参见[此文章](https://community.intersystems.com/post/introducing-intersystems-api-manager)。 它还涵盖了通过 IAM 提供 REST API,所以我在这里不做介绍。 您可能需要修改规范的 `host` 和 `basepath` 参数,使它们指向 IAM,而不是 InterSystems IRIS 实例。
打开 IAM 管理员门户,转到相关工作区的 `Specs`(规范)选项卡。
点击 `Add Spec`(添加规范)按钮并输入新 API 的名称(我们的示例中为 `math`)。 在 IAM 中创建新规范后,点击 `Edit`(编辑)并粘贴规范代码(JSON 或 YAML - IAM 都支持):
不要忘记点击 `Update File`(更新文件)。
现在我们的 API 已发布给开发者。 打开开发者门户,然后点击右上角的 `Documentation`(文档)。 除了三个默认 API,还应该看到我们的新 `Math REST API`:
打开它:
现在,开发者可以看到我们的新 API 的文档,并在同一个地方试用它!
###
### 结论
InterSystems IRIS 简化了 REST API 的开发过程,规范优先的方式使 REST API 生命周期管理更快更简单。 通过这种方式,你可以使用各种工具来完成各种相关任务,例如客户端生成、单元测试、API 管理等等。
### 链接
* [OpenAPI 3.0 规范](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md)
* [创建 REST 服务](https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=GREST)
* [从 IAM 开始](https://community.intersystems.com/post/introducing-intersystems-api-manager)
* [IAM 文档](https://docs.intersystems.com/irislatest/csp/docbook/apimgr/index.html)
文章
Hao Ma · 三月 25, 2021
**关键字**:PyODBC,unixODBC,IRIS,IntegratedML,Jupyter Notebook,Python 3
## **目的**
几个月前,我简单谈到了关于“[将 Python JDBC 连接到 IRIS](https://community.intersystems.com/post/python-jdbc-connection-iris-database-quick-note)”的话题。我后来频繁提起它, 因此决定再写一篇 5 分钟的笔记,说明如何“将 Python ODBC 连接到 IRIS”。
在 Windows 客户端中通常很容易设置 ODBC 和 PyODBC,不过我每次在 Linux/Unix 风格的服务器中设置 unixODBC 和 PyODBC 客户端时,都会遇到一些麻烦。
有没有一种简单连贯的方法,可以不安装任何 IRIS,在原版 Linux 客户端中让 PyODBC/unixODBC 针对远程 IRIS 服务器运行?
## **范围**
最近,我花了点时间研究如何在 Linux Docker 环境的 Jupyter Notebook 中从头开始让一个 PyODBC 演示运行起来, 记录下这篇稍微有些繁琐的笔记,以供日后快速参考。
#### **范围内**:
这篇笔记将涉及以下组件:
PyODBC over unixODBC
安装了 TensorFlow 2.2 和 Python 3 的 Jupyter Notebook 服务器
带有 IntegratedML 的 IRIS2020.3 CE 服务器,包括示例测试数据。
在此环境中
安装了 Docker-compose over AWS Ubuntu 16.04 的 Docker Engine
Docker Desktop for MacOS 和 Docker Toolbox for Windows 10 也经过了测试
#### **范围外**:
同样,在此演示环境中不评估非功能性方面。 它们很重要,并且可以针对特定站点,如:
端到端安全和审核
性能和可扩展性
许可和可支持性等
## **环境**
任何原版 Linux Docker 镜像都可以用于以下配置和测试步骤,但有一个简单的方法可以在 5 分钟内设置这样的环境:
1. Git **克隆**此[演示模板](https://github.com/tom-dyar/integratedml-demo-template)
2. 在包含 docker-compose.yml 文件的克隆目录中运行“**docker-compose up -d**”。
这将创建一个演示环境,如下面的拓扑所示,其中包含 2 个容器。 一个用于 Jupyter Notebook 服务器作为 PyODBC 客户端,另一个用于 IRIS2020.3 CE 服务器。
在上面的环境中,tf2jupyter 仅包含“Python over JDBC”客户端配置;它尚不包含任何 ODBC 或 PyODBC 客户端配置。
因此,我们将直接在 Jupyter Notebook 内部运行以下设置步骤,以使其易于说明。
## **步骤**
以下配置和测试由我在 AWS Ubuntu 16.04 服务器中运行, 由我的同事 @Thomas.Dyar 在 MacOS 中运行。 另外在 Docker Toolbox for Windows 中也进行了简单的测试。 不过,如果您遇到任何问题,还是请告诉我们。
以下步骤可以自动化到其 Dockerfile。 我在这里特别记录一下,以防几个月后忘记。
### **1. 官方文档:**
IRIS 的 ODBC 支持
在 Unix 上定义 ODBC 数据源
IRIS 的 PyODBC 支持
### **2. 连接到 Jupyter 服务器**
我用本地 Putty 的 SSH 隧道连接到远程 AWS Ubuntu 端口 22,然后按照上述拓扑结构映射到端口 8896。
(举个例子,在本地 Docker 环境中,也可以直接直接 http 到 Docker 机器的 IP:8896。)
### **3. 从 Jupyter Notebook 中运行 ODBC 安装**
直接在 Jupyter 单元格中运行以下代码:
!apt-get update<br>!apt-get install gcc<br>!apt-get install -y tdsodbc unixodbc-dev<br>!apt install unixodbc-bin -y<br>!apt-get clean -y
它将安装 gcc(包括 g++)编译器、FreeTDS、unixODBC 和 unixodbc-dev,以在下一步重新编译 PyODBC 驱动程序。
在原生 Windows 服务器或 PC 上安装 PyODBC 不需要这一步。
### **4. 从 Jupyter 中运行 PyODBC 安装**
!pip install pyodbc
Collecting pyodbc
Downloading pyodbc-4.0.30.tar.gz (266 kB)
|████████████████████████████████| 266 kB 11.3 MB/s eta 0:00:01
Building wheels for collected packages: pyodbc
Building wheel for pyodbc (setup.py) ... done
Created wheel for pyodbc: filename=pyodbc-4.0.30-cp36-cp36m-linux_x86_64.whl size=273453 sha256=b794c35f41e440441f2e79a95fead36d3aebfa74c0832a92647bb90c934688b3
Stored in directory: /root/.cache/pip/wheels/e3/3f/16/e11367542166d4f8a252c031ac3a4163d3b901b251ec71e905
Successfully built pyodbc
Installing collected packages: pyodbc
Successfully installed pyodbc-4.0.30
以上是这个 Docker 演示的最简化 pip 安装。 在[官方文档](https://irisdocs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=BNETODBC_support#BNETODBC_support_pyodbc)中,为“MacOS X 安装”提供了更详细的 pip 安装。
### **5 在 Linux 中重新配置 ODBC INI 文件和链接:**
运行以下命令重新创建 **odbcinst.ini** 和 **odbc.ini** 链接
!rm /etc/odbcinst.ini!rm /etc/odbc.ini
!ln -s /tf/odbcinst.ini /etc/odbcinst.ini!ln -s /tf/odbc.ini /etc/odbc.ini
注:这样的原因是,**第 3 步和第 4 步通常会在 \etc\ directory 下创建 2 个空白(因此无效)的 ODBC 文件。**与 Windows 安装不同,这里的空白 ini 文件会导致问题。因此需要先将其删除,然后重新创建一个链接来指向映射的 Docker 卷中提供的真实 ini 文件:/tf/odbcinst.ini 和 /tf/odbc.ini
看一看这两个 ini 文件。在这种情况下,它们是 Linux ODBC 配置的最简形式:
!cat /tf/odbcinst.ini
[InterSystems ODBC35]
UsageCount=1
Driver=/tf/libirisodbcu35.so
Setup=/tf/libirisodbcu35.so
SQLLevel=1
FileUsage=0
DriverODBCVer=02.10
ConnectFunctions=YYN
APILevel=1
DEBUG=1
CPTimeout=<not pooled>
!cat /tf/odbc.ini
[IRIS PyODBC Demo]
Driver=InterSystems ODBC35
Protocol=TCP
Host=irisimlsvr
Port=51773
Namespace=USER
UID=SUPERUSER
Password=SYS
Description=Sample namespace
Query Timeout=0
Static Cursors=0
以上文件都已预先配置,位于映射的驱动器中。 引用的是驱动程序文件 **libirisodbcu35.so**,可以从 IRIS 服务器的容器实例中获取该文件(在其 {iris-installation}/bin 目录下)。
要使上述 ODBC 安装正常运行,**这 3 个文件**必须存在于**具有正确文件权限**的映射驱动器(或任何 Linux 驱动器)中:
libirisodbcu35.so
odbcinst.ini
odbc.ini
**6. 验证 PyODBC 安装 **
!odbcinst -j
unixODBC 2.3.4
DRIVERS............: /etc/odbcinst.ini
SYSTEM DATA SOURCES: /etc/odbc.ini
FILE DATA SOURCES..: /etc/ODBCDataSources
USER DATA SOURCES..: /root/.odbc.ini
SQLULEN Size.......: 8
SQLLEN Size........: 8
SQLSETPOSIROW Size.: 8
import pyodbcprint(pyodbc.drivers())
['InterSystems ODBC35']
以上输出将表明 ODBC 驱动程序目前具有有效链接。
我们应该能够在 Jupyter Notebook 中运行一些 Python ODBC 测试
**7. 运行将 Python ODBC 连接到 IRIS 的示例:**
import pyodbc import time
### 1. Get an ODBC connection #input("Hit any key to start")dsn = 'IRIS PyODBC Demo'server = 'irisimlsvr' # IRIS server container or the docker machine's IP port = '51773' # or 8091 if docker machine IP is useddatabase = 'USER' username = 'SUPERUSER' password = 'SYS'
#cnxn = pyodbc.connect('DSN='+dsn+';') # use the user DSN defined in odbc.ini, or use the connection string belowcnxn = pyodbc.connect('DRIVER={InterSystems ODBC35};SERVER='+server+';PORT='+port+';DATABASE='+database+';UID='+username+';PWD='+ password)
###ensure it reads strings correctly.cnxn.setdecoding(pyodbc.SQL_CHAR, encoding='utf8')cnxn.setdecoding(pyodbc.SQL_WCHAR, encoding='utf8')cnxn.setencoding(encoding='utf8')
### 2. Get a cursor; start the timercursor = cnxn.cursor()start= time.clock()
### 3. specify the training data, and give a model namedataTable = 'DataMining.IrisDataset'dataTablePredict = 'Result12'dataColumn = 'Species'dataColumnPredict = "PredictedSpecies"modelName = "Flower12" #chose a name - must be unique in server end
### 4. Train and predict#cursor.execute("CREATE MODEL %s PREDICTING (%s) FROM %s" % (modelName, dataColumn, dataTable))#cursor.execute("TRAIN MODEL %s FROM %s" % (modelName, dataTable))#cursor.execute("Create Table %s (%s VARCHAR(100), %s VARCHAR(100))" % (dataTablePredict, dataColumnPredict, dataColumn))#cursor.execute("INSERT INTO %s SELECT TOP 20 PREDICT(%s) AS %s, %s FROM %s" % (dataTablePredict, modelName, dataColumnPredict, dataColumn, dataTable)) #cnxn.commit()
### 5. show the predict resultcursor.execute("SELECT * from %s ORDER BY ID" % dataTable) #or use dataTablePredict result by IntegratedML if you run step 4 aboverow = cursor.fetchone() while row: print(row) row = cursor.fetchone()
### 6. CLose and clean cnxn.close()end= time.clock()print ("Total elapsed time: ")print (end-start)
(1, 1.4, 0.2, 5.1, 3.5, 'Iris-setosa')
(2, 1.4, 0.2, 4.9, 3.0, 'Iris-setosa')
(3, 1.3, 0.2, 4.7, 3.2, 'Iris-setosa')
(4, 1.5, 0.2, 4.6, 3.1, 'Iris-setosa')
(5, 1.4, 0.2, 5.0, 3.6, 'Iris-setosa')
... ...
... ...
... ...
(146, 5.2, 2.3, 6.7, 3.0, 'Iris-virginica')
(147, 5.0, 1.9, 6.3, 2.5, 'Iris-virginica')
(148, 5.2, 2.0, 6.5, 3.0, 'Iris-virginica')
(149, 5.4, 2.3, 6.2, 3.4, 'Iris-virginica')
(150, 5.1, 1.8, 5.9, 3.0, 'Iris-virginica')
Total elapsed time:
0.023873000000000033
这里有一些陷阱:
1. **cnxn = pyodbc.connect() **- 在 Linux 环境下,此调用中传递的连接字符串必须正确无误,不能有任何空格。
2. 正确设置连接编码,例如使用 utf8。 在这里默认值对字符串不起作用。
3. **libirisodbcu35.so** - 理想情况下,此驱动程序文件应与远程 IRIS 服务器的版本保持一致。
## **未来计划 **
这样就得到一个带有 Jupyter Notebook 的 Docker 环境,包括 Python3 和 TensorFlow 2.2(无 GPU),通过 PyODBC(以及 JDBC)连接到远程 IRIS 服务器。 所有定制的 SQL 语法应该都可以适用,比如 IRIS Integrated ML 专有的 SQL 语法。那么何不多研究一下 IntegratedML 的功能,用它驱动 ML 生命周期的 SQL 方法以进行一些创新?
另外,我希望接下来能介绍或总结出在 IRIS Native 甚至是 Python 环境中的魔法 SQL 上最简单的 IRIS 服务器挂接方法。 而且,现在有出色的 [Python Gateway](https://github.com/intersystems-community/PythonGateway),我们甚至可以直接从 IRIS 服务器内部调用外部 Python ML 应用和服务。我希望我们也能在这方面多做些尝试。
**附录**
上面的笔记本文件也将被迁入此 Github 存储库以及 Open Exchange 中。
文章
姚 鑫 · 四月 2, 2021
# 第十五章 使用管理门户SQL接口(一)
本章介绍如何在InterSystems IRIS®数据平台管理门户上执行SQL操作。
管理门户界面使用动态SQL,这意味着在运行时准备和执行查询。
Management Portal界面旨在帮助针对小型数据集开发和测试SQL代码。
它不打算用作在生产环境中执行SQL的接口。
管理门户还提供了各种配置SQL的选项。
有关使用管理门户的一般信息,请选择左上角的Help按钮。
通过使用左上角的Contact按钮,可以从管理门户向InterSystems Worldwide Response Center (WRC)报告有关InterSystems软件的问题。
# 管理门户SQL工具
InterSystems IRIS允许使用SQL工具从InterSystems IRIS管理门户检查和操作数据。
此操作的起点是Management Portal System Explorer选项。
从这里选择SQL选项。
这将显示SQL接口,它允许:
- 执行SQL查询—编写和执行SQL命令。
可以对现有的表和数据执行SQL查询,创建表,或插入、更新或删除表数据。
可以编写SQL代码直接转化为一个文本框(包括选择、插入、更新、删除、创建表和其他SQL语句),检索语句的SQL历史文本框,拖拽一个表到文本框来生成一个查询(`SELECT`语句),或构成一个查询(`SELECT`语句)使用`query Builder`接口。
- 过滤模式内容——在屏幕左侧显示当前名称空间的SQL模式或这些模式的过滤子集,以及每个模式的表、视图、过程和缓存查询。
可以选择单独的表、视图、过程或缓存查询来显示其目录详细信息。
- 向导—执行向导,以执行数据导入、导出或数据迁移。
执行向导以链接到表或视图,或链接到存储过程。
- Actions -定义一个视图;
打印一个表定义的详细信息;
通过运行调优表和/或重建索引提高查询的性能;
或者通过清除不需要的缓存查询和/或删除不需要的表、视图或过程定义来执行清理。
- 打开表格——以显示模式在表格中显示当前数据。
这通常不是表中的完整数据:记录的数量和列中的数据长度都受到限制,以提供可管理的显示。
- 工具——执行以下工具之一:SQL运行时统计、索引分析器、备用显示计划、生成报告、导入报告。
- 文档—允许查看SQL错误代码列表和SQL保留字列表。
如果选择了一个表,则允许显示类文档(该表的类引用页)。
## 选择命名空间
所有SQL操作都会在特定名称空间中进行。因此,必须首先指定要通过单击SQL接口页面顶部的 **“开关switch”** 选项要使用的命名空间。这将显示可用名称空间列表,可以从中进行选择。
可以设置管理门户默认命名空间。从管理门户选择系统管理,安全性,用户。单击所需用户的名称。这允许编辑用户定义。从“常规”选项卡中,从下拉列表中选择“启动命名”空间。单击“保存”。如果未选择启动命名空间,则会默认为%SYS.。
## 用户自定义
许多Management Portal SQL操作都是为每个用户自动定制的。
如果在Execute Query选项卡或SQL Statements选项卡中设置了筛选器、最大值、模式或其他选项,则此用户指定的值将保留以供将来使用。
当同一个用户激活管理门户时,将显示该用户先前的设置。
重新启动InterSystems IRIS返回所有选项为默认值。
没有自定义名称空间选择。
它恢复到用户定义启动名称空间。
# 执行SQL查询
从管理门户选择System Explorer,然后选择SQL。
在页面顶部选择带有Switch选项的名称空间;
这将显示可用名称空间的列表。
要执行SQL查询,有三个选项:
- Execute Query:写并执行SQL命令。
SQL命令可以是一个`SELECT`查询,也可以是一个InterSystems SQL DDL或DML语句;
语句执行时在InterSystems IRIS服务器上验证。
- Show History:收回以前运行的SQL语句,然后重新运行它,或者修改它,然后运行它。
列出所有已执行的语句,包括未成功执行的语句。
- 查询生成器:调用SQL查询生成器(它专门用于创建SELECT语句)。
在SQL Query Builder中,通过选择表、列、`WHERE`子句谓词和其他查询组件来创建SQL `SELECT`查询。
然后,可以通过单击Execute query来运行查询。
## 编写SQL语句
Execute Query文本框不仅允许编写`SELECT`和`CALL`查询,还允许编写大多数SQL语句,包括DDL语句(如`CREATE TABLE`)和DML语句(如`INSERT`、`UPDATE`和`DELETE`)。
可以在“执行查询”文本框中指定SQL代码:
- 将SQL代码键入(或粘贴)到文本框中。
SQL代码区域不给SQL文本着色,也不提供任何语法或存在验证。
但是,它确实提供了自动拼写验证。
可以使用X图标删除文本框的内容。
- 使用Show History列表选择前面的SQL语句。
选中的语句将复制到文本框中。
执行时,该语句移到Show History列表的顶部。
注意,Show History列出了之前执行的所有语句,包括那些执行失败的语句。
- 使用表拖放在文本框中构造SQL代码。
- 可以使用Query Builder(而不是Execute Query文本框)来指定和执行`SELECT`查询。
使用查询生成器执行的选择查询不会显示在“执行查询”中,也不会列出在“显示历史”中。
Execute Query文本框中的SQL代码可以包括:
- ?输入参数。如果指定输入参数,例如 `TOP ? or WHERE Age BETWEEN ? AND ?`,`Execute`按钮显示查询窗口的Enter参数值,其中每个输入参数的条目字段按查询中指定的顺序。
- 空白字符。可以指定多个空格,单个和多行返回。标签键已禁用;将代码复制到SQL代码区域时,现有选项卡将转换为单个空格。线返回和未保留多个空格。
- 注释。 SQL代码区域支持单行和多行注释。在Show历史显示中保留并显示注释。在Show Plan语句文本显示或缓存查询中未显示注释。
- 返回多个结果集的查询。
在文本框中编写SQL代码后,可以单击“显示计划”按钮查看SQL代码而不执行SQL代码。如果代码有效,则显示计划显示查询计划。如果代码无效,则显示计划显示SQLCode错误值和消息。还可以使用“显示计划”按钮显示最近执行的SQL代码的此信息。
要执行SQL代码,请单击“执行”按钮。
### 表拖放
可以通过从屏幕左侧的表列表(或视图列表)拖动表(或视图)来生成查询,并将其丢弃到执行查询文本框中。这在表中生成了选择的选项列表,以及指定表的表中的所有非隐藏字段。然后,可以进一步修改此查询并使用Execute按钮执行它。
还可以从屏幕左侧的过程列表中拖放过程名称。
## 执行查询选项
SQL执行界面具有以下选项:
- 具有`SELECT`的“选择模式下拉列表”指定查询应用于提供数据值(例如,在WHERE子句中)的格式,并在查询结果集中显示数据值。选项是显示模式(默认值),ODBC模式和逻辑模式。
具有插入或更新的选择模式下拉列表允许指定输入数据是否将从显示格式转换为逻辑存储格式。对于此数据转换,必须使用选择运行时的选择模式编译SQL代码。在执行时间时,必须将“选择模式”下拉列表设置为逻辑模式。
选择模式对于数据类型是有意义的,其逻辑存储格式与所需的显示格式(显示或ODBC)不同,例如Intersystems Iris日期和时间和Objectscript`%List`结构化数据。
- 最大字段允许限制从查询返回的数量数量。它可以设置为任何正整数,包括0.一旦设置MAX,除非显式更改,否则将该值用于会话持续时间的所有查询。默认值为1000.最大值为100,000,如果输入没有值(将MAX设置为NULL),则输入大于100,000或非数值的值,这是默认值。还可以使用顶部子句限制要返回的数据行数。 MAX对其他SQL语句没有影响,例如删除。
如果单击“更多”选项,则SQL执行界面将显示以下其他选项:
- 方言:SQL代码的方言。包括“IRIS”、“Sybase”和“MSSQL”。默认为IRIS。
在InterSystems Transact-SQL (TSQL)迁移指南中描述了Sybase和MSSQL。
请注意,下次访问管理门户时,选择的方言将成为用户自定义的默认语言。
- 行号:一个复选框,指定是否在结果集中显示的每一行中包含行计数号。
行号是分配给结果集中每一行的连续整数。它只是对返回的行进行编号,它既不对应rowwid也不对应`%VID`。行号列标题名是#。默认是显示行号。
所有这些选项都是用户自定义的。
## 显示计划按钮
Show Plan按钮在页面的文本框中显示语句文本和查询计划,包括查询的当前查询计划的相对成本(开销)。可以从Execute查询或Show History接口调用Show Plan。查询计划是在准备(编译)查询时生成的;
当编写查询并选择Show Plan按钮时,就会发生这种情况。不必执行查询来显示其查询计划。Show Plan在为无效查询调用时显示`SQLCODE`和错误消息。
## SQL语句的结果
在“执行查询”文本框中编写SQL代码之后,可以通过单击“执行”按钮来执行代码。这要么成功执行SQL语句并在代码窗口下面显示结果,要么SQL代码失败。如果SQL代码失败,它会在code窗口下面显示一条错误消息(红色);
按下Show Plan按钮将显示`SQLCODE`错误和错误消息。
执行查询SQL代码执行作为后台进程执行。在执行代码时,Execute按钮被Cancel按钮替换。这允许取消长时间运行的查询的执行。
### 查询数据显示
如果选中了行号框,结果集将作为表返回,行计数器将显示为第一列(`#`)。
其余的列将按照指定的顺序显示。RowID (ID字段)可以显示或隐藏。每个列都由列名(如果指定了,也可以是列别名)标识。聚合、表达式、子查询、主机变量或文字选择项可以由列别名(如果指定)标识,或者由单词`Aggregate_`、`Expression_`、`Subquery_`、`HostVar_`或`Literal_`后跟选择项序列号(默认情况下)标识。
如果行列不包含数据(`NULL`),结果集将显示一个空白的表格单元格。
指定一个空字符串文本将显示一个`HostVar_`字段,其中包含一个空白的表格单元格。
指定`NULL`显示一个带有空白单元格的`Literal_`字段。
如果选择的字段是日期、时间、时间戳或%List编码的字段,则显示的值取决于显示模式。
以下显示特性是管理门户SQL接口独有的,执行查询结果显示和打开表数据显示:
- 数据类型`%Stream.Globalcharacter`的流字段将实际数据(最多100个字符)作为字符串显示。如果流字段中的数据长于100个字符,则显示数据的前100个字符,后跟省略的省略号(`...`)。
- 数据类型`%Stream.GlobalBinary`作为的流字段。
- 字符串数据字段根据需要,以完整的方式显示实际数据。
- Integer字段在结果表单元格中右对齐。 `ROWID`,`NUMERIC`和所有其他字段都是左对齐的。
当使用动态SQL代码,SQL Shell或嵌入式SQL代码执行相同的查询时,不会发生这些结果显示功能。
如果指定的查询返回多个结果集,则执行查询将这些结果集显示为命名选项卡:`Result #1`, `Result #2`等。
### 查询执行指标
如果成功,则执行查询显示性能信息和缓存查询例程的名称。如果显示数据以显示,则显示在性能信息下方。执行信息包括行计数,性能,缓存查询,显示缓存的查询名称,最后更新指定查询的最后一次执行的时间戳。
- Row count:对于`CREATE TABLE`这样的DDL语句,如果操作成功,则显示`Row count: 0`。
对于`INSERT`、`UPDATE`或`DELETE`等DML语句,显示受影响的行数。
对于`TRUNCATE TABLE`语句,快速`TRUNCATE`操作不能确定实际删除的行数,而是设置行数:-1。
对于`SELECT`,显示作为结果集返回的行数。注意,返回的行数由Max设置控制,它可能低于可以选择的行数。
对于多个结果集,列出每个结果集的行数,用/字符分隔。
指定一个或多个聚合函数(且没有选择字段)的查询总是显示`Row count: 1`,并返回表达式、子查询和聚合函数的结果,即使`FROM`子句表不包含行。
一个不指定聚合函数和不选择行的查询总是显示`Row count: 0`并且不返回结果,即使该查询只指定不引用`FROM`子句表的表达式和子查询。
带`no FROM`子句的查询总是显示`行数:1`,并返回表达式、子查询和聚合函数的结果。
- 性能:以运行时间(以秒为单位)、全局引用总数、执行的命令总数和磁盘读取延迟(以毫秒为单位)来衡量。
如果该查询存在缓存的查询,那么这些性能指标将用于执行缓存的查询。
因此,查询的第一次执行将比后续执行具有更高的性能指标。
如果指定的查询返回多个结果集,那么这些性能指标就是所有查询的总和。
要更深入地分析这些性能指标,可以运行`MONLBL`(逐行监视实用程序)并使用星号通配符`%sqlcq*`指定例程名称。
请参考使用`^%SYS.MONLBL`检查例程性能。
- 缓存查询:自动生成的缓存查询类名。
例如,`%sqlcq.USER.cls2`表示用户名称空间中的第二个缓存查询。
每个新的查询被分配一个新的缓存的查询名称,该名称具有下一个连续的整数。
通过单击此缓存查询名称,以显示关于缓存查询的信息,以及显示其显示计划或执行缓存查询的进一步链接。
关闭管理门户或停止InterSystems IRIS不会删除缓存的查询或重置缓存的查询编号。
要从当前命名空间中清除缓存的查询,请调用%`SYSTEM.SQL.Purge()`方法。
并不是所有的SQL语句都会导致缓存的查询。与现有缓存查询相同的查询,除了文字替换值(例如`TOP`子句值和谓词文字)之外,不会创建新的缓存查询。有些SQL语句是不缓存的,包括DDL语句和权限分配语句。
非查询SQL语句,如`CREATE TABLE`,也会显示缓存的查询名。
然而,这个缓存的查询名称被创建然后立即删除;
下一个SQL语句(查询或非查询)重用相同的缓存查询名称。
- 最后一次更新:最后一次执行查询(或其他SQL操作)的日期和时间。
这个时间戳在每次执行查询时都被重置,即使在重复执行相同的查询时也是如此。
- 成功执行还提供了一个打印链接显示打印查询窗口,它给你选择打印或导出到一个文件中查询文本和/或查询的结果集。点击查询和结果切换使可以显示或隐藏文本或查询结果集的查询,查询结果集显示包含名称空间的名字,结果集的数据行数,一个时间戳,缓存的查询名称。
(注意,时间戳是调用Print查询窗口的时间,而不是执行查询的时间。)
“打印查询”按钮用于打印查询窗口的屏幕截图。
“导出到文件”复选框显示指定导出文件格式(`xml、hdml、pdf、txt、csv`)和导出文件路径名的选项。
Export选项忽略查询和结果切换,并始终只导出结果集数据(默认为`:exportQuery.pdf`)和行数(默认为`:exportQueryMessages.pdf`);
不包括查询文本、名称空间、时间戳和缓存的查询名称。
如果不成功,则Execute Query显示错误消息。
可以单击Show Plan按钮来显示相应的`SQLCODE`错误值和消息。
## 显示历史
单击“显示历史记录”可列出当前会话期间执行的SQL语句。
Show History列出从该接口调用的所有SQL语句,包括那些成功执行和那些执行失败的语句。
默认情况下,SQL语句按执行时间列出,最近执行的语句出现在列表的顶部。可以单击任何列标题,根据列值按升序或降序排列SQL语句。从Show History列表中执行SQL语句将更新其执行时间(本地日期和时间戳),并增加其计数(执行次数)。
可以过滤Show History列表,如下所示:在过滤框中指定一个字符串,然后按Tab键。只有包含该字符串的历史项才会包含在刷新后的列表中。
筛选器字符串可以是在SQL语句列中找到的字符串(比如表名),也可以是在执行时间列中找到的字符串(比如日期)。
过滤字符串不区分大小写。
在显式地更改过滤器字符串之前,它将一直有效。
通过选择语句,可以在“Show History”中修改和执行SQL语句,该语句将显示在“execute Query”文本框中。
在“执行查询”中,可以修改SQL代码,然后单击“执行”。
对从Show History中检索到的SQL语句进行任何更改,都会将其作为新语句存储在Show History中;
这包括不影响执行的更改,如更改字母大小写、空格或注释。
空格不会显示在Show History中,但是当从Show History中检索SQL语句时,会保留空格。
通过单击Show History列表中SQL语句右侧的execute按钮,可以直接从Show History列表中执行(重新运行)未修改的SQL语句。
注意,Show History列表与缓存查询列表不同。
Show History列出当前会话中调用的所有SQL语句,包括那些在执行过程中失败的语句。
## 其他SQL接口
InterSystems IRIS支持许多其他编写和执行SQL代码的方法,在本手册的其他章节中有描述。
这些包括:
- 嵌入式SQL:嵌入ObjectScript代码中的SQL代码。
- 动态SQL:使用%SQL。
语句类方法(或其他结果集类方法)用于从ObjectScript代码中执行SQL语句。
- SQL Shell:在终端使用SQL Shell接口执行动态SQL。
文章
Michael Lei · 六月 6, 2023
大家好!
我相信很多人都希望出席 2023 年全球峰会,但出于某种原因无法出席。以下是对 6 月 4 日和 5 日发生的事情的简要回顾。
尽管正式的开幕式要到周日晚上才会举行,但峰会前的活动还是很精彩的。亮点之一(特别是对于这里的体育爱好者而言)是早上的高尔夫锦标赛和足球(又名足球)比赛。不能说关于足球的任何事情,因为我不在场(如果你在场,请在评论部分发表评论和照片!),但据我所知,这是一场有趣的比赛。我可以告诉你关于高尔夫的事!
比赛从早上 6 点开始,一直持续到下午 2 点左右。一开始天气阴沉,雨刚下完。但几个小时后,太阳出现了,防晒霜也随之出现。让我告诉你,50SPF 感觉不够!
为了让它更有趣,当地的动物群决定也出去玩。
在宣布获奖者并享用午餐后,我们返回 The Diplomat Beach Hotel 参加其他活动。
例如,下午有一场女性活动。
这导致周日的主要活动 - 欢迎酒会!这是看到熟悉的面孔并与同事和朋友取得联系的最佳时机。
例如,英语社区的版主(@Muhammad.Waseem、@Dmitry.Maslennikov、@Irène.Mykhailova):
和法语社区(@Lorenzo.Scalese、@Irène.Mykhailova、@Dmitry.Maslennikov、@Guillaume.Rongier7183):
这是“准备”日。主要内容从周一开始!
Terry Ragon 开启了峰会,在他的演讲中提醒大家任何成功的公司都必须创新才能保持相关性。 InterSystems 就是这样的公司之一!
此外,他祝贺大家 InterSystems 成立 45 周年和第 30 届全球峰会!这不是令人兴奋吗?不知道你怎么想,公司成立的时候我还没有出生呢!
开场白之后,第一个主题演讲是关于医疗保健的。其中一点 @Donald.Woodlock 谈到了AI 以及它如何拉近开发人员与客户的距离,而不是相反。
在主题演讲(和午餐)之后,每个人都分散到不同的会议、演讲、会议等。我回到了 Tech Exchange 的开发者社区展位。
如果你在峰会上,别忘了顺路过来——我很乐意给你一些礼物作为简单行动的回报 😘 如果你认识一些还不是社区成员的人,鼓励他们过来也得到一些赃物。此外,请查看全球大师赛- 峰会期间可兑换一些奖励:
还有一些新的(和旧的最爱)奖励!有传言说,我们甚至很快就会有笔记本电脑贴纸!
无论如何,在 DevCom 展位让我有机会与新朋友交谈,并结识其他有空的版主。
@Guillaume.Rongier7183 和@Dmitry.Maslennikov
@Lorenzo.Scalese、@Dmitry.Maslennikov 和@Francisco.López1549
如果您对背景中的演示文稿感兴趣,请看这里。
此外,参加 Tech Exchange 意味着我可以关注那里正在进行的所有演示。例如,了解容器 😉
在所有这些兴奋之后,我们都去吃晚餐和演示。
玩游戏,玩得很开心。
这是 2023 年全球峰会的第一个官方日。敬请期待接下来的活动!
文章
Claire Zheng · 九月 9, 2021
在医院信息化领域,中日联谊医院最近一次站在行业瞩目的聚光灯下,是在2021年7月。在国家卫生健康委统计信息中心发布的《关于2020年度国家医疗健康信息互联互通标准化成熟度测评结果公示的通知》中,9家医院获评五级乙等,中日联谊医院名列其中。这也是目前为数不多的“通关”互联互通五乙的医院之一。2021年是《国家医疗健康信息医院信息互联互通标准化成熟度测评方案(2020年版)》(以下简称新版测评方案)施行的第一个年头。由于测评方案的细化、参评机构的数量增多,业界普遍反映医院互联互通测评的整体难度正在逐年增大。而五级乙等,已是医疗机构目前所能取得的最高级别。
在互联互通新版测评方案中,对互联互通功能的“实际落地”与“有效应用”提出了很高的要求。在五级乙等的分级要求中就明确提出:“实现公众服务应用功能数量不少于27个、医疗服务应用功能数量不少于30个;提供较为完善的互联网诊疗服务,初步实现基于平台的临床决策支持、闭环管理、大数据应用。”
医院信息化建设立足于实际应用,为医院的医疗服务与运营管理提供助力,这是中日联谊医院能够获评五级乙等的重要原因之一。
医院在参与测评的过程中,遇到的首要问题即异构系统的接入问题。互联互通新版测评方案对医院信息系统内外联通业务的数量提出了明确要求,其中对五级乙等的分级要求是:集成平台至少需要接入54个内部业务系统,以及不少于7个外部机构数量,实现基于平台的内部与外部联通业务。
为达到上述要求,医疗机构需有效解决异构系统间的数据交互问题,完成交互服务接口和闭环管理改造。中日联谊医院采用了“东华医为医院信息平台产品”,该产品依托InterSystems技术实现,是互联互通标准化成熟度测评工作的基础。InterSystems数据平台技术不仅稳定可靠,有效支撑数百万级的大规模消息吞吐交换,而且以其强大的可扩展性、易用性和互操作能力,使各异构系统的直接连接更简单,降低了交互服务接口开发的时间和成本,帮助医院高质、高效地推进了业务联通工作。
点击此次,阅读全文
文章
Louis Lu · 一月 7, 2021
**RHEL V7.2 上的 Caché 进程故障**
InterSystems WRC 处理了几个有关进程错误引发的问题,这些问题可以归因于 Red Hat Linux 最近的一次更新。
RHEL V7.2 (systemd-219-19.el7.x86_64) 中实现的一个新功能可能导致操作系统 IPC(进程间通信)信号量在 非系统用户注销时被解除分配(系统用户,即 UID 编号小于 1000 的用户除外)。
Caché 在内部利用 IPC 信号量来控制 Caché 进程的运行(例如,当尝试唤醒 Caché 进程时)。 这通过“semop”系统服务来实现,如果操作系统意外删除了 Caché 用于进行 IPC 通讯的信号量,则进程可能会出现错误。 如果发生这种情况,在 cconsole.log 中会找到以下证据:
“System error while trying to wake-up a process, code = 22”(尝试唤醒进程时系统出错,代码 = 22)
以及在 Caché SYSLOG 中也会记录相应的错误,例如以下典型示例:
Err Process Date/Time Mod Line Routine Namespace
22 39761 09/29/2016 04:41:27PM 61 359 BF0+1359^Ens.Queue.1 HSBUS
这最终可能导致 Caché 的运行实例处于挂起状态。
以下是 Redhat 提供的一篇文章的链接,文中给出了有关此功能的详细信息以及禁用该功能的方法:
[https://access.redhat.com/solutions/2062273](https://access.redhat.com/solutions/2062273 "Follow link")
此问题已在 systemd-219-19.el7_2.4(通过 RHBA-2016-0199 发布 ()中修复。
文章
Hao Ma · 一月 15, 2021
什么是npm-iris?
NPM是“No Project Mess(项目不乱)”的缩写。
NPM是使用Intersystems IRIS和Bootstrp 4建成的项目和任务管理应用程序。
NPM的创建初衷是通过一个简单直观的项目和任务管理软件,帮助开发者和小型商业公司降低日常问题的复杂度。
它能提供不同的任务视图,包括电子表格、看板、日历,甚至甘特图!
为什么?
在不同的团队中工作,您会发现不同的人喜欢不同的工具。
所以,有时您会在一个项目中使用甘特图,在另一个项目中使用看板,在其他项目中使用纸上的列表……
NPM专注于任务。无论您和您的团队喜欢以哪种方式查看。只需单击并更改您的视图。
功能
初始安装
项目
用户
任务 - 创建和管理任务
调度程序 - 任务的日历视图
看板 - 用看板风格管理您的任务
甘特图 - 使用甘特图查看截止日期、里程碑和进度
新特性/改进的路线图
OAuth2身份验证
项目/团队/用户的安全性
时间跟踪
自定义日历(假期)
支持附件
利用AppS.REST框架
Vue.js版本
Home面板,可以查看活动的概况
试一下这款应用程序! http://npm-iris.eastus.cloudapp.azure.com:52773/npm/home.csp
如果您喜欢这个应用程序,并认为我值得您的投票,请投票给npm-iris! https://openexchange.intersystems.com/contest/current
问题
water huang · 四月 17, 2021
如图 dll放在
我调用的方式如下
期待能够在ensemble里面能便捷的调用dll,各种语言开发的dll,至少能支持c#生成的dll, 在Ensemble 2016中,使用$ZF(-4)来操作DLL是Caché的Callout网关的底层实现之一。这种方式比较通用。当然,如果这些DLL(ActiveX/COM)注册在Ensemble服务器上(Windows服务器),还有别的调用方式:使用Caché Activate 网关,用Studio来产生这些DLL的代理类,然后您就可以像使用Ensemble/Caché类一样使用这些DLL里的方法了。 使用Caché Activate 网关 这个并不好用。服务器重启后 需要重启网关,还很可能需要重新导入dll来生成代理类。因此这样的方式 我已经弃用,或者说 “可能需要重新导入dll来生成代理类” 这个是我操作不对才导致这样的结果?使用$ZF(-4)来操作DLL,这个dll具有一些特殊要求?比如? 如果dll重新编译过,那么是需要重新生成代理类的;其它情况下并不需要。另外,Ensemble 2016里有.net 网关,通过Studio来建立dll代理类。不过它依然需要在dll代码发生变化时重新生成代理类。
这也是后续版本(尤其是InterSystems IRIS)推出动态对象网关的原因:通过动态网关就不用生成代理类了,从而避免因为.net/java端发生代码变更而需要重新生成代理类。$ZF()对DLL没有特殊要求。
文章
Michael Lei · 三月 10, 2023
InterSystems IRIS 2022.2 具有适用于 Python 的原生 SDK (https://docs.intersystems.com/iris20222/csp/docbook/Doc.View.cls?KEY=PAGE_python_native)。
我们知道如何使用 IRIS Object Script $Order 函数遍历Global数据结构。
SET key= "" FOR { SET key= $ORDER ( ^myglobal (key)) QUIT :key= "" WRITE !, ^myglobal (key) }
如何使用 IRIS Native SDK 从 Python 执行相同的操作?这里有一个代码示例:
import iris
args = { 'hostname' : '127.0.0.1' , 'port' : 51772 , 'namespace' : 'USER' , 'username' : '_SYSTEM' , 'password' : 'SYS' }
conn = iris.connect(**args)
# Create an iris object
irispy = iris.createIRIS(conn)
# Create a global array in the USER namespace on the server
irispy.set( 'A' , 'root' , 'foo' , 'SubFoo' )
irispy.set( 123 , 'root' , 'bar' , 'lowbar' , 'UnderBar' )
irispy.set( 124 , 'root' , 'bar' , 'lowbar' , 'UnderBar2' )
irispy.set( "hi" , 'root' , 'bar' , 'lowbar' )
irispy.set( "hi again" , 'root' , 'bar3' )
# Read the values from the database and print them
subfoo_value = irispy.get( 'root' , 'foo' , 'SubFoo' )
underbar_value = irispy.get( 'root' , 'bar' , 'lowbar' , 'UnderBar' )
underbar2_value = irispy.get( 'root' , 'bar' , 'lowbar' , 'UnderBar2' )
lowbar_value = irispy.get( 'root' , 'bar' , 'lowbar' )
bar3_value = irispy.get( 'root' , 'bar3' )
print( 'Created two values: ' )
print( ' root("foo","SubFoo")=' , subfoo_value)
print( ' root("bar","lowbar","UnderBar")=' , underbar_value)
print( ' root("bar","lowbar","UnderBar2")=' , underbar2_value)
print( ' root("bar","lowbar")=' , lowbar_value)
print( ' root("bar3")=' , bar3_value)
direction = 0 # direction of iteration (boolean forward/reverse)
next_sub = chr( 0 ) # start at first possible subscript
subs = []
print( "\n Iterating root \n" )
isDef = irispy.isDefined( 'root' , *subs)
while isDef:
next_sub = irispy.nextSubscript( False , 'root' , *subs, next_sub) # get first subscript
if next_sub == None : # we finished iterating nodes on this tree branch, move a level up
if len(subs) == 0 : # no more things to iterate
break
next_sub = subs.pop( -1 ) # pop last subscript in order to continue iterating this level
if irispy.isDefined( 'root' , *subs, next_sub) == 11 :
print( 'root(' ,*subs, next_sub, ')=' ,irispy.get( 'root' , *subs, next_sub))
continue
continue
isDef = irispy.isDefined( 'root' , *subs, next_sub)
if isDef in [ 10 , 11 ]: # keep building subscripts for depth first search
subs.append(next_sub)
next_sub = chr( 0 )
continue
elif isDef == 1 : # reached a leaf node, print it
print( 'root(' ,*subs, next_sub, ')=' ,irispy.get( 'root' , *subs, next_sub))
else : # def 0 is not really expected
print( "error" )
irispy.kill( 'root' )
conn.close()
exit( -1 )
# Delete the global array and terminate
irispy.kill( 'root' ) # delete global array root
conn.close()
文章
Lele Yang · 一月 30, 2022
Linux内核机制OOM Killer,也即Out of Memory Killer, 顾名思义,该机制的主要职能就是当内存不足时,选择并杀掉一些进程,以使系统继续运行。
Caché/Ensemble/IRIS的多个客户曾经遇到过与此相关的系统宕机,宕机的直接原因是数据库核心写进程Write Daemon被OOM Killer选中并杀掉了,在我们的日志文件中可以看到如下信息,
06/15/21-10:50:31:035 (13579) 3 Daemon WRTDMN (pid 13588) died. Freezing system
06/15/21-10:52:25:940 (13601) 2 System Process 'WRTDMN' terminated abnormally (pid 13588)
与之对应,在操作系统的日志文件中可以看到如下记录,
Jun 15 10:50:34 localhost kernel: Free swap = 0kB
Jun 15 10:50:34 localhost kernel: Total swap = 20479996kB
Jun 15 10:50:34 localhost kernel: 16777102 pages RAM
Jun 15 10:50:34 localhost kernel: 0 pages HighMem/MovableOnly
Jun 15 10:50:34 localhost kernel: 324506 pages reserved
Jun 15 10:50:30 localhost kernel: cache invoked oom-killer: gfp_mask=0x42d0, order=3, oom_score_adj=0
Jun 15 10:50:35 localhost kernel: Out of memory: Kill process 13588 (cache) score 127 or sacrifice child
InterSystems在后续的IRIS版本中(从IRIS2021.1.0开始)已经对此做了优化,以使Write Daemon不那么容易被OOM Killer选中。但是要从根本上解决该问题,还是应当重新审视系统的内存分配,如Huge Page,共享内存等,检查Linux内存相关参数,如vm.swappiness,vm.dirty_background_ratio,vm.dirty_ratio等,以使系统可以在内存使用方面达到最大的效用。