清除过滤器
文章
姚 鑫 · 六月 18, 2021
# 第十一章 重新定义读取器处理相关对象的方式
# 重新定义读取器处理相关对象的方式
当`%XML.Reader`找到与启用了XML的类相关的XML元素时,读取器会调用该类的`XMLNew()`方法,后者又会在默认情况下调用`%New()`。也就是说,当读取器找到相关元素时,它会创建相关类的新对象。新对象由从XML文档读取的数据填充。
可以通过在启用XML的类中(或在自己的自定义XML适配器中)重新定义`XMLNew()`来自定义此行为。例如,此方法可以改为打开该类的现有实例。然后,现有实例接收从XML文档读取的数据。
以下示例显示如何修改`XMLNew()`以使用XML文档中的新数据更新现有实例。
在这两个示例中,为简单起见,我们假设XML文档中的一个节点包含一个ID,我们可以将该ID与类的范围中的ID进行比较。当然,我们可以用其他方式将XML文档与现有对象进行比较。
## 当`%XML.Reader`调用`XMLNew()`时
作为参考,`%XML.Reader`在两种情况下自动调用`XMLNew()`方法:
- `%XML.Reader`在调用`%XML.Reader`的`Next()`方法调用`XMLNew()`。在将XML元素(在外部文档中)与启用了XML的类关联之后,`%XML.Reader` `Next()`方法从文档中获取下一个元素,调用`XMLNew()`创建相应对象的实例,然后将该元素导入到对象中。
- 同样,`%XML.Reader`为相关XML元素的任何对象值属性调用`XMLNew()`。
## 示例1:修改启用XML的类中的`XMLNew()`
```java
4
Quine,Maria K.
1964-11-14
Hialeah
94999
Vanzetti,Debra B.
...
```
此文件映射到以下InterSystems IRIS类(部分显示):
```java
Class GXML.PersonWithXMLNew Extends (%Persistent, %Populate, %XML.Adaptor)
{
Parameter XMLNAME = "Person";
/// make sure this is the same as the XMLNAME of the property
/// in this class that is of type %XML.Id
Parameter NAMEOFEXPORTID As %String = "IRISID";
Property IdForExport As %XML.Id(XMLNAME = "IRISID", XMLPROJECTION = "ELEMENT") [ Private, Transient ];
Property Name As %Name;
Property DOB As %Date(FORMAT = 5, MAXVAL = "+$h");
Property Address As GXML.Address;
Property Doctors As list Of GXML.Doctor;
```
在该类中,`IdForExport`属性的用途是在导出该类的对象时将InterSystems IRIS内部ID投影到元素(`IRISID`)。(在此特定示例中,这使我们能够轻松地生成适合导入的文件。类不必包含这样的属性。)
`NAMEOFEXPORTID`参数用于指示导出此类对象时用于InterSystems IRIS ID的元素。包含这一点只是为了方便自定义的`XMLNew()`方法,我们也将该方法添加到该类中。该方法定义如下:
```java
ClassMethod XMLNew(doc As %XML.Document, node As %Integer, contOref As %RegisteredObject = "") As GXML.PersonWithXMLNew
{
Set id=""
Set tmpnode=doc.GetNode(node)
Do tmpnode.MoveToFirstChild()
Do {
//将数据节点与NAMEOFEXPORTID参数提供的字符串进行比较
//指示此对象的ID的XMLNAME
If tmpnode.NodeData=..#NAMEOFEXPORTID {
//从该节点获取文本;这与数据库中的id相对应
Do tmpnode.GetText(.id)}
} While tmpnode.MoveToNextSibling()
//如果给定节点中没有id,则创建一个新对象
If id="" {
Write !, "正在创建新对象..."
Quit ..%New()}
//打开给定对象
Set result=..%OpenId(id)
//如果id与现有对象不对应,则创建一个新对象
If result=$$$NULLOREF {
Write !, "正在创建新对象..."
Quit ..%New()}
Write !, "正在更新现有对象..."
Quit result
}
```
`%XML.Reader`读取`XML`文档并将节点关联到`GXML.PersonWithXMLNew`时调用此方法。此方法查看此类中的`NAMEOFEXPORTID`参数的值,即`IRISID`。然后,它使用元素`IRISID`检查文档中的节点并获取其值。
如果此`ID`对应于此类的现有对象,则该方法将打开该实例。否则,该方法将打开此类的新实例。在这两种情况下,实例都会接收`XML`文档中指定的属性。
最后,以下实用程序类包含一个方法,该方法打开`XML`文件并在新窗口中调用`%XML.Reader`:
```java
/// w ##class(PHA.TEST.Xml).ReadFile()
ClassMethod ReadFile(filename As %String = "e:\temp\xmlnewtest.xml")
{
Set reader=##class(%XML.Reader).%New()
Set sc=reader.OpenFile(filename)
If $$$ISERR(sc) {Do $system.OBJ.DisplayError(sc) Quit }
Do reader.Correlate("Person","GXML.PersonWithXMLNew")
//loop through elements in file
While reader.Next(.person,.sc) {
Write !,person.Name,!
Set sc=person.%Save()
If $$$ISERR(sc) {Do $system.OBJ.DisplayError(sc) Quit }
}
Quit ""
}
```
运行上述方法时,文件中的每个``元素都会发生以下情况之一:
- 打开现有对象,使用文件中的详细信息进行更新,然后保存。
- 或者创建一个新对象,其中包含文件中的详细信息。
```java
DHC-APP>w ##class(PHA.TEST.Xml).ReadFile()
正在创建新对象...
Quine,Maria K.
DHC-APP>w ##class(PHA.TEST.Xml).ReadFile()
正在创建新对象...
Quine,Maria K.
DHC-APP>w ##class(PHA.TEST.Xml).ReadFile()
正在创建新对象...
Quine,Maria K.
DHC-APP>w ##class(PHA.TEST.Xml).ReadFile()
正在更新现有对象...
Quine,Maria K.
DHC-APP>w ##class(PHA.TEST.Xml).ReadFile()
正在更新现有对象...
Quine,Maria K.
DHC-APP>w ##class(PHA.TEST.Xml).ReadFile()
正在更新现有对象...
Quine,Maria K.
```
## 示例2:在自定义XML适配器中修改XMLNew()
在第二个示例中,我们创建一个自定义XML适配器来执行与第一个示例相同的操作。适配器类如下所示:
```java
Class GXML.AdaptorWithXMLNew Extends %XML.Adaptor
{
/// 确保这与该类中属性的XMLNAME相同 它的类型为%XML.Id
Parameter NAMEOFEXPORTID As %String = "IRISID";
Property IdForExport As %XML.Id(XMLNAME = "IRISID", XMLPROJECTION = "ELEMENT") [ Private, Transient ];
ClassMethod XMLNew(document As %XML.Document, node As %Integer, containerOref As %RegisteredObject = "") As %RegisteredObject [ CodeMode = objectgenerator, GenerateAfter = %XMLGenerate, ServerOnly = 1 ]
{
If %compiledclass.Name'="GXML.AdaptorWithXMLNew" {
Do %code.WriteLine(" Set id=""""")
Do %code.WriteLine(" Set tmpnode=document.GetNode(node)")
Do %code.WriteLine(" Do tmpnode.MoveToFirstChild()")
Do %code.WriteLine(" Do {")
Do %code.WriteLine(" If tmpnode.NodeData=..#NAMEOFEXPORTID ")
Do %code.WriteLine(" {Do tmpnode.GetText(.id)}")
Do %code.WriteLine(" } While tmpnode.MoveToNextSibling() ")
Do %code.WriteLine(" If id="""" {")
Do %code.WriteLine(" Write !,""Creating new object...""")
Do %code.WriteLine(" Quit ##class("_%class.Name_").%New()}")
Do %code.WriteLine(" set result=##class("_%class.Name_").%OpenId(id)")
Do %code.WriteLine(" If result=$$$NULLOREF {")
Do %code.WriteLine(" Write !,""Creating new object...""")
Do %code.WriteLine(" Quit ##class("_%class.Name_").%New() }")
Do %code.WriteLine(" Write !,""Updating existing object ...""")
Do %code.WriteLine(" Quit result")
}
QUIT $$$OK
}
}
```
`IdForExport`属性和`NAMEOFEXPORTID`参数建立了一个约定,用于在导出子类的对象时如何将InterSystems IRIS内部`ID`投影到元素。其目的是,如果在子类中重新定义`IdForExport`,则相应地重新定义`NAMEOFEXPORTID`。
在这个类中,`XMLNew()`方法是一个方法生成器。编译该类(或任何子类)时,InterSystems IRIS会将此处显示的代码写入此方法的主体中。
以下类扩展了我们的自定义适配器:
```java
Class GXML.PersonWithXMLNew2
Extends (%Persistent, %Populate, GXML.AdaptorWithXMLNew)
{
Parameter XMLNAME = "Person";
Property Name As %Name;
Property DOB As %Date(FORMAT = 5, MAXVAL = "+$h");
Property Address As GXML.Address;
Property Doctors As list Of GXML.Doctor;
}
```
当运行前面显示的示例`ReadFile`方法时,对于文件中的每个 ``元素,该方法要么创建并保存一条新记录,要么打开并更新现有记录。
文章
Jingwei Wang · 七月 8, 2022
本片文章主要介绍两种Production debug的方式,
使用管理门户测试Production 组件
使用VS Code来对远程production代码进行debug
使用管理门户测试Production 组件
可以使用管理门户来对Production进行调试
测试组件
开启production测试: Interoperability -> 配置 -> Producton
在Production设置中,将开发与调试中的'测试开启'选中,如下图所示。
测试: Interoperability -> 测试 -> 业务主机
首先选择目标类型,业务流程或者业务操作,然后再目标名称这个下拉菜单中选择你要调试的组件名称,点击‘测试’
在请求详情中,填写测试请求的请求内容,然后点击'调用测试服务',测试结果如下。
然后,可以点击‘可视化追踪’来查看详情。
跟踪(Trace)
跟踪元素使你能够看到production中各种元素的行为,以便进行调试或诊断。
将跟踪元素添加到production的适当区域,在production运行时相应的跟踪信息就被写入,但是跟踪消息只是字符串,与Ens.Message及其子类无关。
跟踪消息可以启动和关闭,配置production以启用跟踪,意味着在运行时,跟踪元素被执行。而关闭跟踪意味着跟踪元素在production运行时会被被忽略。
可以选择将适用的业务主机配置为在前台运行,这样你就可以在生产运行时在终端看到跟踪信息。对于如何开启前台调试请参考Production前台调试。
启动跟踪
Interoperability -> 配置 -> Producton,选中要设置跟踪的组件。选中‘跟踪事件记入日志’,这一步会将追踪信息写到事件日志中。
默认情况下,所有的用户跟踪元素都被启用。你也可以通过设置^Ens.Debug来启用各种系统事件的跟踪,如果只是调试自己的代码用户跟踪就已经足够了,不需要开启其他系统事件的跟踪。
设置跟踪
业务服务 - BS ,业务操作 - BO :
在代码中添加用户跟踪
$$$TRACE("received application for "_request.CustomerName)
在代码中添加系统跟踪
$$$sysTRACE(trace_message)
在代码中添加error log
$$$LOGERROR("Awaiting connect on port "_..Port_" with timeout "_..CallInterval)
在代码中添加info log
$$$LOGINFO("Got data chunk, size="_$length(data)_"/"_tChunkSize)
业务流程 - BP :
在BPL中,点击 ‘-添加活动-' 下拉菜单,选择’跟踪‘
查看跟踪
从消息查看器中查看:
Interoperability -> 查看 -> 消息
选择你要查看的消息,点击相应的'会话'超链接,
点击 ◇,即可查看相应的trace
从事件日志中查看: Interoperability -> 查看 -> 事件日志 , 如下图所示,可以看到你所设置的所有事件日志。
使用VS Code来对远程production 代码进行debug
InterSystems支持使用VS Code连接到InterSystems IRIS服务器使用ObjectScript开发代码。如果您没有使用过VS Code连接到InterSystems IRIS服务器,请先阅读本社区文章 使用VSCode 进行IRIS 开发。
VS Code 连接远程 IRIS 请参考文章 使用VSCode 进行IRIS 开发,将VS Code连接到IRIS实例。
VS Code对本地productioin代码进行debug 如果想使用VS Code对本地代码进行debug,也请参考文章使用VSCode 进行IRIS 开发中的 ''在VSCode中调试' 部分。
VS Code对远程代码进行debug 在IRIS实例上面操作: 创建web 应用程序,在需要远程连接的IRIS实例上面建立一个名为/_vscode的web应用程序,配置如下:
在VS Code上面操作:
将VS Code连接到远程IRIS之后, 创建相应的工作区
InterSystems Tools - > 选择你要debug的命名空间 -> 点击右边笔状图标’Edit Code in Namespace‘。
进入到刚刚创建的工作区
创建workspace.json 文件 , 查看 -> command palette -> Open Workspac setting, 生成如下JSON文件, 文件名为workspace.json
{
"folders": [
{
"name": "localhcc:HCC",
"uri": "isfs://localhcc:hcc/"
}
],
"settings": {
"objectscript.showExplorer": false
}
}
创建launch.json 文件 , 点击左边菜单栏中的 'run and debug'图标,左上角会出现在launch.json文件中配置的调试名称。
配置launch.json文件
Debug普通类方法,使用如下launch.json文件,具体debug方式,请参考文章使用VSCode 进行IRIS 开发中的 ''在VSCode中调试' 部分。下图的launch.json文件表示可以调试 Test.SQL类中的SavePatientFromMsg函数,其中2为参数。
{
"version": "0.2.0",
"configurations": [
{
"type": "objectscript",
"request": "launch",
"name": "ObjectScript Debug SQL",
"program": "##class(Test.SQL).SavePatientFromMsg(2)"
}
]
}
Debug production, 使用如下launch.json文件, 其中processId为Production组件中的作业号
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "objectscript",
"request": "attach",
"name": "RetrieveCDAOperation",
"processId": 4436
}
]
}
Production中的作业号查询方式,如下图所示,在IRIS管理门户中,进入production页面。选中你想调试的组件,在右边即可以查看作业号。
调试
测试前,先增加断点,本文使用业务操作BO为例,我需要测试我的业务操作 HCC.SVR.Prod.BO.SaveCDAOperation。在我的业务操作类HCC.SVR.Prod.BO.SaveCDAOperation中增加几个断点。
选择你想调试的名称。点击左边绿色三角形的小图标’start debugging‘。
点击左边菜单栏中的 'run and debug'图标,左上角会出现在launch.json文件中配置的调试名称。选择你想调试的名称。点击左边绿色三角形的小图标’start debugging‘。
点击’start debugging‘后,最上方会出现测试功能条,但是功能条处于灰色,不可用状态。
然后触发production,可以使用postman 或者 soapUI等测试工具来触发要测试的production组件。如下图所示,相应的变量显示在左边菜单栏旁边,最上方的测试功能条处于可用状态,可以点击’step in‘ 、 ’step over‘、’step out‘ 来进行相应的调试,同时,INT - 中间源文件 Queue.1.int也会显示在调试页面上。INT 为中间源文件,是可执行的InterSystems IRIS对象代码。
如果想略过其他函数或者中间源文件,直接跳到你想调试的类或者函数中,可以将鼠标直接点击在想调试的函数中,然后右击鼠标,点击'Run to Cursor'。
点击'Run to Cursor'后,debug就会运行到鼠标指针闪烁的函数中,之后可以使用’step in‘ 、 ’step over‘、’step out‘ 来进行相应的调试。关于如何监控及查看调试过程中的变量,请参考文章使用VSCode 进行IRIS 开发中的 ''在VSCode中调试' 部分
文章
Hao Ma · 一月 15, 2021
InterSystems编程语言的错误管理技术一直在发展。接下来,我们将展示几种不同的错误管理实现方式,以及为什么要使用TRY/THROW/CATCH机制。
您可以点击这里阅读官方的错误处理建议。
为了支持遗留应用程序,InterSystems不会废弃非推荐的错误管理方法。我们建议使用objectscriptQuality等工具来检测遗留的非推荐用例以及其他可能的问题和错误。
$ZERROR
$ZERROR是一种较老的错误管理机制,支持与标准“M”不同的实现。虽然$ZERROR现在仍然有效,但我们非常不推荐使用。
如果您已经使用了$ZERROR,那么很容易对该变量进行错误的设计使用。$ZERROR是一个全局公共变量,可以被当前进程中正在执行的所有routine(宏)(来自InterSystems或自定义的)进行访问和修改。因此,它的值仅在产生错误的时候是可靠的。InterSystems不保证$ZERROR在调用系统库时会保留旧值。
我们在这里对一些案例展开分析。
案例1:自定义代码中的错误代码
Line
Code
Comments
$ZERROR value
1
Set ...
""
2
Set ...
""
...
Do ...
""
...
...
""
N-m
Do CacheMethodCall()
Call to another Caché system methods
""
...
""
N
Set VarXX = MyMethod()
The custom method generates an ObjectScript error
<UNDEFINED>B+3^InfinityMethod *varPatient
N+1
Set …
<UNDEFINED>B+3^InfinityMethod *varPatient
N+2
Do OtherCacheMethodCall()
Caché system method. $ZERROR is not updated if there is no error.
<UNDEFINED>B+3^InfinityMethod *varPatient
...
If ...
<UNDEFINED>B+3^InfinityMethod *varPatient
...
...
<UNDEFINED>B+3^InfinityMethod *varPatient
...
While ...
<UNDEFINED>B+3^InfinityMethod *varPatient
...
If $ZERROR’=”” Quit “Error”
<UNDEFINED>B+3^InfinityMethod *varPatient
N+m
Quit "OK"
<UNDEFINED>B+3^InfinityMethod *varPatient
案例2:内部Caché错误出现误报
在这种情况下,自定义代码运行良好,但内部Caché错误引发了一个错误。
Line
Code
Comments
$ZERROR value
1
Set …
""
2
Set …
""
..
Do …
""
..
…
""
N-m
Do CacheMethodCall()
Internal error but it is managed internally and decides to continue execution
<UNDEFINED>occKl+3^ MetodoInternoCache *o0bxVar
..
<UNDEFINED>occKl+3^ MetodoInterno *o0bxVar
N
Set VarXX = MyMethod() // OK
<UNDEFINED>occKl+3^ MetodoInterno *o0bxVar
N+1
Set …
<UNDEFINED>occKl+3^ MetodoInterno *o0bxVar
N+2
Do OtherCacheMethodCall() // OK
<UNDEFINED>occKl+3^ MetodoInterno *o0bxVar
…
If …
<UNDEFINED>occKl+3^ MetodoInterno *o0bxVar
…
…
<UNDEFINED>occKl+3^ MetodoInterno *o0bxVar
…
While …
<UNDEFINED>occKl+3^ MetodoInterno *o0bxVar
…
If $ZERROR’="" Quit "Error"
An error is detected while there is no error at all
<UNDEFINED>occKl+3^ MetodoInterno *o0bxVar
N+m
Quit "OK"
<UNDEFINED>occKl+3^ MetodoInterno *o0bxVa
案例3:在Caché内部代码中重置$ZERROR时出现误报
在这种情况下,即使不存在错误,也会直接或间接调用一个内部Caché方法或routine(宏)来重置公共变量$ZERRO
Line
Code
Comments
$ZERROR
1
Set ...
""
2
Set ...
""
...
Do ...
""
...
...
""
N-m
Do CacheMethodCall()
<UNDEFINED>occKl+3^ MetodoInternoCache *o0bxVar
...
<UNDEFINED>occKl+3^ MetodoInterno *o0bxVar
N
Set VarXX = MyMethod()
<UNDEFINED>occKl+3^ MetodoInterno *o0bxVar
N+1
Set …
<UNDEFINED>occKl+3^ MetodoInterno *o0bxVar
N+2
Do OtherCacheMethodCall()
Internal method that resets $ZERROR whether there is error or not
""
...
If ...
""
...
...
""
...
While ...
""
...
If $ZERROR’="" Quit "Error"
Error not detected
""
N+m
Quit "OK"
""
$ZTRAP
$ZTRAP是在上下文中进行错误管理的,因此不存在在上下文环境之外被意外覆盖的风险。当出现错误时,控件返回到调用堆栈中的第一个错误控件。
当出现错误并处理完成后,必须清除$ZTRAP,以便在发生另一个错误时避免出现无限循环。
因此,$ZTRAP在错误管理方面比$ZERROR更先进,但开发人员在添加操作时有可能会产生更多的错误。
如果想要进一步了解此方法的使用,可以查看官方文档中的“$ZTRAP错误处理”节选内容。
%Status
该方法用于系统库中,因此是调用系统库时必须使用的机制。
可以点击这里查看用法。
TRY/THROW/CATCH
这是最现代的错误管理方法,也是目前比较推荐的方法。
您可以点击这里查看用法。
此方法可在上下文中管理错误,并且不需要开发人员管理内部错误变量。
优点
关于TRY/THROW/CATCH的文献有很多,这里列举了一些它的优点:
提供了一种清晰的方式进行异常情况处理,将错误代码与常规代码分开处理
简化了错误检测,所以无需在每次操作后检查错误
允许错误传播到上层
支持运行时错误,允许在崩溃后恢复并继续运行
缺点
最明显的缺点是轻微的性能损失,因此您必须知道需要在什么时候使用该方法。
通常,没有必要在每种方法上都使用TRY/THROW/CATCH,在很多情况下在操作前多进行几次简单的验证就可以避免很多避免错误,从而避免不必要地使用TRY/THROW/CATCH方法。
结论
避免使用$ZERROR和$ZTRAP。
只有在调用系统库时才使用%STATUS。
可以使用TRY/THROW/CATCH管理错误,但不要滥用。
文章
Michael Lei · 二月 13, 2023
在 InterSystems IRIS 2022.2 中,我们引入了列存储作为持久化 IRIS SQL 表的新选项,可以将您的分析查询性能提高一个数量级。该功能在 2022.2 和 2022.3 中标记为实验性,但将在即将发布的 2023.1 版本中“升级”为完全支持的生产能力。
产品文档和这个介绍性视频已经描述了行存储(仍然是 IRIS 上的默认设置并在我们的整个客户群中使用)与列表存储之间的区别,并提供了有关为您的用例选择合适的存储布局的高级指导。在本文中,我们将详细阐述这个主题,并根据行业实践建模原则、内部测试和抢先体验计划参与者的反馈分享一些建议。
通常,我们为您的 IRIS SQL 模式选择合适的表布局的指南如下:
如果您正在部署利用 IRIS SQL 或对象的应用程序,例如电子病历、ERP 或事务处理应用程序,则无需将其当前的行存储布局更改为列式存储布局。为最终用户应用程序或编程事务发出的大多数 SQL 查询仅检索或更新有限数量的行,结果行通常对应于表行,聚合函数的使用非常有限。在这种情况下,列式存储和矢量化查询处理提供的优势不适用。
如果在应用程序还嵌入了操作分析,如果相应的分析查询的当前性能不令人满意,请考虑添加列索引。这包括,例如,显示当前库存的仪表板或实时数据的基本财务报告。查找聚合中使用的数字字段(例如数量、货币)或范围条件中使用的高基数字段(例如时间戳)。这种机会的一个很好的指标是当前使用位图索引来加速大量行的过滤,通常是在低基数字段(例如分类或有序字段)上。无需替换这些位图索引;附加的柱状索引与它们一起工作得很好,旨在避免从主地图或常规索引地图(每行单个 gref)中过度读取。
如果您的 IRIS SQL 表包含的行数少于一百万,则无需考虑列式存储。我们不希望将自己固定在特定数字上,但矢量化查询处理的好处不太可能在这些低范围内产生影响。
如果您正在为数据仓库、商业智能或类似的分析用例部署 IRIS SQL 架构,请考虑将其更改为默认的列式存储。星型模式、雪花模式或其他非规范化表结构以及位图索引和批量摄取的广泛使用是这些用例的良好指标。从列式存储中获益最多的分析查询是那些扫描大量行并在它们之间聚合值的查询。在定义“柱状表”时,IRIS 会透明地对该表中不适合柱状存储的列采用行布局,例如流、长字符串或串行字段。 IRIS SQL 完全支持这种混合表布局,并将对查询计划中符合条件的部分使用矢量化查询处理。位图索引在柱状表上的附加值有限,可以忽略不计。
技术使用与否将根据环境和数据相关参数而有所不同。因此,我们强烈建议客户在具有代表性的设置中测试不同的场景。列索引可以很容易地添加到常规的按行组织的表中,并且会很快产生对查询性能优势的现实看法。这与混合表格布局的灵活性一起,是 InterSystems IRIS 的一个关键差异化因素,可帮助客户实现数量级的性能改进。
随着我们在完整的生产版本中获得更多的实际经验,我们打算使这些建议更加具体。显然,我们可以通过抢先体验计划和 POC 参与,根据客户的实际架构和工作量提供更具体的建议,并期待客户和社区成员的反馈。 列存储Columnar Storage 是 InterSystems IRIS Advanced Server 高级版的一部分,在 InterSystems IRIS 和 IRIS for Health 社区版中也可以试用。有关完全脚本化的演示环境,请参阅这里的 GitHub资源 。
文章
姚 鑫 · 五月 1, 2023
# 第四十五章 管理镜像 - 使用 ^MIRROR 状态监视器
# 使用 ^MIRROR 状态监视器
`^MIRROR` 例程提供基于字符的镜像状态监视器。 `^MIRROR Status Monitor` 选项显示镜像成员的状态,包括类型、状态、日志传输延迟和 `dejournal` 延迟(请参阅镜像成员日志传输和 `Dejournaling` 状态)。监视器可以在任何镜像成员上运行,但在故障转移成员上运行它会提供有关仲裁器配置和所有连接的异步成员的信息,而在异步成员上运行它则不会。
要启动状态监视器,请打开终端窗口,在 `%SYS` 命名空间中运行 `^MIRROR` 例程(请参阅使用 `^MIRROR` 例程),然后从镜像状态菜单中选择状态监视器。以下是在故障转移成员上运行时监视器的输出示例:
```java
Status of Mirror MIR25FEB at 17:17:53 on 02/27/2018
Member Name+Type Status Journal Transfer Dejournaling
-------------------------- --------- ---------------- --------------
MIR25FEB_A
Failover Primary N/A N/A
MIR25FEB_B
Failover Backup Active Caught up
MIR25FEB_C
Disaster Recovery Connected Caught up Caught up
MIR25FEB_D
Read-Only Reporting Connected Caught up Caught up
Arbiter Connection Status:
Arbiter Address: 127.0.0.1|2188
Failover Mode: Arbiter Controlled
Connection Status: Both failover members are connected to the arbiter
Press RETURN to refresh, D to toggle database display, Q to quit,
or specify new refresh interval
```
当在异步成员上运行状态监视器时,仅列出故障转移成员和该异步成员,并且还会显示异步(正在运行或已停止)上的 `dejournaling` 状态,例如:
```java
Status of Mirror MIR25FEB at 17:17:53 on 02/27/2018
Member Name+Type Status Journal Transfer Dejournaling
-------------------------- --------- ---------------- --------------
MIR25FEB_A
Failover Primary N/A N/A
MIR25FEB_B
Failover Backup Active Caught up
MIR25FEB_C
Disaster Recovery Connected Caught up Caught up
Dejournal Status: running (process id: 12256)
Press RETURN to refresh, D to toggle database display, Q to quit,
or specify new refresh interval
```
默认情况下,不显示有关镜像数据库的信息。在提示符处输入 `d` 以列出有关镜像中每个数据库的信息,包括名称、目录、状态和要取消的下一条记录,如使用镜像监视器中所述,例如:
```java
Mirror Databases:
Last Record
Name Directory path Status Dejournaled
------------- ----------------------------------- ----------- -----------
MIR25FEB_DB1 C:\InterSystems\20182209FEB25A\Mgr\MIR25FEB_DB1\
Active
Current,c:\intersystems\20182209feb25a\mgr\journal\MIRROR-MIR25FEB-20180227.001,40233316
MIR25FEB_DB2 C:\InterSystems\20182209FEB25A\Mgr\MIR25FEB_DB2\
Active
Current,c:\intersystems\20182209feb25a\mgr\journal\MIRROR-MIR25FEB-20180227.001,40233316
```
# 监控镜像通信进程
每个系统(主要和备份故障转移成员,以及每个连接的异步成员)上运行的进程负责镜像通信和同步。
## 主要故障转移成员上的镜像进程
在主要故障转移成员上运行系统状态例程 (^%SS) 会显示下表中列出的进程。
注意:本节的 `^%SS` 输出中有意省略了 `CPU`、`Glob` 和 `Pr` 列。
#### 主要故障转移成员上的镜像进程
Device| Namespace| Routine| User/Location
---|---|---|---
/dev/null| %SYS |MIRRORMGR |Mirror Master
MDB2| %SYS| MIRRORCOMM| Mirror Primary*
192.168.1.1 |%SYS |MIRRORCOMM |Mirror Svr:Rd*
这些过程定义如下:
- `Mirror Master`:该进程在系统启动时启动,负责各种镜像控制和管理任务。
- `Mirror Primary`:这是出站数据通道;这是一个单向通道。每个连接的系统有一个作业(备份故障转移或异步成员)。
- `Mirror Svr:Rd*`:这个是入站确认通道;这是一个单向通道。每个连接的系统有一个作业(备份故障转移或异步成员)。
每个连接的异步成员都会在主故障转移成员上产生一组新的 `Mirror Master`、`Mirror Primary` 和 `irror Svr:Rd*` 进程。
## 备份故障转移/异步成员上的镜像进程
在备份故障转移/异步成员上运行系统状态例程 (`^%SS`) 会显示下表中列出的进程。
#### 备份故障转移/异步成员上的镜像进程
Device| Namespace| Routine| User/Location
---|---|---|---
/dev/null| %SYS |MIRRORMGR |Mirror Master
/dev/null |%SYS|MIRRORMGR| Mirror Dejour
/dev/null| %SYS|MIRRORMGR| Mirror Prefet*
/dev/null |%SYS |MIRRORMGR| Mirror Prefet*
MDB1 |%SYS|MIRRORMGR |Mirror Backup
/dev/null| %SYS|MIRRORMGR |Mirror JrnRead
此表中标识的进程也出现在每个连接的异步成员上:
- `Mirror Master`:该进程在系统启动时启动,负责各种镜像控制和管理任务。
- `Mirror JrnRead (Mirror Journal Read)`:该过程将备份生成的日志数据读入内存,并将这些更改排队等待 `dejournal` 作业取消。
- 镜像延迟(`Mirror Dejournal`):这是备份故障转移成员上的延迟作业;它将接收到的日志数据中的 `sets` 和 `kills` 发送到镜像数据库。
- `Mirror Prefet*`(镜像预取):这些进程负责在`dejournal` 作业实际尝试使用它们之前将 `dejournal` 作业所需的磁盘块预取到内存中。这样做是为了加快 `dejournaling` 过程。系统上通常配置了多个镜像预取作业。镜像备份:这个过程是一个双向通道,将从主服务器接收到的日志记录写入备份的镜像日志文件,并向主服务器返回确认。
文章
Jingwei Wang · 五月 4, 2022
InterSystems IRIS IntegratedML功能用于使用AutoML技术获得机器学习的预测结果和概率。AutoML是一种机器学习技术,用于选择更好的机器学习算法/模型基于已有数据(用于训练AutoML模型的数据)来预测状态、数字和结果。你不需要一个数据科学家,因为AutoML会测试最常见的机器学习算法,并根据分析的数据特征选择更好的算法给你。在这篇文章中可以看到更多管乐IntegratedML的信息。
InterSystems IRIS有一个内置的AutoML引擎,但也允许你使用H2O和DataRobot。在这篇文章中,我将向你展示使用InterSystems AutoML引擎的每个步骤。
第1步 - 下载样例应用程序做练习
1. 转到 https://openexchange.intersystems.com/package/Health-Dataset
2. Clone/git 将 Repo拉到任何本地目录中
$ git clone https://github.com/yurimarx/automl-heart.git
3. 在同一个目录中打开一个Docker终端并运行
$ docker-compose build
4. 运行IRIS容器
$ docker-compose up -d
第2步--了解业务场景和可用数据
业务场景是利用过去的数据预测心脏疾病,数据如下:
SELECT age, bp, chestPainType, cholesterol, ekgResults,
exerciseAngina, fbsOver120, heartDisease, maxHr,
numberOfVesselsFluro, sex, slopeOfSt, stDepression, thallium
FROM dc_data_health.HeartDisease
心脏病表(heartDisease)的数据字典来源(https://data.world/informatics-edu/heart-disease-prediction/workspace/data-dictionary):
Column name
Type
Description
age
Integer
In years
sex
Integer
(1 = male; 0 = female)
chestPainType
Integer
Value 1: typical angina -- Value 2: atypical angina -- Value 3: non-anginal pain -- Value 4: asymptomatic
bp
Integer
Resting blood pressure (in mm Hg on admission to the hospital)
cholesterol
Integer
Serum cholestoral in mg/dl
fbsOver120
Integer
(fasting blood sugar > 120 mg/dl) (1 = true; 0 = false)
ekgResults
Integer
Resting electrocardiographic results -- Value 0: normal -- Value 1: having ST-T wave abnormality (T wave inversions and/or ST elevation or depression of > 0.05 mV) -- Value 2: showing probable or definite left ventricular hypertrophy
maxHr
Integer
Maximum heart rate achieved
exerciseAngina
Integer
Exercise induced angina (1 = yes; 0 = no)
stDepression
Double
ST depression induced by exercise relative to rest
slopeOfSt
Integer
The slope of the peak exercise ST segment -- Value 1: upsloping -- Value 2: flat -- Value 3: downsloping
numberOfVesselsFluro
Integer
Number of major vessels (0-3) colored by flourosopy
thallium
Integer
3 = normal; 6 = fixed defect; 7 = reversable defect
heartDisease
String
Value 0: < 50% diameter narrowing -- Value 1: > 50% diameter narrowing
心脏病(heartDisease)是我们需要预测的属性
第3步--准备好训练数据
HeartDisease表有270行。我们将得到250行来训练我们的预测模型。为了做到这一点,我们需要创建VIEW ,通过 管理门户>系统资源管理器>SQL。
CREATE VIEW automl.HeartDiseaseTrainData AS
SELECT * FROM dc_data_health.HeartDisease WHERE ID < 251
第4步 - 准备验证数据
我们将得到20行来验证预测的结果。 为了做到这一点,我们需要创建VIEW ,通过 管理门户>系统资源管理器>SQL。
CREATE VIEW automl.HeartDiseaseTestData AS
SELECT * FROM dc_data_health.HeartDisease WHERE ID > 250
第5步--创建AutoML模型来预测心脏疾病
IntegratedML允许创建一个AutoML模型来做预测和概率(详见https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GIML_BASICS)。要做到这一点,需要创建以下模型,通过 管理门户>系统资源管理器>SQL。
CREATE MODEL HeartDiseaseModel PREDICTING (heartDisease) FROM automl.HeartDiseaseTrainData
这个模型将从automl.HeartDiseaseTrainData view中获得训练数据(学习)。
第6步 - 执行训练
执行训练,了做到这一点,执行下面训练SQL语句,通过 管理门户>系统资源管理器>SQL。
TRAIN MODEL HeartDiseaseModel
第7步 - 验证训练后的模型
为了验证训练结果,在 管理门户>系统资源管理器>SQL 中执行以下SQL指令。
VALIDATE MODEL HeartDiseaseModel FROM automl.HeartDiseaseTestData
使用automl.HeartDiseaseTestData view 中的测试数据验证了HeartDiseaseModel。
第8步 - 获取验证指标
为了查看验证过程中的验证指标,在 管理门户>系统资源管理器>SQL 中执行以下SQL 指令。
SELECT * FROM INFORMATION_SCHEMA_ML_VALIDATION_METRICS
要了解返回的结果,请见 https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GIML_VALIDATEMODEL.
InterSystems IRIS 文件从验证结果中详细说明了以下内容。:
验证指标,可以在INFORMATION_SCHEMA_ML_VALIDATION_METRICS表中查看
对于回归模型,以下指标会被保存:
方差
R-squared
平均平方误差
均方根误差
对于分类模型,以下指标会被保存:
Precision — 这是用真阳性数除以预测阳性数(真阳性与假阳性之和)计算出来的。
Recall — 这是用真阳性数除以实际阳性数(真阳性与假阴性之和)来计算的。
F-Measure — 这是由以下表达式计算的:F = 2 * (精度*召回率) / (精度+召回率)
Accuracy — 这是用真阳性和真阴性的数量除以整个测试集的总行数(真阳性、假阳性、真阴性和假阴性之和)来计算的。
第9步 - 使用你的新AutoML模型执行预测 - 最后一步!
为了查看验证过程中的验证指标,在 管理门户>系统资源管理器>SQL 中执行以下SQL指令。
SELECT *, PREDICT(HeartDiseaseModel ) AS heartDiseasePrediction FROM automl.HeartDiseaseTestData
比较列 heartDisease(真实值)和 heartDiseasePrediction(预测值)。
文章
Michael Lei · 六月 26, 2022
在前一篇文章中,我已经演示了一种简单的方法来记录数据的变化。在这个时候,我改变了负责记录审计数据的 "审计抽象类 "和记录审计日志的数据结构。
我已经将数据结构改为父子结构,其中将有两个表来记录 "交易 "和在该交易中改变的 "字段的值"。
看一下新的数据模型:
看看从 "审计类 "改变的代码吧:
Class Sample.AuditBase [ Abstract ]{ Trigger SaveAuditAfter [ CodeMode = objectgenerator, Event = INSERT/UPDATE, Foreach = row/object, Order = 99999, Time = AFTER ]{ #dim %compiledclass As %Dictionary.CompiledClass #dim tProperty As %Dictionary.CompiledProperty #dim tAudit As Sample.Audit Do %code.WriteLine($Char(9)_"; get username and ip adress") Do %code.WriteLine($Char(9)_"Set tSC = $$$OK") Do %code.WriteLine($Char(9)_"Set tUsername = $USERNAME") Set tKey = "" Set tProperty = %compiledclass.Properties.GetNext(.tKey) Set tClassName = %compiledclass.Name Do %code.WriteLine($Char(9)_"Try {") Do %code.WriteLine($Char(9,9)_"; Check if the operation is an update - %oper = UPDATE") Do %code.WriteLine($Char(9,9)_"if %oper = ""UPDATE"" { ") Do %code.WriteLine($Char(9,9,9)_"Set tAudit = ##class(Sample.Audit).%New()") Do %code.WriteLine($Char(9,9,9)_"Set tAudit.Date = +$Horolog") Do %code.WriteLine($Char(9,9,9)_"Set tAudit.UserName = tUsername") Do %code.WriteLine($Char(9,9,9)_"Set tAudit.ClassName = """_tClassName_"""") Do %code.WriteLine($Char(9,9,9)_"Set tAudit.Id = {id}") Do %code.WriteLine($Char(9,9,9)_"Set tSC = tAudit.%Save()") do %code.WriteLine($Char(9,9,9)_"If $$$ISERR(tSC) $$$ThrowStatus(tSC)") Do %code.WriteLine($Char(9,9,9)_"Set tAuditId = tAudit.%Id()") While tKey '= "" { set tColumnNbr = $Get($$$EXTPROPsqlcolumnnumber($$$pEXT,%classname,tProperty.Name)) Set tColumnName = $Get($$$EXTPROPsqlcolumnname($$$pEXT,%classname,tProperty.Name)) If tColumnNbr '= "" { Do %code.WriteLine($Char(9,9,9)_";") Do %code.WriteLine($Char(9,9,9)_";") Do %code.WriteLine($Char(9,9,9)_"; Audit Field: "_tProperty.SqlFieldName) Do %code.WriteLine($Char(9,9,9)_"if {" _ tProperty.SqlFieldName _ "*C} {") Do %code.WriteLine($Char(9,9,9,9)_"Set tAuditField = ##class(Sample.AuditField).%New()") Do %code.WriteLine($Char(9,9,9,9)_"Set tAuditField.Field = """_tColumnName_"""") Do %code.WriteLine($Char(9,9,9,9)_"Set tAuditField.OldValue = {"_tProperty.SqlFieldName_"*O}") Do %code.WriteLine($Char(9,9,9,9)_"Set tAuditField.NewValue = {"_tProperty.SqlFieldName_"*N}") Do %code.WriteLine($Char(9,9,9,9)_"Do tAuditField.AuditSetObjectId(tAuditId)") Do %code.WriteLine($Char(9,9,9,9)_"Set tSC = tAuditField.%Save()") do %code.WriteLine($Char(9,9,9,9)_"If $$$ISERR(tSC) $$$ThrowStatus(tSC)") Do %code.WriteLine($Char(9,9,9)_"}") } Set tProperty = %compiledclass.Properties.GetNext(.tKey) } Do %code.WriteLine($Char(9,9)_"}") Do %code.WriteLine($Char(9)_"} Catch (tException) {") Do %code.WriteLine($Char(9,9)_"Set %msg = tException.AsStatus()") Do %code.WriteLine($Char(9,9)_"Set %ok = 0") Do %code.WriteLine($Char(9)_"}") Set %ok = 1} }
通过Test()类方法改变数据,现在你可以看到审计类(Sample.Audit)中的 "父记录 "和 "审计字段 "类中的 "子字段 "被改变。(Sample.AuditField)。
d ##class(Sample.Person).Test(1)INSERT INTO Sample.Person (Name, Age) VALUES ('TEST PARENT-CHILD', '01')SQLCODE: 0ID Age Name1 01 TEST PARENT-CHILD 1 Rows(s) AffectedUPDATE Sample.Person SET Name = 'INTERSYSTEMS DEVELOPER COMMUNITY', Age = '100' WHERE Name = 'TEST PARENT-CHILD'SQLCODE:0ID Age Name1 100 INTERSYSTEMS DEVELOPER COMMUNITY 1 Rows(s) Affected
审计类:
注意Sample.AuditField记录通过Audit字段=1对Sample.Audit类进行了引用。你可以通过使用两个类的关系来查询数据,如下所示:
这样就搞定了。这样,我们有一个不同的审计日志数据结构。
问题
jingwei lu · 三月 13, 2022
现在我们院区也遇到这样一个问题。关于cache的高可用架构现有有两个方案:
1.选择双节点的完全无共享架构的自动转移镜像集群外加一个灾难恢复镜像
2.就像你提问的那种,两台主机先做Rose HA用一套双活存储实现高可用,然后再弄一台服务器做Rose HA的单机镜像(有可能是同步也有可能是异步那种比如灾难恢复镜像)
现在想问问如果单纯考虑切换时候对业务的影响,如果切换的中断时长什么的,用哪个最好啊?第二种方案真有很多单位用么?
希望有大神能替我解答一下,谢谢。 可以参考下历史的帖子。https://community.intersystems.com/post/backup-strategy-%E5%8F%8C%E6%9C%BA%E5%A4%87%E4%BB%BD%E7%AD%96%E7%95%A5
如果是我,会选择方案1,原因:既然 intersystems 提供了完整的 mirror 高可用方案,就没必要再和第三方的方案掺和在一起,凭空增加了问题排错的复杂度。如果真出现了服务中断,这个时候要第一要务是尽快做出正确的决策,以便尽快恢复服务。架构的复杂性会给自己造成很多不必要的麻烦以及决策上的复杂性 (个人建议,仅供参考。) 我们推荐的高可用性方案是Mirror。也就是配置至少主备两个镜像成员+灾备异步镜像成员,可选配置多个异步报表镜像成员以及多个灾备异步镜像成员。
当然同时推荐的是建立健全的外部备机机制,以及使用外部备份进行恢复的演练,手动升级灾备异步镜像成员为主机的演练。
对于大型应用一般使用ECP创建多台应用服务器,进行多用户的负载分配,再将多台应用服务器连接至数据服务器,数据服务器进行高可用性配置。 感谢分享 感谢分享 常见问题系列-系统管理篇-如何进行数据库备份
InterSystems Caché系统高可用与数据库镜像
文章
Jingwei Wang · 七月 4, 2022
高可用性(HA)指的是使系统或应用程序在很高比例的时间内保持运行,最大限度地减少计划内和计划外的停机时间。
维持系统高可用性的主要机制被称为故障转移。在这种方法下,一个故障的主系统被一个备份系统所取代;也就是说,生产系统故障转移到备份系统上。许多HA配置还提供了灾难恢复(DR)的机制,即在HA机制无法保持系统的可用性时,也能及时恢复系统的可用性。
本文简要讨论了可用于基于InterSystems IRIS的应用程序的HA策略机制,提供了HA解决方案的功能比较,并讨论了使用分布式缓存的故障转移策略。
操作系统级别的集群HA
在操作系统层面上提供的一个常见的HA解决方案是故障转移集群,其中主要的生产系统由一个(通常是相同的)备用系统补充,共享存储和一个跟随活动成员的集群IP地址。在生产系统发生故障的情况下,备用系统承担生产工作量,接管以前在故障主系统上运行的程序和服务。备用机必须能够处理正常的生产工作负载,只要恢复故障主机所需的时间就可以了。也可以选择让备用机成为主机,一旦主机恢复,故障主机将成为备用机。
InterSystems IRIS的设计可以轻松地与所支持的平台的故障转移集群技术相结合(如InterSystems支持的平台中所述)。InterSystems IRIS实例安装在集群的共享存储设备上,以便两个集群成员都能识别它,然后添加到集群配置中,这样它将作为故障转移的一部分在备用机上自动重新启动。在故障转移后重新启动时,系统自动执行正常的启动恢复,保持结构和逻辑的完整性,就像InterSystems IRIS在故障系统上重新启动一样。如果需要,可以在一个集群上安装多个InterSystems IRIS实例。
InterSystems IRIS 镜像
具有自动故障转移功能的 InterSystems IRIS 数据库镜像为计划内和意外停机提供了经济有效的高可用性解决方案。镜像依赖于数据复制而不是共享存储,避免了存储故障导致的重大服务中断。
InterSystems IRIS 镜像由两个物理上独立的 InterSystems IRIS 系统组成,称为故障转移成员。每个故障转移成员在镜像中维护每个镜像数据库的副本;应用程序更新是在主故障转移成员上进行的,而备机故障转移用户的数据库则通过主成员的日志文件保持数据同步。
镜像会自动将主节点的角色分配给两个故障转移成员中的一个,而另一个故障转移成员则自动成为备机系统。当主 InterSystems IRIS 实例失败或变得不可用时,备机将自动快速接管并成为主机。
第三个系统称为仲裁机,它与故障转移成员保持持续的联系,在无法直接通信时安全地做出故障转移决策,为他们提供所需信息。在每个故障转移系统主机上运行的代理(称为 ISCAgents )进程也有助于自动故障转移逻辑。除非备机能够确认主节点确实处于停机状态或不可用状态,并且无法再作为主节点运行,否则备机将无法接管。在仲裁机和 ISCAgents 之间,这可以在几乎每个中断场景下完成。
当镜像配置使用 virtual IP address(虚拟 IP 地址)时,将应用程序连接重定向到新主节点是透明的。如果连接是通过 ECP,它们会自动重置到新的主节点。用于重定向应用程序连接的其他机制也是可用的。
当主机实例恢复正常时,它将自动成为备机实例。操作员启动的故障转移也可在计划的维护或升级停机期间维护可用性。
自动故障转移机制
镜像主旨在当主节点失败或变得不可用时,为备机提供安全的自动故障转移。本部分描述了允许这种情况发生的机制,包括:
• 自动故障转移的安全性要求
• 自动故障转移规则
• 对于各种中断情况的镜像响应
• 自动故障转移机制详节
自动故障转移的安全性要求
备机的 InterSystems IRIS 实例只有在能够确保满足以下两个条件时才能自动接管主节点:
• 备机实例已从主节点接收到最新的日志数据。
这一要求保证了主节点上镜像数据库的所有持久更新都已经或将要对备机的相同数据库进行,从而确保不会丢失任何数据。
• 主机实例不再作为主机实例运行,并且在没有手动干预的情况下无法这样做。
这个要求消除了两个故障转移成员同时作为主节点的可能性,这可能导致逻辑数据库性能下降和完整性损失。
自动故障转移规则
备机状态和自动故障转移
在正常镜像操作期间,备机故障转移成员的日志传输状态为活动,这意味着它已从主节点接收到所有日志数据,并与其同步。活跃的备机接收写在主机上的当前日志数据,主机等待备机确认收到日志之后才考虑该数据的持久化。因此,备机为活跃状态满足故障转移的第一个条件。
如果活跃备机在Quality of Service(QoS)(服务质量超时)内不能确定已从主节点接收新数据,主节点会撤销备机的活动状态,断开备机并暂时进入故障状态。当处于故障状态时,主节点不提交任何新的日志数据(可能导致应用程序暂停),确保充足的时间,使故障转移成员在不发生异步的情况下恢复联系或进行适当的安全故障转移决策
当备机重新连接到主节点时,它首先通过从主节点获取所有最近的日志数据来跟上,然后变为活动状态。当备机通过从主节点同步了最新的日志数据并确认其接收而跟上时,将恢复其活动状态。
备机处于活动状态时的自动故障转移
当备机处于活动状态时,如果它能够确认故障转移的第二个条件,主节点不可以再作为主节点,并且在没有人工干预的情况下不能继续成为主节点,那么它就有资格作为主机进行接管。备机可以通过以下三种方式中的任意一种来接管主节点:
• 通过接收来自主机的请求接管的通信
这发生在主机实例正常关闭期间或主机实例检测到主机实例挂起时。一旦主节点发送了这条消息,它就不能再作为主机了,活动备机可以安全地接管它。如果前一个主机被挂起,新的主机就会迫使它关机。
• 通过从仲裁机处收到信息,得知其已与主机失去联系。
当一个网络事件同时将主节点与备机和仲裁机隔离时,它将无限期地进入故障状态。因此,如果一个活跃备机与主机失去联系,并且从仲裁机那里得知它也与主机用失去了联系,那么备机可以安全地接管,因为主机用必须要么已经出现故障,要么被隔离并处于故障状态,因此不能再作为主节点运行了。当连接恢复时,如果前一个主节点挂起,新的主节点就会将之前的主节点强制关闭。
• 通过从主机的 ISCAgent 接收主机实例已关闭或挂起的信息。
当仲裁机不可用或未配置仲裁机时,与主机实例失去联系的活动备机可以尝试联系主机实例的 ISCAgent(只有在主机实例主机系统仍在运行时才可能)以确认主机实例已关闭或处于挂起状态。一旦代理确认主节点不能再作为主机,并且故障转移因此是安全的,备机就会接管。
备机不处于活动状态时的自动故障转移
不活动的备机可以尝试与主机实例的 ISCAgent 联系,以确认主机实例处于关闭状态,或在挂起时强制关闭主机实例,并从代理获取主机实例最近的日志数据。如果在这两方面都成功,那么备机可以安全地作为主机接管。
不活动且无法与主机的 ISCAgent 联系的备机无法确保主机不再作为主机,并且无法保证此时的备机拥有主机最新的日志更新,因此无法接管主机。
当备机没有活动时,仲裁机在故障转移机制中不起任何作用。
对于各种中断情况的镜像响应
自动故障转移中主机中断场景的响应
在以下几种主要的主节点中断情况下,活动的备机故障转移成员会自动接管:
主节点的计划中断(例如出于维护目的)通过关闭其 InterSystems IRIS 实例来启动。
自动故障转移发生是因为主机指示活跃备机接管。
主 InterSystems IRIS 实例由于意外情况而挂起。
自动故障转移发生是因为主节点检测到它被挂起并指示活跃备机接管。
主 InterSystems IRIS 实例由于意外情况而被迫关闭或完全失去响应。
在此情况下,主节点不能指示备机接管。然而,活动备机可以在从仲裁机那里得知它也与主节点失去联系后接管,也可以通过与主机的 ISCAgent 联系并获得主机停机的确认后接管。
主机存储的子系统失败。
存储失败的典型后果是主机实例挂起,原因是 I/O 错误,在这种情况下,主节点检测到它被挂起,并指示活动的备机接管(如场景 2)。然而,在某些情况下,场景 3 或场景 5 描述的行为可能适用。
主机的主机系统发生故障或失去响应。
如果活动的备机从仲裁机获悉它也与主机失去联系,则发生自动故障转移。
如果没有配置仲裁机,或者仲裁机在主机故障前已经不可用,则不可能进行自动故障转移;在这些情况下,可以选择手动强制备机成为主机。
一个网络问题隔离了主机。
如果配置了仲裁机,并且在网络故障时两个故障转移成员都不能连接到它,则主节点将无限地进入故障状态。
• 如果活动的备机从仲裁机获悉它也与主机失去联系,则发生自动故障转移。
• 如果备机在与主节点失去联系的同时失去与仲裁机的联系,则不可能进行自动故障转移。如果两个故障转移成员都已启动,则在还原网络时,备机将与主节点联系,主节点恢复操作。或者,可以手动指定主节点。
如果没有配置仲裁机,或者在网络故障发生前,故障转移成员之一已经与仲裁机断开,则无法进行自动故障转移,主节点将继续作为主机运行。
一个未活动的备机(例如备机正在启动或未追上主机最新日志)可以通过与主节点的 ISCAgent 联系并获得最新的日志数据,在上述场景 1 到 4 下接管。未活动的备机不能在场景 5 和 6 中接管,因为它不能与 ISCAgent 联系;在这些情况下,手动强制使备机成为主机可能是一种选择。
自动故障转移机制详解
代理控制模式
当镜像启动时,故障转移成员在代理控制模式下开始操作。如果仲裁机不可用或未配置仲裁机,则保持此模式。当处于代理控制模式时,故障转移成员响应彼此之间的联系丢失,如下所述。
主机对失联的反应
如果主节点失去了与活动备机的连接,或者超过了等待它确认数据接收的 QoS timeout (QoS 超时),则主程序撤消备机的活动状态并进入故障状态,等待备机确认其不再活动。当主节点收到来自备机的确认或故障超时(是 QoS 超时的两倍)过期时,主程序退出故障状态,恢复为主程序运行。
如果主节点失去了与非活动备机的连接,则它将继续作为主程序运行,不会进入故障状态。
备机对失联的反应
如果备机失去了与主节点的连接,或者超过了等待来自主机的消息的 QoS timeout (QoS 超时),那么它将尝试与主客户的 ISCAgent 联系。如果代理报告主机实例仍作为主机实例运行,则备机重新连接。如果代理确认主节点处于关闭状态,或者它已将其强制关闭,则备机行为如下:
• 如果备机处于活动状态,且代理确认主节点在故障超时内处于停机状态,则备机将作为主服务端接管。
• 如果备机未处于活动状态,或者超过了故障超时时间,那么如果代理确认主节点已停机,并且能够从代理获得最新的日志数据,则备机将接管。
无论是否处于活动状态,备机永远无法在代理控制模式下自动接管,除非主节点自身确认它已挂起,或者主服务的代理确认主服务已停机(可能是在强制停机之后),如果主节点已停机或网络隔离,这两种情况备机无法自动接管。
注意: 当其中一个故障转移成员重启时,它会尝试联系另一个成员的 ISCAgent,其行为与不活动备机的描述一样。
仲裁机控制模式
当故障转移成员相互连接时,两者都连接到仲裁机,并且备机是活动的,它们进入仲裁机控制模式,在该模式中,故障转移会员根据仲裁机提供的关于另一个故障转移成员的信息对它们之间的联系丢失做出响应。因为每个故障转移成员通过测试其与另一个故障转移成员的连接来响应其仲裁连接的丢失,反之亦然,由单个网络事件引起的多个连接丢失被作为单个事件处理。
在仲裁机控制模式中,如果故障转移成员仅丢失其仲裁机连接,或者备机 丢失其活动状态,则故障转移成员协调切换到代理控制模式。
如果主节点和备机节点之间的连接在仲裁机控制模式下断开,则每个故障转移成员根据仲裁机连接的状态进行响应,如下图所述。
所有三个系统连接:
镜像进入仲裁机控制模式(如果尚未进入仲裁机控制模式)
备机失去与仲裁机的连接,但仍连接到主节点:
镜像切换到代理控制模式
主节点继续作为主节点运行
备机尝试重新连接仲裁机
主节点失去与仲裁机的连接,但仍连接到备机:
镜像切换到代理控制模式
主节点继续作为主节点运行
主节点尝试重新连接仲裁程序
故障转移成员彼此失去连接,仍然连接到仲裁机:
镜像切换到代理控制模式
主节点继续作为主节点运行
备机尝试重新连接主节点
仲裁机失败或隔离-故障转移成员失去与仲裁机的连接,但仍彼此连接:
镜像切换到代理控制模式
主节点继续作为主节点运行
两个故障转移成员都尝试重新连接仲裁机
备机中断或被隔离-主节点和仲裁机失去与备机的连接,但仍相互连接:
主节点切换到代理控制模式并继续作为主节点操作
备机(如果在操作中)切换到代理控制模式并尝试重新连接到主节点
主节点中断或被隔离-备机和仲裁机失去与主节点的连接,但仍相互连接:
主节点(如果在运行中)将无限期地保持在仲裁控制模式和故障状态
备机作为主机接管,切换到代理控制模式,并在恢复连接时强制主机关闭
三个连接全部丢失:
主节点(如果在运行中)将无限期地保持在仲裁控制模式和故障状态;如果与备机设备联系,则切换到代理控制模式并恢复主设备的运行
备机(如果在操作中)切换到代理控制模式并尝试重新连接到主节点
注意: 由于单个事件(或多个同时发生的事件)而导致所有连接丢失的情况很少见。在大多数情况下,镜像在所有连接丢失之前切换到代理控制模式,在这种情况下:
主节点(如果在运行)继续作为主节点运行
备机(如果正在运行)尝试重新连接到主节点
文章
Claire Zheng · 三月 3, 2021
亲爱的社区用户:
InterSystems社区会举办丰富的竞赛活动,项目提交后最重要的环节就是“投票”!
一般来说社区新注册用户在投票时会发现无法投票并收到以下提示:
如果遇到这种情况,解决方案分两步:
第一步:登陆社区(cn.community.intersystems.com)后进行发帖、回复、回答问题等操作;
第二步:如果您在社区已经完成了上述第一步的操作,那么接下来只需要……等一天。因为我们的系统会每天进行一次active user更新。
成为Active user后,就可以继续给你心仪的项目投票啦!
建议中国用户登录社区中文版(cn.community.intersystems.com)进行操作,以便获得更快的支持! 请再等一天 @Sheng.Li @Jianjun.Miao @gu.jingguo , Thx! 明白了,十分感谢! 明白了,十分感谢! 原来有中文社区,好不容易才找到,,, 通过学习期望也能参加到竞赛!加油! 碰到了这个问题,完美解决,谢谢! 注册满一天留言才能投票
公告
Claire Zheng · 十月 19, 2022
2022年9月5日-10月24日(北京时间),我们正在举办🏆InterSystems开发者社区中文版首届技术征文大赛🏆(←点击链接进入参赛页面,浏览所有参赛文章)!投票截止至10月23日,你的支持与喜爱,是优秀作品获得“开发者社区奖”的关键!我们先来看看目前作品排名情况吧!距离投票截止还有五天(截止至10月23日),我们暂时无法获得专家评审分数,以下根据作品“点赞”进行排名。
N
作者
标题
点赞⬇
查看数
奖励项目个数
1
Zhe Wang
IRIS如何进行CRUD操作
36
197
3
2
Meng Cao
Caché数据库私有apache版本升级
33
336
1
3
sun yao
前端操作自动生成BS、BP、BO
22
123
2
4
John Pan
论集成标准的选择对医院信息集成平台建设的影响
21
113
1
5
lizw lizw
关于%Dictionary.CompiledClass类在实际业务中的一些应用
20
114
1
6
聆严 周
使用Prometheus监控Cache集群
18
175
3
7
Chang Liu
在国产系统上安装Healthconnect2021
17
104
2
8
Zhe Wang
IRIS快速查询服务思路分享
17
58
3
9
Zhe Wang
使用Global进行数据可视化---商业智能(BI)
16
37
2
10
John Pan
如何调用Ensemble/IRIS内置的HL7 V2 webservice - Java,PB9,Delphi7样例
14
96
2
11
Zhe Wang
小工具:IRIS管理页打开显示查询功能
14
62
2
12
water huang
对 %XML.PropertyParameters类的探索
13
39
1
13
姚 鑫
IRIS与Caché的23种设计模式
13
59
3
14
Zhe Wang
Rest实现Post、Get、Put、Delete几种操作方式
13
30
2
15
聆严 周
Cache / IRIS 操作数据的3种基本方式
11
153
1
16
he hf
10分钟快速开发一个连接到InterSystems IRIS数据库的C#应用
11
193
2
17
shaosheng shengshao
HEALTHSHARE2018版如何实现AES(CBC)的HEX输出,并可以实现加密和解密
11
63
3
18
water huang
Ens.Util.JSON类的启发
9
54
2
19
bai hongtao
第三方HA软件结合MIRROR使用方法探讨
9
69
1
20
li wang
HealthConnect访问HTTPS开头地址
8
21
2
21
shaosheng shengshao
windows下处理IIS在未安装但Healthshare已安装的时候,部署IIS服务并代理Healthshare
8
43
1
22
zhanglianzhu zhanglianzhu
Cache死循环检测和申明式事务
8
101
0
23
Liu Tangh
在Cache系统中使用负载均衡服务的探讨
8
25
1
24
yaoguai wan
IRIS架构的浅显理解以及windows10、docker安装IRIS Health详解流程及部分问题浅析
6
30
2
25
Zhe Wang
COS的基本语法
3
12
3
*奖励项目详见参赛规则:点击阅读
我们此次征文大赛计分规则如下:
🥇【专家提名奖】评选规则,由经验丰富的专家评审团进行评选打分,与其他加分项综合后进行排名。
🥇【开发者社区奖】评选规则,每个点赞计分为1分,与其他加分项综合后进行排名。
🥇【入围奖】评选规则,成功参赛的其余用户都将获得特别奖励。
每位作者只可以获得一个奖项(即:您只可以获得一次专家提名奖/开发者社区奖/入围奖);
当出现票数相当的平手情况时,将以专家评选投票数作为最终票数高低的判断标准。
那么,抓住最后五天的投票时间,为你喜欢的作品“点赞”投票吧!你的点赞是优秀作品获得【开发者社区奖】的关键!
10月24日,我们将通过Online Meetup宣布获奖名单,敬请留意后续参会信息!
欢迎关注InterSystems开发者社区中文版首届技术征文大赛
文章
Michael Lei · 三月 21, 2023
InterSystems IRIS 是一个高性能、可靠且可扩展的数据平台,用于为医疗保健、金融服务和其他行业构建和部署关键任务应用程序。它提供了广泛的功能,包括数据管理、集成、分析等。
IRIS 提供的功能之一是能够将 Python 代码嵌入到 ObjectScript 代码中。这意味着您可以在 IRIS 应用程序中使用 Python 库和函数,让您可以访问大量的工具和资源。在本文中,我们将了解如何在 InterSystems IRIS 中使用嵌入式 Python。
设置嵌入式 Python
在 IRIS 中开始使用嵌入式 Python 之前,您需要设置环境。这涉及安装 Python 解释器和配置 IRIS 以识别它。
第一步是安装 Python。您可以从官方网站 ( https://www.python.org/downloads/ ) 下载最新版本的 Python。安装 Python 后,需要将其添加到系统的 PATH 环境变量中。这允许 IRIS 找到 Python 解释器。
接下来,您需要配置 IRIS 以识别 Python。为此,您需要创建一个 Python 网关。网关是一个在 IRIS 之外运行的进程,充当 IRIS 和 Python 之间的桥梁。
要创建网关,请打开一个终端窗口并导航到 Python 安装目录。然后运行以下命令:
python -m irisnative
此命令启动 Python 网关并创建到 IRIS 的连接。您应该看到类似于以下内容的输出:
Python Gateway Version: 0.7 Python Version: 3.8.5 (default, Jan 27 2021, 15:41:15) [GCC 9.3.0] Connected to: IRIS
这表明 Python 网关正在运行并连接到 IRIS。
在 IRIS 中使用嵌入式 Python
现在您已经设置了环境,您可以开始在 IRIS 中使用 Python。为此,您需要使用 ##class(%Net.Remote.Gateway).%New() 方法创建到 Python 网关的连接。此方法返回对可用于执行 Python 代码的 Python 对象的引用。
以下是如何使用嵌入式 Python 计算两个数字之和的示例:
Set gateway = ## class (%Net.Remote.Gateway).% New () Set result = gateway.% Execute ( "2 + 3" ) Write "Result: " , result , !
在此示例中,我们使用 %New() 方法创建到 Python 网关的连接。然后我们执行Python代码"2 + 3",返回结果5。我们将结果存储在result-变量中并输出到控制台。
在 IRIS 中使用 Python 库
在 IRIS 中使用嵌入式 Python 的好处之一是能够使用 Python 库。 Python 拥有庞大的库生态系统,用于数据处理、机器学习等。通过使用这些库,您可以扩展 IRIS 应用程序的功能。
要在 IRIS 中使用 Python 库,您首先需要使用 pip包管理器安装该库。例如,要安装 numpy 库,请运行以下命令:
pip install numpy
安装该库后,您可以通过使用 Python 网关导入它来在 IRIS 应用程序中使用它。以下是如何使用 numpy库创建矩阵的示例:
vbnet
Set gateway = ##class(%Net.Remote.Gateway).%New() Set np = gateway.%Get("numpy") Set matrix = np.%New("array", 10, 10)
在这个例子中,
我们使用 %New()方法创建到 Python 网关的连接。然后我们使用 -%Get() 方法检索对 numpy 库的引用。我们使用 numpy 库中的 array 方法创建一个新矩阵,大小为 10 x 10。我们将矩阵存储在matrix 变量,然后我们可以在我们的 IRIS 应用程序中使用它。
结论
在本文中,我们了解了如何在 InterSystems IRIS 中使用嵌入式 Python。我们已经了解了如何在 IRIS 中设置环境、执行 Python 代码和使用 Python 库。使用嵌入式 Python 可以扩展 IRIS 应用程序的功能,使您能够访问大量的工具和资源。通过将 Python 的强大功能与 IRIS 的性能和可靠性相结合,您可以构建满足组织需求的关键任务应用程序。
文章
姚 鑫 · 九月 12, 2022
# 第三十章 管理许可(三)
# 确定许可证容量和使用情况
如何知道已使用了多少许可证以及由谁使用?类中的 `%SYSTEM.License` 提供了到 `IRIS` 许可证应用程序编程接口 (`API`) 的接口,并提供了许多方法和相关查询,可以使用这些方法和相关查询来查询许可证容量和当前使用情况。
可以使用 `%Library.%ResultSet` 类的 `RunQuery` 方法运行多个许可查询。例如:
```
USER>do ##class(%ResultSet).RunQuery("%SYSTEM.License","Summary")
LicenseUnitUse:Local:Distributed:
当前使用的软件许可单元 :2:2:
使用的最大软件许可单元数 :3:2:
授权的软件许可单元 :25:25:
当前连接 :2:2:
最大连接数 :6:6:
```
可以从管理门户的许可证使用页面(系统操作 > 许可证使用)查看这些查询的输出,详细信息如下表所示:
许可证使用页面上的链接| `License Query`
---|---
`Summary` |`Summary()` — 返回许可证使用摘要,如 `$System.License.ShowSummary` 所示。
`Usage by Process`| `ProcessList()` — 返回操作系统进程标识符 (`PID`) 使用的许可证,如 `$System.License.DumpLocalPID` 所示。
`Usage by User`| `UserList()` —按用户 ID 返回许可证使用。
`Distributed License Usage`| `AllKeyConnectionList()` — 返回按用户排序的当前分布式许可证使用情况。 (当没有连接许可服务器时禁用此功能。)
还可以使用 `%SYSTEM.License` 中的以下类方法来显示信息,或将许可证数据库转储到文件中:
`$System.License.CKEY` 显示密钥。该子例程由 `^CKEY` 程序调用,该程序为保持兼容性而保留:
```java
USER>Do $System.License.CKEY()
InterSystems IRIS Key display:
Based on the active key file 'c:\intersystems\irishealth\mgr\iris.key'
LicenseCapacity = InterSystems IRIS 2021.2 Enterprise - Concurrent Users for x86-64 (Microsoft Windows):25, Natural Language Processing (NLP), En
CustomerName = ISC DC Moderators - Xin Yao
OrderNumber = 202224285
ExpirationDate = 7/15/2023
AuthorizationKey = 4125500002500002500000XXXXXXXXXXXXXXXXX01
MachineID =
当前可用 = 23
最小可用 = 22
最大可用 = 25
```
`$System.License.ShowCounts` 总结了在本地系统共享内存中跟踪的许可证使用情况:
```java
USER> Do $System.License.ShowCounts()
本地软件许可使用视图.
25 授权的总数量 LU
23 当前可用 LU
22 最小可用 LU
2 当前用户处于活动状态
3 处于活动状态的最大用户数
1 当前 CSP 用户处于活动状态
1 处于活动状态的最大 CSP 用户数
0 当前 CSP 会话处于宽限期
0 处于宽限期的最大 CSP 会话数
```
`.License.ShowServer` 显示活动的许可证服务器地址和端口:
```java
USER> Do $System.License.ShowServer()
活动软件许可服务器地址 = 127.0.0.1 端口 = 4002
```
如果开发了基于 `REST` 的应用程序,许可证将随着使用而消耗。为防止这种情况发生,请配置可以建立的 `Web Gateway` 连接数。从 `Web Gateway` 管理部分的管理门户:
1. 导航到服务器访问。
2. 选择无状态参数。
3. 将最大值设置为比许可证小 `2` 或 `3` 的数字,以允许服务器端登录。
**注意:根据应用程序的服务器端需求,需要对此进行调整。**
通过在所有可用连接都忙时执行此操作,新请求将排队而不是被拒绝。由于超出许可计数,不会看到拒绝。随着数量的增长,客户端的响应时间会减慢。这表明需要购买更多许可证。 如果开发了基于 REST 的应用程序,许可证将随着使用而消耗。为防止这种情况发生,请配置可以建立的 Web Gateway 连接数。从 Web Gateway 管理部分的管理门户
姚老师,上面描述的这段是从哪里进入的啊,可以给与相应的导航图吗
文章
姚 鑫 · 三月 10, 2022
# 第七十七章 SQL函数 LENGTH
返回字符串表达式中字符数的字符串函数。
# 大纲
```java
LENGTH(string-expression)
{fn LENGTH(string-expression)}
```
# 参数
- `string-expression` - 字符串表达式,可以是列名、字符串文字或另一个标量函数的结果,其中基础数据类型可以表示为任何字符类型(例如 `CHAR` 或 `VARCHAR`)。
`LENGTH` 返回 `INTEGER` 数据类型。
# 描述
`LENGTH` 返回一个整数,表示给定字符串表达式的字符数,而不是字节数。字符串表达式可以是字符串(从中删除尾随空格)或数字( IRIS 将其转换为规范形式)。
请注意,`LENGTH` 可用作 ODBC 标量函数(使用花括号语法)或 SQL 通用函数。
`LENGTH` 和其他长度函数(`$LENGTH`、`CHARACTER_LENGTH`、`CHAR_LENGTH` 和 `DATALENGTH`)都执行以下操作:
- `LENGTH` 返回字段的逻辑(内部数据存储)值的长度,而不是显示值,无论 `SelectMode` 设置如何。所有 SQL 函数始终使用字段的内部存储值。
- `LENGTH` 返回数字的规范形式的长度。规范形式的数字不包括前导零和尾随零、前导符号(单个减号除外)和尾随小数分隔符。 `LENGTH` 返回数字字符串的字符串长度。数字字符串不会转换为规范形式。
- `LENGTH` 不排除字符串中的前导空格。可以使用 `LTRIM` 函数从字符串中删除前导空格。
在执行以下操作时,`LENGTH` 与其他长度函数(`$LENGTH`、`CHARACTER_LENGTH`、`CHAR_LENGTH` 和 `DATALENGTH`)不同:
- `LENGTH` 不包括尾随空格和字符串终止字符。
`$LENGTH`、`CHARACTER_LENGTH`、`CHAR_LENGTH` 和 `DATALENGTH` 不排除尾随空格和终止符。
- 如果传递一个 `NULL` 值,`LENGTH` 返回 `NULL`,如果传递一个空字符串,则返回 `0`。
如果传递 `NULL` 值,`CHARACTER_LENGTH`、`CHAR_LENGTH` 和 `DATALENGTH` 也返回 `NULL`,如果传递空字符串,则返回 `0`。如果传递一个 `NULL` 值,则 `$LENGTH` 返回 `0`,如果传递一个空字符串,则返回 `0`。
- `LENGTH` 不支持数据流字段。为字符串表达式指定流字段会导致 `SQLCODE -37`。
`$LENGTH` 也不支持流字段。 `CHARACTER_LENGTH`、`CHAR_LENGTH` 和 `DATALENGTH` 函数确实支持数据流字段。
# 示例
在以下示例中, IRIS 首先将每个数字转换为规范形式(删除前导零和尾随零,解析前导符号,并删除尾随小数分隔符)。每个 `LENGTH` 返回长度为 `1`:
```sql
SELECT {fn LENGTH(7.00)} AS CharCount,
{fn LENGTH(+007)} AS CharCount,
{fn LENGTH(007.)} AS CharCount,
{fn LENGTH(00000.00)} AS CharCount,
{fn LENGTH(-0)} AS CharCount
1 1 1 1 1
```
在以下示例中,第一个 `LENGTH` 删除前导零,返回长度值 `2`;第二个 `LENGTH` 将数值视为字符串,并且不删除前导零,返回长度值 `3`:
```sql
SELECT LENGTH(0.7) AS CharCount,
LENGTH('0.7') AS CharCount
2 3
```
以下示例返回值 `12`:
```sql
SELECT LENGTH('INTERSYSTEMS') AS CharCount
12
```
以下示例显示了 `LENGTH` 如何处理前导和尾随空格。第一个 `LENGTH `返回 `15`,因为 `LENGTH` 不包括尾随空格,但不包括前导空格。第二个 `LENGTH` 返回 `12`,因为 `LTRIM` 排除了前导空格:
```sql
SELECT LENGTH(' INTERSYSTEMS ') AS CharCount,
LENGTH(LTRIM(' INTERSYSTEMS ')) AS CharCount
15 12
```
以下示例返回 `Sample.Person` 表中每个 `Name` 值中的字符数:
```sql
SELECT Name,{fn LENGTH(Name)} AS CharCount
FROM Sample.Person
ORDER BY CharCount
```
以下示例返回 `DOB`(出生日期)字段中的字符数。请注意,返回的长度(由 `LENGTH`、`CHAR_LENGTH` 和 `CHARACTER_LENGTH`)是日期的内部 (`$HOROLOG`) 格式,而不是显示格式。 `DOB`的显示长度为十个字符;所有三个长度函数都返回 5 的内部长度:
```sql
SELECT DOB,{fn LENGTH(DOB)} AS LenCount,
CHAR_LENGTH(DOB) AS CCount,
CHARACTER_LENGTH(DOB) AS CtrCount
FROM Sample.Person
```
以下嵌入式 SQL 示例给出了 `Unicode` 字符字符串的长度。返回的长度是字符数 (7),而不是字节数。
```java
/// d ##class(PHA.TEST.SQLCommand).Length()
ClassMethod Length()
{
s a = $CHAR(920,913,923,913,931,931,913)
&sql(SELECT LENGTH(:a) INTO :b )
if SQLCODE'=0 {
w !,"Error code ",SQLCODE
} else {
w !,"The Greek Sea: ",a,!,$LENGTH(a),!,b
}
}
```
```java
DHC-APP>d ##class(PHA.TEST.SQLCommand).Length()
The Greek Sea: ΘΑΛΑΣΣΑ
7
7
```
文章
姚 鑫 · 七月 4, 2021
# 第二十七章 定制SAX解析器的执行自定义实体解析
# 执行自定义实体解析
XML文档可能包含对外部DTD或其他实体的引用。默认情况下,InterSystems IRIS尝试查找这些实体的源文档并解析它们。要控制InterSystems IRIS解析外部实体的方式,请使用以下步骤:
1. 定义实体解析程序类。
此类必须在扩展`%XML.SAX.EntityResolver`,并且必须实现 `resolveEntity()`方法,该方法具有以下签名:
```java
method resolveEntity(publicID As %Library.String, systemID As %Library.String) as %Library.Integer
```
每当XML处理器找到对外部实体(如DTD)的引用时,就会调用该方法;这里的public ID和systemID是该实体的Public和系统标识符字符串。
该方法应获取实体或文档,将其作为流返回,然后在将流包装在`%XML.SAX.StreamAdapter`的实例中。此类提供了用于确定流特征的必要方法。
如果无法解析该实体,则该方法应返回`$$$NULLOREF` ,以向SAX解析器指示该实体无法解析)。
尽管方法签名指示返回值为`%Library.Integer`,但该方法应返回`%XML.SAX.StreamAdapter`的实例或该类的子类。
此外,引用外部实体的标识符始终传递给文档中指定的`resolveEntity()`方法。具体地说,如果这样的标识符使用相对URL,则该标识符将作为相对URL传递,这意味着引用文档的实际位置不会传递给`resolveEntity()`方法,并且无法解析该实体。在这种情况下,请使用默认实体解析器,而不是自定义实体解析器。
2. 读取XML文档时,请执行以下操作:
a. 创建实体解析程序类的实例。
b. 读取XML文档时使用该实例,如本章前面的“指定解析器选项”中所述。
## 示例
例如,以下XML文档:
```xml
Some < xhtml-content > with custom entities &entity1; and &entity2;.
Here is another paragraph with &entity1; again.
```
本文档使用以下DTD:
```xml
```
要阅读本文档,需要如下所示的自定义实体解析器:
```java
Class CustomResolver.Resolver Extends %XML.SAX.EntityResolver
{
Method resolveEntity(publicID As %Library.String, systemID As %Library.String) As %Library.Integer
{
Try {
Set res=##class(%Stream.TmpBinary).%New()
//check if we are here to resolve a custom entity
If systemID="http://www.intersystems.com/xml/entities/entity1"
{
Do res.Write("Value for entity1")
Set return=##class(%XML.SAX.StreamAdapter).%New(res)
}
Elseif systemID="http://www.intersystems.com/xml/entities/entity2"
{
Do res.Write("Value for entity2")
Set return=##class(%XML.SAX.StreamAdapter).%New(res)
}
Else //otherwise call the default resolver
{
Set res=##class(%XML.SAX.EntityResolver).%New()
Set return=res.resolveEntity(publicID,systemID)
}
}
Catch
{
Set return=$$$NULLOREF
}
Quit return
}
}
```
下面的类包含一个demo方法,该方法解析前面显示的文件并使用此自定义解析器:
```java
Include (%occInclude, %occSAX)
Class CustomResolver.ParseFileDemo
{
ClassMethod ParseFile()
{
Set res= ##class(CustomResolver.Resolver).%New()
Set file="c:/temp/html.xml"
Set parsemask=$$$SAXALLEVENTS+$$$SAXERROR
Set status=##class(%XML.TextReader).ParseFile(file,.textreader,res,,parsemask,,0)
If $$$ISERR(status) {Do $system.OBJ.DisplayError(status) Quit }
Write !,"Parsing the file ",file,!
Write "Custom entities in this file:"
While textreader.Read()
{
If textreader.NodeType="entity"{
Write !, "Node:", textreader.seq
Write !," name: ", textreader.Name
Write !," value: ", textreader.Value
}
}
}
}
```
下面显示了此方法在终端会话中的输出:
```java
GXML>d ##class(CustomResolver.ParseFileDemo).ParseFile()
Parsing the file c:/temp/html.xml
Custom entities in this file:
Node:13
name: entity1
value: Value for entity1
Node:15
name: entity2
value: Value for entity2
Node:21
name: entity1
value: Value for entity1
```
## 示例2
例如,读取包含以下内容的XML文档:
```xml
```
在本例中,将在`publicId`设置为 `-//OASIS//DTD DocBook XML V4.1.2//EN`并将`systemId`设置为`c:\test\doctypes\docbook\docbookx.dtd.`的情况下调用`resolveEntity`方法。
`resolveEntity`方法确定外部实体的正确源,将其作为流返回,并将其包装在`%XML.StreamAdaptor`的实例中。XML解析器从这个专用流中读取实体定义。
例如,请参考InterSystems IRIS库中包含的`%XML.Catalog`和`%XML.CatalogResolverclass。%XML.Catalog`类定义一个简单的数据库,该数据库将公共和系统标识符与URL相关联。`%XML.CatalogResolver`类是一个实体解析器类,它使用此数据库查找给定标识符的URL。`%XML.Catalogclass`可以从SGML样式的编录文件加载其数据库;该文件将标识符映射到标准格式的URL。