搜索​​​​

清除过滤器
文章
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高可用架构的选择

现在我们院区也遇到这样一个问题。关于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 · 九月 1, 2023

JWT - JSON Web Token Authentication

JWT Authentication 原理及验证流程 原理 JWT 是一种结构紧凑、URL 安全的身份验证、授权或信息交换方式。在身份验证的情况下,服务器会向已通过身份验证的客户端提供一个 JWT,这样客户端在 JWT 过期之前就无需再提供密码来访问服务器上受保护的资源。 验证流程: 客户端发送Login到服务端 服务端返回 JWT给客户端 客户端校验JWT签名 客户端发送带有JWT签名的request到服务端 服务端检查JWT签名的有效期,在有效期内,则返回response给客户端,不在有效期内,返回error JWT 配置步骤 创建REST服务 配置Web Application 安全配置 客户端发送Login,从服务器获取JWT 配置成功,发送带有JWT的request 1. 创建REST服务 在InterSystems IRIS中,可以使用/api/mgmnt自动创建REST的 .disp .impl 和 .spec 类,本篇文章不介绍具体创建REST服务的流程,具体内容请参考社区文章创建REST 服务。 2. 配置Web Application IRIS管理门户:系统管理 -> 安全 -> 应用程序 -> Web 应用程序 选中 ‘Use JWT Authentication’ 复选框,并设置‘JWT Access Token Timeout’ 和‘JWT Refresh Token Timeout’, 其中,JWT Access Token Timeout 为JWT 令牌超时秒数,如果你打算长时间测试 API,建议将此值设为 1 小时(3600 秒),JWT Refresh Token Timeout(令牌刷新超时的秒数)设为 900 秒。 3. 安全配置 IRIS管理门户:系统管理 -> 安全 -> 系统安全 -> 身份验证/Web 会话选项 ‘JWT Issuer field’用于签名和验证 JWT 的签名算法。‘JWT Issuer field’将出现在 JWT 的声明部分,其目的是告知是谁给了你这个令牌。您可以将其设置为 "InterSystems"。 4. 客户端发送Login,从服务器获取JWT 使用基本 HTTP 身份验证或在请求正文中使用有效凭证发送POST请求到http://<api>/login端点,服务器端会返回一个访问令牌和一个刷新令牌,可在后续请求中使用。 body样例: { "user": "superuser", "password": "iris" } 5.配置成功,发送带有JWT的request 到此为止,您已经成功配置JWT,可以发送带有JWT的请求了。只需要在Header中添加Authorization,如下所示: -H "Authorization: Bearer {access_token}"
文章
Hao Ma · 一月 15, 2021

ObjectScript错误管理

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管理错误,但不要滥用。
文章
Jingwei Wang · 五月 4, 2022

IntegratedML 演练

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(预测值)。
文章
姚 鑫 · 五月 1, 2023

第四十五章 管理镜像 - 使用 ^MIRROR 状态监视器

# 第四十五章 管理镜像 - 使用 ^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` 过程。系统上通常配置了多个镜像预取作业。镜像备份:这个过程是一个双向通道,将从主服务器接收到的日志记录写入备份的镜像日志文件,并向主服务器返回确认。
文章
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资源 。
文章
姚 鑫 · 七月 4, 2021

第二十七章 定制SAX解析器的执行自定义实体解析

# 第二十七章 定制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。
文章
姚 鑫 · 三月 10, 2022

第七十七章 SQL函数 LENGTH

# 第七十七章 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 ```
文章
姚 鑫 · 九月 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 管理部分的管理门户 姚老师,上面描述的这段是从哪里进入的啊,可以给与相应的导航图吗
公告
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

参赛文章选编- 使用嵌入式 Python教程

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 的性能和可靠性相结合,您可以构建满足组织需求的关键任务应用程序。
文章
Jingwei Wang · 七月 4, 2022

IRIS/HealthConnect 高可用机制

高可用性(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,其行为与不活动备机的描述一样。 仲裁机控制模式 当故障转移成员相互连接时,两者都连接到仲裁机,并且备机是活动的,它们进入仲裁机控制模式,在该模式中,故障转移会员根据仲裁机提供的关于另一个故障转移成员的信息对它们之间的联系丢失做出响应。因为每个故障转移成员通过测试其与另一个故障转移成员的连接来响应其仲裁连接的丢失,反之亦然,由单个网络事件引起的多个连接丢失被作为单个事件处理。 在仲裁机控制模式中,如果故障转移成员仅丢失其仲裁机连接,或者备机 丢失其活动状态,则故障转移成员协调切换到代理控制模式。 如果主节点和备机节点之间的连接在仲裁机控制模式下断开,则每个故障转移成员根据仲裁机连接的状态进行响应,如下图所述。 所有三个系统连接: 镜像进入仲裁机控制模式(如果尚未进入仲裁机控制模式) 备机失去与仲裁机的连接,但仍连接到主节点: 镜像切换到代理控制模式 主节点继续作为主节点运行 备机尝试重新连接仲裁机 主节点失去与仲裁机的连接,但仍连接到备机: 镜像切换到代理控制模式 主节点继续作为主节点运行 主节点尝试重新连接仲裁程序 故障转移成员彼此失去连接,仍然连接到仲裁机: 镜像切换到代理控制模式 主节点继续作为主节点运行 备机尝试重新连接主节点 仲裁机失败或隔离-故障转移成员失去与仲裁机的连接,但仍彼此连接: 镜像切换到代理控制模式 主节点继续作为主节点运行 两个故障转移成员都尝试重新连接仲裁机 备机中断或被隔离-主节点和仲裁机失去与备机的连接,但仍相互连接: 主节点切换到代理控制模式并继续作为主节点操作 备机(如果在操作中)切换到代理控制模式并尝试重新连接到主节点 主节点中断或被隔离-备机和仲裁机失去与主节点的连接,但仍相互连接: 主节点(如果在运行中)将无限期地保持在仲裁控制模式和故障状态 备机作为主机接管,切换到代理控制模式,并在恢复连接时强制主机关闭 三个连接全部丢失: 主节点(如果在运行中)将无限期地保持在仲裁控制模式和故障状态;如果与备机设备联系,则切换到代理控制模式并恢复主设备的运行 备机(如果在操作中)切换到代理控制模式并尝试重新连接到主节点 注意: 由于单个事件(或多个同时发生的事件)而导致所有连接丢失的情况很少见。在大多数情况下,镜像在所有连接丢失之前切换到代理控制模式,在这种情况下: 主节点(如果在运行)继续作为主节点运行 备机(如果正在运行)尝试重新连接到主节点
文章
姚 鑫 · 五月 7, 2021

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

# 第三章 使用多维存储(全局变量)(三) # 在全局变量中复制数据 若要将全局变量(全部或部分)的内容复制到另一个全局变量(或局部数组)中,请使用ObjectScript `Merge`命令。 下面的示例演示如何使用`Merge`命令将`OldData`全局变量的全部内容复制到`NewData`全局变量中: ``` Merge ^NewData = ^OldData ``` 如果合并命令的`source`参数有下标,则复制该节点及其后代中的所有数据。如果`Destination`参数有下标,则使用目标地址作为顶级节点复制数据。例如,以下代码: ```java Merge ^NewData(1,2) = ^OldData(5,6,7) ``` 将`^OldData(5,6,7)`及其下的所有数据复制到`^NewData(1,2)`。 # 维护全局变量内的共享计数器 大规模事务处理应用程序的一个主要并发瓶颈可能是创建唯一标识符值。例如,考虑一个订单处理应用程序,在该应用程序中,必须为每一张新发票指定一个唯一的标识号。传统的方法是维护某种计数器表。每个创建新发票的进程都会等待获取此计数器上的锁,递增其值,然后将其解锁。这可能会导致对此单个记录的激烈资源争用。 为了解决此问题,InterSystems IRIS提供了ObjectScript `$INCREMENT`函数。`$INCREMENT`自动递增全局节点的值(如果该节点没有值,则设置为1)。`$INCREMENT`的原子性意味着不需要锁;该函数保证返回一个新的增量值,不会受到任何其他进程的干扰。 可以使用`$INCREMENT`,如下所示。首先,必须决定在其中存放计数器的全局节点。接下来,无论何时需要新的计数器值,只需调用`$INCREMENT`: ```java SET counter = $INCREMENT(^MyCounter) ``` InterSystems IRIS对象和SQL使用的默认存储结构使用`$INCREMENT`来分配唯一的对象(行)标识符值。 # 对全局变量中的数据进行排序 存储在全局变量中的数据会根据下标的值自动排序。例如,下面的ObjectScript代码定义了一组全局变量(按随机顺序),然后遍历它们以演示全局节点按下标自动排序: ```java /// 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 "" } ``` ```java DHC-APP> w ##class(PHA.TEST.Global).GlobalSort() Athens Boston Cambridge London New York ``` 应用程序可以利用全局函数提供的自动排序来执行排序操作或维护对某些值的有序、交叉引用的索引。 InterSystems SQL和ObjectScript使用全局变量自动执行这些任务。 # 全局变量节点排序规则 全局变量节点的排序顺序(称为排序)在两个级别上进行控制:全局变量本身内部和使用全局变量的应用程序。 在应用程序级别,可以通过对用作下标的值执行数据转换来控制全局节点的排序方式(InterSystems SQL和对象通过用户指定的排序函数来执行此操作)。 例如,如果创建一个按字母顺序排序但忽略大小写的名称列表,那么通常你会使用名称的大写版本作为下标: ```java /// 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`值进行排序的索引,则可以强制年龄始终为数字:** ```java Set ^Data(+age,id) = "" ``` **如果希望将值排序为字符串(如`“0022”`、`“0342”`、`“1584”`),则可以通过添加空格(`“”`)字符来强制下标值始终为字符串。例如,如果正在构建一个按邮政编码对`id`值进行排序的索引,则可以强制`zipcode`始终为字符串:** ```java Set ^Data(" "_zipcode,id) = "" ``` 这确保带有前导零的值(如`“0022”`)始终被视为字符串。 ## `$SORTBEGIN`和`$SORTEND`函数 通常,不必担心在InterSystems IRIS中对数据进行排序。无论使用SQL还是直接全局访问,排序都是自动处理的。 然而,在某些情况下,可以更有效地进行排序。 具体来说,在以下情况下(`1`)需要设置大量随机(即未排序)的全局节点,(`2`)生成的全局节点的总大小接近InterSystems IRIS缓冲池的很大一部分,那么性能可能会受到不利影响- 因为很多SET操作涉及到磁盘操作(因为数据不适合缓存)。 这种情况通常出现在涉及创建索引全局函数的情况下,例如批量数据加载、索引填充或对临时全局函数中的未索引值进行排序 为了有效地处理这些情况,ObjectScript提供了`$SORTBEGIN`和`$SORTEND`函数。 `$SORTBEGIN`函数为全局变量(或其中的一部分)启动了一种特殊模式,在这种模式中,进入全局变量的数据集被写入一个特殊的临时缓冲区,并在内存(或临时磁盘存储)中进行排序。 当在操作结束时调用`$SORTEND`函数时,数据将按顺序写入实际的全局存储中。 总体操作效率更高,因为实际的写操作是按照要求更少磁盘操作的顺序完成的。 `$SORTBEGIN`函数很容易使用; 在开始排序操作之前,用你想要排序的全局变量的名称调用它,并在操作完成时调用`$SORTEND`: ```java /// 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提供了一种在运行时创建全局变量引用的方法。 这对于在程序编译时不知道全局变量结构或名称的应用程序非常有用。 间接操作符@支持间接操作,它解除了对包含表达式的字符串的引用。 根据@操作符的使用方式,有几种间接类型。 下面的代码提供了一个名称间接引用的示例,在这个示例中,使用`@`操作符对包含全局引用的字符串进行解引用: ```java /// 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 "" } ``` ```java DHC-APP> w ##class(PHA.TEST.Global).GlobalIndirect() Value: This data was set indirectly. ``` 也可以使用下标间接在间接语句中混合表达式(变量或文字值): ```java /// 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 "" } ``` ```javan 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的一个基本特性; 它并不局限于全局引用。
问题
water huang · 四月 22, 2021

m里面如何获取cpu的序列号?

m 里面如何获取cpu的序列号? 可以调用操作系统的命令来获取CPU序列号。例如在Cache' for Windows上,可以执行:SAMPLES>s args=3SAMPLES>s args(1)="CPU"SAMPLES>s args(2)="get"SAMPLES>s args(3)="ProcessorID"SAMPLES>d $ZF(-100,"","wmic",.args)ProcessorId0FABFBFF000506EX0FABFBFF000006EX0FABFBFF000006EX0FABFBFF000006EX 乔工,请问 $zf函数的使用,在哪里可以查询到它的所有使用说明 InterSystems Cache'InterSystems IRIS 刚才试了一下,这个不行呢 感谢你的回答 但是我用的是ensemble2016 是Windows吗?在Windows命令行,执行wmic CPU get ProcessorID,能得到CPU序列号吗? Ensemble 2016有点久,没有$ZF(-100)。用$ZF(-1): https://cedocs.intersystems.com/ens20161/csp/docbook/DocBook.UI.Page.cls?KEY=RCOS_fzf-1#RCOS_B80417 是Windows,在Windows命令行,执行wmic CPU get ProcessorID,能得到CPU序列号 刚才试了 还是不行 用$ZF(-1), 可以将OS命令输出保存到文件里。例如:w $ZF(-1,"wmic CPU get ProcessorID > c:\temp\cpuinfo.txt") 系统是windows 10 试了,不行,返回的值是1 返回值是1,说明报错了。确认一下是否OS命令写正确了。另外,输出不是看返回值,是看输出的文件 我直接复制的你写的这个命令。