搜索​​​​

清除过滤器
文章
Nicky Zhu · 五月 20, 2021

互操作消息统一管理系列:SearchTable加速检索

在上一篇文章[《互操作消息统一管理系列:Message Bank》](https://cn.community.intersystems.com/post/%E4%BA%92%E6%93%8D%E4%BD%9C%E6%B6%88%E6%81%AF%E7%BB%9F%E4%B8%80%E7%AE%A1%E7%90%86%E7%B3%BB%E5%88%97%EF%BC%9Amessagebank)中,我们了解到在Message Bank中,消息均以半结构化(XML)或非结构化(Stream)的形式保存,因此无法与客户端的结构化消息一样,直接支持基于索引的检索。为此,需要在Message Bank中定义Search Table以支持查询。关于Search Table的定义和作用,请查阅https://docs.intersystems.com/healthconnectlatest/csp/docbook/DocBook.UI.Page.cls?KEY=EEDI_search_tables。 ## 一. 在Message Bank中查询消息的特殊之处 大家如果使用过消息查看器,则能够了解IRIS自动持久化消息并提供界面让大家能够根据消息头中(如发生事件、来源、目标等)或消息体中(如患者姓名、诊断名称等消息的具体属性)来查询消息。 而在Message Bank上进行查询时,如果直接使用消息查看器,能够查询的是Message Bank的Production中传输的消息,而若不是在源系统中出现的消息,这一点一定不要混淆。 Message Bank提供了消息仓库查看器供大家查询源系统中的消息 ![image](/sites/default/files/inline/images/2_1_0.png) 查询界面尽管在风格与功能上与消息查看器非常相似,但查询的目标是在Message Bank中转储的消息(存储格式为虚拟文档或字符流,见[上一篇文章](https://cn.community.intersystems.com/post/%E4%BA%92%E6%93%8D%E4%BD%9C%E6%B6%88%E6%81%AF%E7%BB%9F%E4%B8%80%E7%AE%A1%E7%90%86%E7%B3%BB%E5%88%97%EF%BC%9Amessagebank))。因此,查询索引与原来的消息截然不同,需要单独创建。其中,虚拟文档在IRIS中有原生支持,我们先来看自定义消息类型的处理过程。 先看这样一个案例。 ## 二. 测试系统 假设我们有一个REST接口实现的添加患者业务,如下所示: ![image](/sites/default/files/inline/images/2_2_0.png) 实现了REST接口,接收如下格式的POST请求,在该实例数据库中创建患者: ![image](/sites/default/files/inline/images/2_3_0.png) 注意,从外部JSON传入的报文,在经过IRIS处理的过程中,会被转换为XML格式的消息,如下: ![image](/sites/default/files/inline/images/2_4_0.png) 在被转储到Message Bank后,以同样结构的XML字符流存储 ![image](/sites/default/files/inline/images/2_5_1.png) ## 三. 在消息转储过程中创建索引 Message Bank中转储的消息默认是没有索引的,需要在消息转储过程中创建。 ### 通过回调干预消息转储过程 Message Bank的Production中的服务Ens.Enterprise.MsgBank.TCPService提供了回调函数入口,使用户可以在消息转储过程中执行自定义的逻辑实现需求。因此,可用于触发对Search Table索引的建立过程。 要定义回调函数,则需要继承类Ens.Enterprise.MsgBank.BankHelperClass,并实现方法OnBankMsg,如下所示: Class MessageBank.MsgSerializationHelper Extends Ens.Enterprise.MsgBank.BankHelperClass [ Language = objectscript ] { /// 参数IndexClassList用于指定将被该回调函数索引的(客户端)消息类 Parameter IndexClassList = "LIS.REST.MSG.PatientReq"; /// 消息签名不能更改 ClassMethod OnBankMsg(pHeader As Ens.Enterprise.MsgBank.MessageHeader, pFullHeaderID As %String, pBody As %RegisteredObject = "", pFullBodyID As %String, pService As Ens.Enterprise.MsgBank.TCPService) As %Status [ Language = objectscript ] { Set tSC = $$$OK do { set clientBodyClassName = pHeader.ClientBodyClassName /// 如果消息类型在本类的关注列表中,则进行后续处理;否则忽略,只被转储 if ..#IndexClassList[clientBodyClassName { /// 使用服务实例将Message Bank消息(包含客户端消息信息)传递给后续组件处理 do pService.SendRequestAsync("MessageBank.Process.ReceiveBankMsgBPL", pHeader) } } while 0 Quit $$$OK } } 注意,尽管整个建立Search Table的过程可以在服务中完成,但由于服务不能开启多进程,对于构建索引这样的高延迟任务,容易变成性能瓶颈。因此,强烈建议将消息转发给BP和BO,并启用多进程处理提高处理速度。 定义好回调函数和Helper类执行,在BS中引用即可,如下: ![image](/sites/default/files/inline/images/2_6_0.png) 在Business Process中分发建立Search Table 建立索引的过程需要应用特定的Search Table类实现,因此通过Business Service将消息分发到Business Process后,则可通过BP根据消息的类型再调用特定的Search Table实现,如下: ![image](/sites/default/files/inline/images/2_7_0.png) 即根据消息类型的不同,使用不同的逻辑进行索引。其中,对于本例的消息类型LIS.REST.MSG.PatientReq ![image](/sites/default/files/inline/images/2_8_0.png) 在switch分支中判定后即可调用对应的代码段处理。 ![image](/sites/default/files/inline/images/2_9_0.png) 在该段代码中,我们需要使用Message Bank消息(该BP的request)中记录的MessageBodyId,对于在源系统中的结构化消息,在Message Bank中被记录为流,该Id即为流记录的Id。根据Id加载流对象后,将该流对象通过IndexDoc方法传递给这类消息对象专用的Search Table实现类完成索引的建立。 ### Search Table类的实现 该类需要继承Ens.CustomSearchTable并实现OnIndexDoc回调方法,如下: Class MessageBank.SearchTable.Client.LISReqTable Extends Ens.CustomSearchTable [ Language = objectscript ] { ///在Search Table中对要建立索引的字段建模,并对该字段建立索引,在查询时即可套用 Property FullName As %String(MAXLEN = 36); Index IdxFullName On FullName; ///特别提醒,在Message Bank中转储的结构化消息类型会变成字符流,因此,我们是在对字符流建立索引,而不是对源系统的消息类型建立索引 Parameter DOCCLASS = "%Stream.GlobalCharacter"; ///建立索引的回调方法 ClassMethod OnIndexDoc(pDocObj As %Persistent, ByRef pSearchTable As MessageBank.SearchTable.Client.LISReqTable) As %Status { set st = $$$OK do { set text = pDocObj.Read($$$MaxLocalLength) set text = $ZSTRIP(text, "*C",, $C(9,10,13,133)) ///读取字符流建立虚拟文档对象 set vDoc = ##class(EnsLib.EDI.XML.Document).ImportFromString(text, .st) quit:$$$ISERR(st) ///从虚拟文档对象获得所需的属性值并为Search Table对象赋值 set pSearchTable.FullName = vDoc.GetValueAt("/PatientReq/FullName") } while 0 if $$$ISERR(st) $$$LOGWARNING("Failed to index message LIS.REST.MSG.PatientReq:"_pDocObj.%Id()_" "_st) quit $$$OK } } 应当注意的是,使用Search Table支持查询,则意味着查询的目标是Search Table对象而不是直接在Message Bank转储的消息上进行查询。平台的查询功能会负责维护查询逻辑,在Search Table上执行查询并关联到消息上去。 因此,Search Table的属性可以使用消息中的属性组合,大家也可以思考和改进本例实现这样一个场景,源系统中不传FullName全名,代之以分在两个属性中的姓和名。但在Message Bank中却可以在Search Table上建立全名FullName并建立索引,使用户可以通过全名查询消息。 还应当注意到的是,在解析字符流获取所需的属性值时,用户可以选择使用虚拟文档处理,如果能够获得原始消息的类定义,也可以将字符流转换为消息对象再取值。考虑到在实际的生产环境下在Message Bank并不能确保获取源消息定义,采用虚拟文档更为通用且不受类型限制,推荐大家使用。 ## 四. 实现效果 定义好上述代码后启动Message Bank的Production,并测试源系统的接口: ![image](/sites/default/files/inline/images/2_10_0.png) 此时查看Message Bank的production上的消息: ![image](/sites/default/files/inline/images/2_11_0.png) 可看到在Message Bank有新消息触发了处理流程 通过SQL查询Search Table中的记录 ![image](/sites/default/files/inline/images/2_12_0.png) 则可看到最后一行中记录的全名 再转到消息仓库查看器,查询时选择查询表域,选择好对应的SearchTable类型,选择我们刚才使用的全名Biden ![image](/sites/default/files/inline/images/2_13_0.png) 则可查询到消息和对应的Message Trace ![image](/sites/default/files/inline/images/2_14_0.png) ## 对虚拟文档的处理过程 源系统的消息类型如为虚拟文档,例如EnsLib.EDI.XML.Document,则Message Bank的转储消息类型也是EnsLib.EDI.XML.Document,此时的处理流程与上述流程一致,但处理细节上会简单一些。体现在 ### MessageBank消息对象的获取 ![image](/sites/default/files/inline/images/2_15_0.png) 如上图所示,不直接使用字符流,通过Id直接获得虚拟文档对象即可。 ### Search Table的定义 Class MessageBank.SearchTable.DocSearchTable Extends EnsLib.EDI.XML.SearchTable [ Language = objectscript ] { XData SearchSpec [ XMLNamespace = "http://www.intersystems.com/EnsSearchTable" ] { {orderperson} {/root/d/e} {/root/lis/li} } } 如上所示,EnsLib.EDI.XML.Document有系统原生的Search Table模版EnsLib.EDI.XML.SearchTable,继承该类后在XData中定义好Search Table的属性和取值路径(在消息XML中的路径)即可。 关于EnsLib.EDI.XML.Document的Search Table,可通过https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=EEDI_search_tables获取更多信息。
文章
Qiao Peng · 一月 30, 2022

多语言字符集系列文章-- 第二篇 各种技术对字符集使用的声明

各种技术在交换数据的时候,就需要知道对方给的数据使用什么字符集和字符编码,否则很可能就解码错了。这里列举了医疗行业常见的数据交换技术方式和它们对字符集使用的声明方式。 2.1 文件 文件是字符型数据最常见的交换方式,文本编辑工具通常在保存时都会让用户选择保存成什么字符编码。对于不同的字符编码,文件是如何保存的呢? 通常会在文件头使用字节顺序标志(BOM,Byte Order Mark)来标记文件的编码。下表是常见的编码格式对应的BOM,注意ANSI并不需要BOM,我把它列在这里的目的是希望一目了然。 字节 编码格式 00 00 FE FF UTF-32, big-endian FF FE 00 00 UTF-32, little-endian FE FF UTF-16, big-endian FF FE UTF-16, little-endian EF BB BF UTF-8 空 ANSI 例如,汉字的“中”的各种编码如下: 用Windows的写字板软件将这个汉字分别保存成UTF-8、ANSI、Unicode编码,在UltraEdit打开其16进制模式查看,就可以看到如下的输出: 这里ANSI其实保存的是GBK码(使用GBK代码页936),Unicode其实保存的是UTF16编码。试图以ANSI保存文件时,对超出了GBK编码范围的汉字,Windows写字板软件会提示包含Unicode文字,以ANSI保存会丢失数据,如下图: 对于ANSI保存的包含汉字的文件,代码页信息并不在文件里。并不是所有的文本编辑器和文字处理代码都能正确解析这样的文件,因为它们并不知道代码页。这是可能造成文件中文乱码的一个原因。 另外,前面提到通过BOM可以确定文件编码方式,但并不是所有的文件都使用了BOM。因此特定的文本编辑器和文字处理代码对中文都可能产生显示乱码。 2.2 HTTP 对于HTTP消息内容,包括SOAP、RESTful,浏览器/客户端和服务器怎么知道字符编码呢? HTTP头的Content-Type可以通过参数charset指定文字编码。例如: Content-Type: text/html; charset = UTF-8 如果没有正确配置charset,就可能产生乱码。例如网页表单提交的中文数据,服务器没有正确解码从而产生乱码。 不同的Web服务器都可以设置默认的charset,例如Apache可以修改httpd.conf文件,通过配置AddDefaultCharset指定默认字符集编码。 2.3 XML XML文件当然可以使用前面提到的文件编码方式设置,以HTTP传递的XML数据也可以使用HTTP的文字编码设置。同时,XML规范自己也定义了文字编码声明的方式,从而保证通过任何方式传递(例如TCP)的XML都可以被正确解析。 XML定义的编码方式是设置encoding属性,例如: <?xml version="1.0" encoding="UTF-8"?> 注意,JSON并没有声明文字编码的设置。通常JSON数据都是通过HTTP传递的,因此使用HTTP的文字编码设置。 2.4 数据库连接 数据库连接也是造成文字乱码的一个重灾区。客户端从数据库服务器获取数据、向数据库服务器提交数据,怎么知道数据的文字编码呢? 和数据库相关的文字编码有2部分:数据库内码、数据库连接使用的字符编码。 数据库内码: 由于Unicode码历史并不悠久,数据库管理系统的历史远早于Unicode,直到最近这20年,数据库厂商才开始支持Unicode内码。当然,每个厂商的Unicode内码编码也不一样,例如InterSystems的Caché和IRIS是UTF-16格式,而Oracle是UTF-8和UTF-16。因为UTF-8处理效率低,大多数Unicode数据库都使用UTF-16。现在依然能看到不是Unicode内码的老版本数据库。 很多数据库相关的字符编码问题和数据库内码设置有关。例如国内不少的Oracle安装时没设置过字符编码,数据库内码NLS_CHARACTERSET默认为US7ASCII,客户端字符集NLS_LANG默认为AMERICAN_AMERICA.US7ASCII。这并不会造成中文无法保存,因为数据库只是将客户端的数据逐位保存下来,无论什么编码。但这样保存的中文数据只有Oracle自己的客户端能正常显示,其它的客户端可能就按ASCII处理从而造成乱码。因此正确设置数据库的内码很关键。 数据库连接使用的字符编码: ODBC: 直到ODBC 3.5标准(1997年)之前绝大多数的ODBC连接的函数调用和字符串编码都是ANSI(单字节或双字节),所以中文生僻字大多都会处理异常。 ODBC 3.5 规定ODBC驱动管理器要能够透明地处理Unicode和ANSI之间的转换,从而让ANSI和Unicode的数据库客户端都可以正确地向数据库获取和提交数据。 当然,并不是所有的Unicode字符都能转为ANSI码,例如中文“”字,所以ANSI的数据库客户端依然会遇到生僻字乱码。 JDBC: Java对字符串使用UTF-16编码,如果数据库内码也使用UTF-16,那么不会有乱码问题,例如InterSystems IRIS。但很多数据库并非使用UTF-16,这些数据库的JDBC驱动就需要支持UTF-16和数据库内码之间的转换。通常可以通过设置JDBC的连接字符串的特定属性来实现。 例如mysql的连接字符串中指定characterEncoding: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8 其它连接方式: 另外,很多数据库还提供XDBC之外的连接方式,这些连接方式有自己的字符编码逻辑。 2.5 HL7 V2 在医疗行业,HL7 V2是目前最为广泛使用的消息交换标准。它可以通过文件、TCP、HTTP、SOAP 等多种通道交换。因此,HL7 V2消息标准中,也有设置消息字符编码的字段:MSH-18。正确设置该字段有助于不同的系统正确地处理HL7 V2消息里的字符数据。 例如下面的HL7消息在MSH段设置其字符编码为UTF-8: MSH|^~\&||PHLS|||||ORU^R01|||2.5|||AL|||UTF-8| PID||S2345|S2345^^^PHLS^MR|C9876^^^COR^XX~S45008787^^^MA^DL|张^三||19301019|M|||1 Memorial Drive^^剑桥市^MA^02142||||||||063070516 PV1||O|||||ISCGP001^建国^李|||||||EO|||||HSVN00008|||||||||||||||||||||||||20200912090700|20200912090700 ORC||00265-001|0606:H00550R||||^^^202009120910||202009120910|||ISCGP001^建国^李|PHLS||||||||PHLS||||||||LAB OBR||00265-001|0606:H00550R|CBCD^血常规^L|||202009121049|||||||202009120937|Blood|ISCGP001^Moore^James||||||202009121227|||F OBX||NM|WBC^WHITE BLOOD CELL COUNT||6.24|10(9)/L|4.0-10.6||||F|||202009121049 OBX||NM|RBC^RED BLOOD CELL COUNT||4.99|10x12/L|4.5-5.9||||F|||202009121049 OBX||NM|HGB^HEMOGLOBIN||13.6|g/dL|12.0-16.0||||F|||202009121049 OBX||NM|HCT^HEMATOCRIT||41.6|Percent|36.0-46.0||||F|||202009121049 2.6 其它 在上面提到的和字符集相关的乱码之外,有时我们会混淆一些其它的、并非真正乱码的情况。下面这些中文显示的“乱码”,并非乱码: URL编码: 根据RFC 3986,如果URL的路径中有URL的保留字,就需要对URL路径中的保留字使用转义符% 进行转码,也叫做百分号编码。例如ASCII中下面的字符都需要转义: ! # $ & ' ( ) * + , / : ; = ? @ [ ] %21 %23 %24 %26 %27 %28 %29 %2A %2B %2C %2F %3A %3B %3D %3F %40 %5B %5D 对于非ASCII码字符,按其UTF-8编码字节顺序加%转义。例如“https://cn.community.intersystems.com/post/多语言字符集系列文章-第一篇-多语言字符集和相关标准简史”会被转义为: https://cn.community.intersystems.com/post/%E5%A4%9A%E8%AF%AD%E8%A8%80%E5%AD%97%E7%AC%A6%E9%9B%86%E7%B3%BB%E5%88%97%E6%96%87%E7%AB%A0-%E7%AC%AC%E4%B8%80%E7%AF%87-%E5%A4%9A%E8%AF%AD%E8%A8%80%E5%AD%97%E7%AC%A6%E9%9B%86%E5%92%8C%E7%9B%B8%E5%85%B3%E6%A0%87%E5%87%86%E7%AE%80%E5%8F%B2 其中%E5%A4%9A就是“多”字的UTF-8编码E5A49A的3个字节加上%。 在InterSystems技术平台上,可以用$ZCVT("%E5%A4%9A","I","URI")将URL编码翻译为中文字符,或使用$ZCVT("多","O","URI")将汉字进行URL编码。 HTML实体编码(转义): HTML实体编码(HTML Entity Encode)将字符编码为&开头;结尾的字符串,例如"<"编码为"<"。而中文字符就会被按其unicode编码进行转义,例如“广”字的unicode 10进制码为24191,所以它的HTML实体编码是"广"。在InterSystems技术平台上,可以用$ZCVT("广","I","HTML")将其翻译为中文字符。 Base64 编码: 当使用SOAP传递的数据是一个XML字符串或其它应被视为二进制类型的数据时,通常使用Base64对其进行编码,从而不会破坏XML结构解析。所以它也不是乱码。
文章
Nicky Zhu · 二月 3, 2021

IRIS中的权限管理

下一篇: [案例: 建立只能使用SQL的用户](https://cn.community.intersystems.com/post/%E6%A1%88%E4%BE%8B-%E5%BB%BA%E7%AB%8B%E5%8F%AA%E8%83%BD%E4%BD%BF%E7%94%A8sql%E7%9A%84%E7%94%A8%E6%88%B7) IRIS通过认证(Authentication)与授权(Authorization)两项机制控制外部用户对系统及应用、数据资源的可访问性。因此。如需要进行权限控制,则需要通过配置认证和授权进行。 ## IRIS中的认证 {#2} 认证可以验证任何试图连接到InterSystems IRIS®的用户的身份。一旦通过认证,用户就与IRIS建立了通信,从而可以使用其数据和工具。有许多不同的方法可以验证用户的身份;每种方法都称为验证机制。IRIS 通常被配置为只使用其中一种方式。 支持的认证方式 * 实例认证:通过用户名/密码对登录平台,即密码认证 * LDAP:通过第三方LDAP服务器(如Windows Active Directory )完成认证 * 操作系统认证:建立操作系统用户-平台用户映射,使用操作系统用户登录平台 * Kerberos:使用Kerberos协议进行认证 * 代理认证:使用自定义的代码实现认证过程 ### 系统服务与认证 {#2.1} 在安装时,IRIS会启动一系列系统级的服务用与控制与外部用户或系统的交互,这些服务都绑定了默认的认证机制 ![image](/sites/default/files/inline/images/2.1.png) 图中红框标出的即为系统安装后会自动启用并需经认证才可使用的系统服务,认证手段可配置。 例如,如果变更%Service_Console的身份验证方法,取消密码方法,用户就不能通过输入用户名密码登入Terminal。 通过Portal的菜单 系统管理 > 安全 > 服务 可访问该设置。 ### 账户控制参数 {#2.2} 通过系统管理 > 安全 > 系统安全 > 系统范围的安全参数中的选项可对于用户名/密码认证手段的行为进行更多的约束。 ![image](/sites/default/files/inline/images/2.2.png) * 非活动限制 - 指定用户账户不活跃的最大天数,它被定义为成功登录之间的时间。当达到此限制时,该帐户将被禁用。值为0(0)表示对登录之间的天数没有限制。[对于最低安全级别的安装,默认为0,对于正常和锁定的安装,默认为90]。 * 无效登录限制 (0-64) - 指定连续不成功的登录尝试的最大次数。在达到此限制后,要么禁用账户,要么对每次尝试进行递增的时间延迟;行动取决于如果达到登录限制字段则禁用账户的值。值为0(零)表示对无效登录的次数没有限制。[默认为5] * 如果达到登录限制,则禁用账户 - 如果选中,则指定达到无效登录次数(在前一字段中指定)将导致用户账户被禁用。 * 密码有效期天数(0-99999) - 指定密码过期的频率以及用户更改密码的频率(天数)。当初始设置时,指定密码过期的天数。0(0)表示密码永远不会过期。不会影响已设置了下次登录时更改密码字段的用户。[默认为0] 需要特别注意的是,密码有效性、过期和禁用账户等设置会影响IRIS实例的所有账户,包括IRIS超级管理员账户。如触发了控制策略,则在更新这些帐户的信息之前,可能无法进行各种操作,这可能导致意外的结果。如超级管理员账户被锁定,则需要通过紧急模式启动实例再进行修改。 对于系统可用的认证手段的配置和其他可用的安全配置,请参见[Security Administration Guide](https://docs.intersystems.com/irisforhealthlatest/csp/docbook/DocBook.UI.Page.cls?KEY=GCAS "安全管理向导") ## IRIS中的授权 {#3} ### 授权模型 {#3.1} InterSystems公司的授权模式采用基于角色的访问控制。 * Users – 用户 * Roles – 角色 * Privileges – 权限 * Resources – 资源 | Permissions – 许可 在这种模式下,用户拥有与分配给各自用户身份的角色相关的权限。 ![image](/sites/default/files/inline/images/3.1.png) * 一个角色是一个命名的特权集合 * 一个用户可以拥有一个以上的角色 * 权限分配给角色,角色分配给用户 其中,Roles就是权限的集合,而权限提供对资源的特定类型的访问的许可。 * 可控资源: 数据库,服务,应用(包括Web应用 )和其他 * 可选用的许可: Read, Write or Use,其中执行代码需要数据库的读权限 ### 资源的定义 {#3.2} 资源是一项相对抽象的概念,用来指代IRIS中的数据库,服务,应用等可被访问的对象。例如,对于数据库,在建立时默认采用%DB_%DEFAULT指代,也可自定义资源(数据库资源必须以%DB_开头): ![image](/sites/default/files/inline/images/3.2.png) 对于Web应用,默认不需要通过资源控制,即所有可登录用户都可访问(但该用户进程不一定能访问到数据,还需参照是否具有对数据库的访问权限)。如通过分配资源进行控制,则登录用户还需具有资源才能访问这个Web应用: ![image](/sites/default/files/inline/images/3.3.png) 因此,一项权限实际上是指对某个资源的一些特定操作的集合。 例如,对于数据库UserDB具有读写操作许可的权限A,对于Web应用/csp/sys具有使用操作许可的权限B。如果我们将这两项权限都赋给角色RoleA,那么这个角色就同时拥有A权限和B权限,从而能够访问数据库UserDB和访问Web应用/csp/sys。 ### SQL授权 {#3.3} 除了对数据库进行授权外,IRIS作为一个数据平台,需要对外提供数据访问。因此,IRIS也提供了SQL授权对用户可执行的SQL进行细粒度的权限控制。 SQL的授权可以分配给角色或用户。但通常在企业环境中,用户数量会很多,仍然需要对SQL用户进行分组,根据分组规划角色,通过角色进行授权的控制,才能有效降低维护授权所需的工作量。 SQL的授权针对SQL类型,可分为库、表级授权。 对于Create table、drop view、truncate table这一类的DDL,使用库级授权,即用户可在特定的库中执行建表、删除视图等经过授权的操作。如下: ![image](/sites/default/files/inline/images/3.4.png) 对于Select、update等DML,则使用表级授权,使用户能够通过DML访问特定的表中的数据。如下: ![image](/sites/default/files/inline/images/3.5.png) 除通过Portal操作之外,对于SQL的授权,还可使用IRIS SQL中的额GRANT语句,例如: `GRANT * ON Schema Test TO TestRole` 这个SQL即可以将当前操作数据库下Schema Test中的所有表的所有权限都赋给TestRole这个角色。 关于GRANT语句的用法,可参见[GRANT指令](https://docs.intersystems.com/irisforhealthlatest/csp/docbook/DocBook.UI.Page.cls?KEY=RSQL_grant) 以上即为IRIS中进行权限控制所需掌握的概念和内容,在后续文章中,我们会结合实例向大家介绍其使用。 下一篇: [案例: 建立只能使用SQL的用户](https://cn.community.intersystems.com/post/%E6%A1%88%E4%BE%8B-%E5%BB%BA%E7%AB%8B%E5%8F%AA%E8%83%BD%E4%BD%BF%E7%94%A8sql%E7%9A%84%E7%94%A8%E6%88%B7) 推荐阅读 [Security Administration Guide](https://docs.intersystems.com/irisforhealthlatest/csp/docbook/DocBook.UI.Page.cls?KEY=GCAS "安全管理向导") - https://docs.intersystems.com/irisforhealthlatest/csp/docbook/DocBook.UI.Page.cls?KEY=GCAS
文章
YuHao Wan · 十一月 5, 2022

Caché实现SM4分组密码算法

### 0. 算法概述 SM4算法是一种分组密码算法。其分组长度为128bit,密钥长度也为128bit。加密算法与密钥扩展算法均采用32轮非线性迭代结构,以字(32位)为单位进行加密运算,每一次迭代运算均为一轮变换函数F。SM4算法加/解密算法的结构相同,只是使用轮密钥相反,其中解密轮密钥是加密轮密钥的逆序。 ### 1. 密钥及轮密钥 密钥长度为128比特,表示为MK=(MK(0),MK(1),MK(2),MK(3)),其中MKi(i=0,1,2,3)为字。 轮密钥表示为(rk(0),rk(1),...,rk(31)),其中rk(i)(i=0,...,31)为32比特字。轮密钥由秘钥生成。 ![密钥及轮密钥](https://gitee.com/wanyuhao/intersystems/raw/master/%E5%9B%BD%E5%AF%86%E7%AE%97%E6%B3%95SM3,SM4/images/SM4-01.png) ### 2. 消息填充分组 首先,将明文转化为字节,由于SM4加密算法按照128个位进行分组,所以很大几率会出现最后一个分组不够128位的情况,需要进行**填充**,填充方式有很多,比如ZeroPadding、PKCS7Padding、PKCS5Padding,不管使用哪种方式,最后每个分组都是128位。每个分组按照**32位**一个字分成四个字。 #### ECB模式与CBC模式 - ECB模式 电子密码本模式,最古老,最简单的模式,将加密的数据分成若干组,每组的大小跟加密密钥相同。不足的部分进行填充。 按照顺序将计算所得的数据连在一起即可,各段数据之间互不影响。 优点:简单,有利于并行计算,误差不会被传递。 缺点:不能隐藏明文的模式,可能对明文进行主动攻击。 - CBC模式 密文分组链接模式,也需要进行分组,不足的部分按照**指定的数据**进行填充。 需要一个**初始化向量**,每个分组数据与上一个分组数据加密的结果进行**异或运算**,最后再进行加密。将所有分组加密的结果连接起来就形成了最终的结果。 优点:不容易进行主动攻击,安全性好于ECB。 缺点:不利于并行计算,误差传递,需要初始化向量。 #### 三种填充方式的比较 某些加密算法要求明文需要按一定长度对齐,叫做**块大小**(BlockSize),比如16字节,那么对于一段任意的数据,加密前需要对最后一个块填充到16 字节,解密后需要删除掉填充的数据。 - **ZeroPadding**,数据长度不对齐时使用**0**填充,否则不填充。 - **PKCS7Padding**,假设数据长度需要填充n(n>0)个字节才对齐,那么填充n个字节,每个字节都是**n**;如果数据本身就已经对齐了,则填充一块长度为块大小的数据,每个字节都是块大小。 - **PKCS5Padding**,PKCS7Padding的子集,块大小固定为**8**字节。 由于使用PKCS7Padding/PKCS5Padding填充时,最后一个字节肯定为填充数据的长度,所以在解密后可以准确删除填充的数据,而使用ZeroPadding填充时,没办法区分真实数据与填充数据,所以只适合以\0结尾的字符串加解密。 ### 3. 迭代运算 本加解密算法由32次迭代运算和1次反序变换R组成。 ![迭代运算](https://gitee.com/wanyuhao/intersystems/raw/master/%E5%9B%BD%E5%AF%86%E7%AE%97%E6%B3%95SM3,SM4/images/SM4-02.png) #### 3.1 轮函数F和合成置换T ![轮函数和合成置换T](https://gitee.com/wanyuhao/intersystems/raw/master/%E5%9B%BD%E5%AF%86%E7%AE%97%E6%B3%95SM3,SM4/images/SM4-03.png) ### 4. Caché实现 ``` /// SM4算法是一种分组密码算法。其分组长度为128bit,密钥长度也为128bit。 /// 加密算法与密钥扩展算法均采用32轮非线性迭代结构,以字(32位)为单位进行加密运算,每一次迭代运算均为一轮变换函数F。 /// SM4算法加/解密算法的结构相同,只是使用轮密钥相反,其中解密轮密钥是加密轮密钥的逆序。 /// 本方法适用于 ECB模式,ZeroPadding填充模式 Class Utility.SM4 Extends %RegisteredObject { /// Creator: wyh /// CreatDate: 2022-11-03 /// Description:SM4加密 /// Input: msg:原文 mk:128位密钥 /// Output: 密文 /// Debug: w ##class(Utility.SM4).Encrypt("342622199009262982", "F2D8D966CD3D47788449C19D5EF2081B") ClassMethod Encrypt(msg, mk) { #; 1. 密钥及轮密钥 #; a) 密钥长度为128比特,表示为MK=(MK(0),MK(1),MK(2),MK(3)),其中MKi(i=0,1,2,3)为字 #; b) 轮密钥表示为(rk(0),rk(1),...,rk(31)),其中rk(i)(i=0,...,31)为32比特字。轮密钥由密钥生成。 #; 密钥扩展算法: #; (K(0),K(1),K(2),K(3))=(MK(0)^FK(0),MK(1)^FK(1),MK(2)^FK(2),MK(3)^FK(3)) #; rk(i)=K(i+4)=K(i)^T'(K(i+1)^K(i+2)^K(i+3)^CK(i)),i=0,1,...,31 #; 系统参数FK(0)=(A3B1BAC6),FK(1)=(56AA3350),FK(2)=(677D9197),FK(3)=(B27022DC) #; 固定参数CK(i)(i=0,1,...,31)为: #; 00070E15, 1C232A31, 383F464D, 545B6269, #; 70777E85, 8C939AA1, A8AfB6BD, C4CBD2D9, #; E0E7EEF5, FC030A11, 181F262D, 343B4249, #; 50575E65, 6C737A81, 888F969D, A4ABB2B9, #; C0C7CED5, DCE3EAF1, F8FF060D, 141B2229, #; 30373E45, 4C535A61, 686F767D, 848B9299, #; A0A7AEb5, BCC3CAD1, D8DFE6ED, F4FB0209, #; 10171E25, 2C333A41, 484F565D, 646B7279. s mk = $zcvt(mk, "L") f i = 0 : 1 : 3 d .s MK(i) = $e(mk, 8 * i + 1, 8 * (i + 1)) s FK = "a3b1bac656aa3350677d9197b27022dc" f i = 0 : 1 : 3 d .s FK(i) = $e(FK, 8 * i + 1, 8 * (i + 1)) s CK = "00070e151c232a31383f464d545b626970777e858c939aa1a8afb6bdc4cbd2d9e0e7eef5fc030a11181f262d343b424950575e656c737a81888f969da4abb2b9c0c7ced5dce3eaf1f8ff060d141b222930373e454c535a61686f767d848b9299a0a7aeb5bcc3cad1d8dfe6edf4fb020910171e252c333a41484f565d646b7279" f i = 0 : 1 : 31 d .s CK(i) = $e(CK, 8 * i + 1, 8 * (i + 1)) f i = 0 : 1 : 3 d .s K(i) = ..HexXOR(MK(i), FK(i)) f i = 4 : 1 : 35 d .s K(i) = ..HexXOR(K(i - 4), ..T2(..HexXOR(..HexXOR(..HexXOR(K(i + 1 - 4), K(i + 2 - 4)),K(i + 3 - 4)), CK(i - 4)))) f i = 0 : 1 : 31 d .s rk(i) = K(i + 4) #; 2. 消息填充分组 #; 每组128位,每组再分X(0),X(1),X(2),X(3)作为明文输入. s hex = ..s2hex(msg) s len = $l(hex)/32 s rtn = "" f i = 0 : 1 : len-1 d .k X .s M(i) = $e(hex, 32 * i + 1, 32 * (i + 1)) .f j = 0 : 1 : 3 d ..s X(j) = $e(M(i), 8 * j + 1, 8 * (j + 1)) #; 3. 迭代运算,密文输出(Y(0),Y(1),Y(2),Y(3)) #; a) 32次迭代运算 #; X(i+4)=F(X(i),X(i+1),X(i+2),X(i+3),rk(i)),i=0,1,...,31 #; F(X(i),X(i+1),X(i+2),X(i+3),rk(i))=X(i)^T(X(i+1)^X(i+2)^X(i+3)^rk(i)),i=0,1,...,31 #; b)反序变换 #; (Y(0),Y(1),Y(2),Y(3))=R(X(32),X(33),X(34),X(35))=(X(35),X(34),X(33),X(32)) .f k = 0 : 1 : 31 d ..s X(k + 4) = ..HexXOR(X(k), ..T(..HexXOR(..HexXOR(..HexXOR(X(k + 1), X(k + 2)), X(k + 3)), rk(k)))) .s rtn = rtn_X(35)_X(34)_X(33)_X(32) q rtn } /// Creator: wyh /// CreatDate: 2022-11-03 /// Description:SM4解密 /// 解密变换与加密变换结构相同,不同的仅是轮密钥的使用顺序,解密时使用轮密钥序(rk(31),rk(32),...,rk(0)). /// Input: hex:密文 mk:128位密钥 /// Output: 明文 /// Debug: w ##class(Utility.SM4).Decrypt("5efcbbfdb7a326b340295acb1c0e20fe2622730932bdb5302b5a4ee308944ecc", "F2D8D966CD3D47788449C19D5EF2081B") ClassMethod Decrypt(hex, mk) { s mk = $zcvt(mk, "L") f i = 0 : 1 : 3 d .s MK(i) = $e(mk, 8 * i + 1, 8 * (i + 1)) s FK = "a3b1bac656aa3350677d9197b27022dc" f i = 0 : 1 : 3 d .s FK(i) = $e(FK, 8 * i + 1, 8 * (i + 1)) s CK = "00070e151c232a31383f464d545b626970777e858c939aa1a8afb6bdc4cbd2d9e0e7eef5fc030a11181f262d343b424950575e656c737a81888f969da4abb2b9c0c7ced5dce3eaf1f8ff060d141b222930373e454c535a61686f767d848b9299a0a7aeb5bcc3cad1d8dfe6edf4fb020910171e252c333a41484f565d646b7279" f i = 0 : 1 : 31 d .s CK(i) = $e(CK, 8 * i + 1, 8 * (i + 1)) f i = 0 : 1 : 3 d .s K(i) = ..HexXOR(MK(i), FK(i)) f i = 4 : 1 : 35 d .s K(i) = ..HexXOR(K(i - 4), ..T2(..HexXOR(..HexXOR(..HexXOR(K(i + 1 - 4), K(i + 2 - 4)),K(i + 3 - 4)), CK(i - 4)))) f i = 0 : 1 : 31 d .s rk(i) = K(35 - i) s len = $l(hex)/32 s rtn = "" f i = 0 : 1 : len-1 d .k X .s M(i) = $e(hex, 32 * i + 1, 32 * (i + 1)) .f j = 0 : 1 : 3 d ..s X(j) = $e(M(i), 8 * j + 1, 8 * (j + 1)) .f k = 0 : 1 : 31 d ..s X(k + 4) = ..HexXOR(X(k), ..T(..HexXOR(..HexXOR(..HexXOR(X(k + 1), X(k + 2)), X(k + 3)), rk(k)))) .s rtn = rtn_X(35)_X(34)_X(33)_X(32) q ..hex2str(rtn) } /// 非线性变换τ构成 /// τ由4个并行的S盒,设输入A=(a0,a1,a2,a3),输出为B=(b0,b1,b2,b3) /// (b0,b1,b2,b3)=τ(A)=(Sbox(a0),Sbox(a1),Sbox(a2),Sbox(a3)) /// w ##class(Utility.SM4).tau("942600f0") ClassMethod tau(a) { f i = 0 : 1 : 7 d .s a(i) = $e(a, i + 1) s s(0) = "d690e9fecce13db716b614c228fb2c05" s s(1) = "2b679a762abe04c3aa44132649860699" s s(2) = "9c4250f491ef987a33540b43edcfac62" s s(3) = "e4b31ca9c908e89580df94fa758f3fa6" s s(4) = "4707a7fcf37317ba83593c19e6854fa8" s s(5) = "686b81b27164da8bf8eb0f4b70569d35" s s(6) = "1e240e5e6358d1a225227c3b01217887" s s(7) = "d40046579fd327524c3602e7a0c4c89e" s s(8) = "eabf8ad240c738b5a3f7f2cef96115a1" s s(9) = "e0ae5da49b341a55ad933230f58cb1e3" s s(10) = "1df6e22e8266ca60c02923ab0d534e6f" s s(11) = "d5db3745defd8e2f03ff6a726d6c5b51" s s(12) = "8d1baf92bbddbc7f11d95c411f105ad8" s s(13) = "0ac13188a5cd7bbd2d74d012b8e5b4b0" s s(14) = "8969974a0c96777e65b9f109c56ec684" s s(15) = "18f07dec3adc4d2079ee5f3ed7cb3948" f i = 0 : 1 : 15 d .f j = 0 : 1 : 15 d ..s s(i, j) = $e(s(i), 2 * j + 1, 2 * (j + 1)) s rtn = "" f i = 0 : 1 : 3 d .s r = ..hex2int(a(2 * i)) .s c = ..hex2int(a(2 * i + 1)) .s rtn = rtn _ s(r, c) return rtn } /// 线性变换L /// 非线性变换τ的输出是线性变换L的输入.设输入为B,输出为C. /// C=L(B)=B^(B
问题
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é系统高可用与数据库镜像
文章
Nicky Zhu · 二月 4, 2021

案例: 建立只能使用SQL的用户

上一篇: [IRIS中的权限管理](https://cn.community.intersystems.com/post/iris%E4%B8%AD%E7%9A%84%E6%9D%83%E9%99%90%E7%AE%A1%E7%90%86) 在上一篇文章中,我们介绍了IRIS中的权限控制体系。在本文中我们将以一个常见的实施需求为例介绍如何使用IRIS的权限配置出一个只能使用SQL的用户。 ## 需求的分解 和所有用户需求一样,当用户提出一个需求时,除其语义显式的含义之外,还需分析其是否具有没有明确说明的含义。 对于一个只能使用SQL的用户这样一个需求,即应当结合平台的特征分解成为功能需求: 具有一个合法,可通过用户名和密码使用IRIS的用户 该用户的数据库权限 - 确认项:可以使用SQL访问所有数据库还是某几个特定的数据库? 该用户的SQL权限 - 确认项:对于特定的数据库,是否可以执行所有的DDL? - 确认项:对于特定的数据库,是否对每一张表都可以执行Select、Update等所有的DML 该用户的程序权限 - 确认项:用户是否可以通过Portal登录并管理IRIS? 如上所示,一个只能使用SQL的用户,这样一个看似简单的需求,如果需要与用户获得一致的理解并付诸实施,则需要将其分解,对于没有在用户需求中明确约定的部分,应作为待确认项与用户确认。 另外,需要注意的是,IRIS作为一个数据平台,除了提供底层的数据库之外,也提供了Portal等Web应用程序便于使用Sharding、HA、Interoperability等平台功能,通过平台对外提供的Webservice、REST等web接口也受Web应用程序控制。因此,当需要设计权限体系时,不但需要考虑用户使用的数据库相关的权限,也需要考虑是否需要控制Web应用程序的权限。 我们假设需要提供一个严格意义上的只能对某个库的某个Schema下的所有表具有只读权限的用户,即: 具有一个合法,可通过用户名和密码使用IRIS的用户 该用户的数据库权限 - 该用户只能使用DemoSpace命名空间下的数据库 该用户的SQL权限 - 该用户不能执行任何DDL - 该用户只能对DemoSchema下的所有表执行Select语句 该用户的程序权限 - 该用户不能登录Portal,不能执行Portal中提供的任何管理功能 ## 对数据库的配置 在IRIS中创建数据库时,默认的行为是引用%DB_%DEFAULT这个资源,并引用%DB_%DEFAULT角色(注意,平台中有一个叫做%DB_%DEFAULT的资源,同时还有一个叫做%DB_%DEFAULT的角色。%DB_%DEFAULT角色通过%DB_%DEFAULT资源获得默认的数据库访问权限)。如果直接使用%DB_%DEFAULT角色或%DB_%DEFAULT资源,都有可能影响到之前以默认配置创建的数据库,因此,在需要细粒度控制访问权限时,往往需要自定义资源和角色实现。 ### 创建资源%DB_DemoDB_Res {#3.1} 资源可在创建数据库的同时创建,也可以在使用默认资源创建数据库之后,再给数据库指派其他资源。本例中,我们在创建命名空间DEMOSPACE的同时创建数据库DEMODB并创建资源。 ![image](/sites/default/files/inline/images/picture1_8.png) ### 创建角色DemoDB_Read_Role {#3.2} ![image](/sites/default/files/inline/images/picture2_2.png) 在创建角色之后,即可为其分配资源。根据资源类型的不同,对资源的操作可以有读、写和使用三种权限。对于数据库引用的资源,是读权限和写权限。在本例中,我们需要创建的是只读用户,因此,资源权限分配读权限即可,不用赋写权限。 ![image](/sites/default/files/inline/images/picture3_2.png) ## 创建用户DemoUser并分配角色 通过Portal创建用户之后,即可给用户分配角色。在本例中,需要给这个用户分配之前创建的DemoDB_Read_Role角色。 ![image](/sites/default/files/inline/images/picture4_2.png) 在经过上述配置之后,用户DemoUser即已拥有对命名空间DEMOSPACE中数据库的访问权限。 ![image](/sites/default/files/inline/images/picture5_2.png) 此时,用户对数据库拥有读权限,但并没有对表执行查询或建表的权限,如果尝试create table,则会得到如下的权限错误信息: ![image](/sites/default/files/inline/images/picture_autherror.png) 为继续实验,我们通过Portal执行这SQL语句先创建DemoSchema.Persons这张表 ![image](/sites/default/files/inline/images/picturecreatetable.png) ## 为角色DemoDB_Read_Role分配SQL权限 由于用户具有的权限不足,无法执行SQL操作,因此我们需要对该用户的角色赋予对应的权限(或直接给用户赋权,但平台用户较多时,考虑到用户管理的成本,并不推荐这样做)。可以采用如下手段进行SQL的授权 ### 通过Portal授权 在Portal的用户管理和角色管理功能中,均可指定要授予的SQL权限。 SQL特权栏用于对DDL进行数据库级的授权,例如对DEMOSPACE命名空间下的数据库分配建表、修改表、建视图等DDL操作。在本例中,用户不具有这些权限,因此不对该用户对应的角色授予这些权限。 ![image](/sites/default/files/inline/images/picturerole.png) 在SQL表,SQL视图和SQL过程栏中,则是分别对表、视图和存储过程授予查询、执行等权限。在本例中,用户需要对DemoSchema这个Schema下的表拥有select查询权限,即可通过对SQL表授权进行 ![image](/sites/default/files/inline/images/picturegrant.png) 授权后该角色的SQL表权限如下 ![image](/sites/default/files/inline/images/picture10_1.png) 此时通过SQL工具已可执行查询 ![image](/sites/default/files/inline/images/picture11_0.png) 使用Portal授权时是针对单个的表、视图或存储过程进行。在上例中,我们单独对表DemoSchema.Persons进行了授权,如果我们再建立一张DemoSchema.Employee表,当前的角色和用户并不能自动获得读取其数据的权限。 ### 通过SQL授权 超级管理员或拥有SQL授权权限的用户可以通过SQL的GRANT语句对数据库对象(包括库,函数,表,视图和存储过程等)进行授权,SQL GRANT语句的语法和使用详见[GRANT](https://docs.intersystems.com/irisforhealthlatest/csp/docbook/DocBook.UI.Page.cls?KEY=RSQL_grant),此处不再赘述。 在上面的例子中我们建立了表DemoSchema.Persons,建表的同时建立的Schema DemoSchema。假如现在我们希望对授权进行简化,使角色DemoDB_Read_Role可以直接获得Schema下所有表的读权限,则可以用如下的SQL: `GRANT SELECT ON SCHEMA DemoSchema to DemoDB_Read_Role` 执行成功后再查看DemoDB_Read_Role的SQL权限,会发现: ![image](/sites/default/files/inline/images/picture12_0.png) 即这个角色已经拥有了Schema级的授权,因此,对整个Schema下的所有表都拥有权限。之后在Schema中如果建立了新的表,则这个角色会自动拥有这些表的读权限。 ## 限制用户登录和使用Portal 上例配置的用户可以用于登录IRIS的Portal,但由于没有任何系统功能的权限,不能执行操作。 ![image](/sites/default/files/inline/images/picture13_0.png) IRIS的Web应用程序在创建时默认并不需要额外的资源去访问,这意味着所有合法用户都能登入这个Web应用,但由于支撑应用的后台程序和数据是受到资源的保护的,能登入的用户不一定具有运行程序、访问数据的权限,正如我们建立的用户DemoUser可以登录Portal,但没有功能菜单可用。 如果需要进一步限制用户的行为,禁止其登录,则还需要对应用权限进行控制。 如我们在上一篇文章:[IRIS中的权限管理](https://cn.community.intersystems.com/post/iris%E4%B8%AD%E7%9A%84%E6%9D%83%E9%99%90%E7%AE%A1%E7%90%86) 中所述,Portal是平台提供的Web应用程序,是通过Web应用的权限控制可访问性,因此,需要修改应用的资源要求。 通过菜单: 系统管理 > 安全 > 应用程序 > Web应用程序 可以访问当前系统提供的Web应用列表,其中的/csp/sys即为系统管理门户Portal ![image](/sites/default/files/inline/images/picture14_0.png) 其中必要的资源一栏即为该应用的资源需求,默认为空,即访问该应用不需要特定资源,只要是合法用户即可。我们可以为该应用指定所需资源,例如%Development,即只有具有%Development资源的角色及其对应的用户才能够访问该程序。保存设置后,再尝试以用户DemoUser登录Portal,结果是 ![image](/sites/default/files/inline/images/picture15_0.png) 除非我们为DemoUser引用的角色DemoDB_Read_Role分配资源%Development,该用户都不能登录。 当然,超级管理员由于拥有所有权限,不受这个设置的影响。 ## 总结 通过上述实验,我们创建了一个用户,只能使用SQL连入并查询指定Schema下的表。希望通过这个实验,大家能够掌握IRIS权限管理的基本元素: * 用户,角色,权限,资源和许可构成的权限控制体系 * 数据库、SQL和应用程序都是权限管理的对象 大家在实际项目中可以根据最终用户的实际需要,灵活应用这些概念,构建满足需求的权限配置。 上一篇: [IRIS中的权限管理](https://cn.community.intersystems.com/post/iris%E4%B8%AD%E7%9A%84%E6%9D%83%E9%99%90%E7%AE%A1%E7%90%86) 很棒!学习到了IRIS权限控制! 感谢版主,权限控制讲解的非常清楚! 用户,角色,权限,资源讲解的十分清楚,学到了。
文章
Nicky Zhu · 五月 20, 2021

互操作消息统一管理系列:MessageBank

## 一. 企业信息库简介 企业信息库(MessageBank)是一个可选的远程归档设施,可以从多个来自不同实例的互操作性Production中收集信息、事件日志项目和搜索表项。如下图所示: ![image](/sites/default/files/inline/images/1_1_0.png) 这套环境由两种角色的实例构成: 企业信息库服务器,它本身也是一个Production,完全由Message Bank服务组成,接收来自任何数量的客户Production提交的消息、日志等。 客户端Operation(Message Bank Operation),将其添加到一个正在运行的Production中,并用企业信息库服务器的地址进行配置。如连接通畅,消息和日志即可自动转发到Message Bank并在其中存储。 为了使你能方便地看到信息库中的信息,InterSystems IRIS®提供了以下附加选项。 对于企业信息库实例,管理门户自动包括企业监控器页面,在那里你可以监控客户端Production的状态,浏览消息库,并对被监控客户的消息进行检索。 对于每个客户端实例,你在消息库实例中配置一个到企业监控器的链接。 如下所示: ![image](/sites/default/files/inline/images/1_2_0.png) ## 二. 常见应用场景 ### 消息归档 在使用IRIS互操作性时,对于生产环境,为保障其有充足的磁盘空间和即时查询的效率,通常会采用消息和日志过期策略。在生产环境中只保留近期(如一个月)的信息以备回溯,过期数据将定期被清除。因此,如果有长期保留消息(如在生产环境清除周期之外还需要更长时间的回溯)的需求,则可以通过Message Bank对消息和日志进行长期保存。 ### 企业消息仓库 对于集成规模较大,集成业务较多的大型企业和集团(如大型医院、医联体、医共体),往往会采用多套互操作性实例支撑数据交换和集成业务。在这种环境下,可以通过Message Bank汇聚和存储整个企业环境下的所有互操作消息和日志,为业务集中监控、跨实例业务故障分析等工作创造条件。 ### 消息和日志再利用 理想条件下,实施互操作性项目之后,消息和日志中就会包括大量的业务数据,典型的包括下达的医嘱、患者信息、医疗记录等。通过对Message Bank中的数据进行分析和挖掘,能够获得有价值的业务信息。 接下来我们会为大家介绍Message Bank的搭建过程。 ## 三. 搭建Message Bank ### 创建Message Bank 命名空间 在生产所用的实例之外,我们需要使用一台独立的实例用于安装和配置Message Bank(实例安装过程和License激活过程从略,请查看安装文档或联系您的支持工程师)。 在该实例上,创建一个命名空间安装Message Bank,如下所示: ![image](/sites/default/files/inline/images/1_3_0.png) 由于Message Bank本质上由Production实现,因此创建命名空间时要选上对互操作Producation的支持。 InterSystems为大家提供了可以套用的Production模版。因此,请按照以下步骤创建Production: 在刚才创建的命名空间MessageStore下创建类MessageBank.BankProduction,继承Ens.Enterprise.MsgBank.Production并将Ens.Enterprise.MsgBank.Production中的XData代码块拷贝到新建的类中,如下所示: ![image](/sites/default/files/inline/images/1_4_0.png) 保存和编译该类,并在Interoperability菜单中加载该Production。 ![image](/sites/default/files/inline/images/1_5_0.png) 其中已部署了两个服务: MsgBankService:该服务通过TCP连接从其他Production接收消息 注意该Service默认使用9192端口与其他客户端通信。 ![image](/sites/default/files/inline/images/1_6_0.png) MonitorService:该服务收集其他实例的其他Production的运行状态 此时,这个Production已经具备了从其他实例的Production收集消息和事件信息的能力,可直接启动。当然,我们还需要配置与客户端的连接。 ### 为客户端Production添加消息转发Operation 假设我们已经有一个可运行的的如下所示的Production ![image](/sites/default/files/inline/images/1_7_0.png) 注意这个Production与Message Bank不在同一个实例上。 ![image](/sites/default/files/inline/images/1_8_0.png) 这个Production接收XML格式的报文并根据报文类型转发到不同的BO。 要将这个Production加入Message Bank,则需要对该客户端Production添加Business Operation Ens.Enterprise.MsgBankOperation。 ![image](/sites/default/files/inline/images/1_9_0.png) 对于该Operation,需要指定要连接的Message Bank的IP地址和端口。 ![image](/sites/default/files/inline/images/1_10_0.png) 同时,建议开启这个Operation的“启用存档”开关,保证在Message Bank临时故障时挂起消息,在故障恢复后还能捕捉到故障期间的消息和日志。 配置完成后启用该Operation。 ### 在Message Bank中加入客户端信息 上述连接建立后,客户端和Message Bank间的连接已建立,还需要配置Message Bank和客户端Production之间的程序信息(相当于注册)才能正常工作。 #### 添加客户端连接凭据 ![image](/sites/default/files/inline/images/1_11_0.png) Message Bank需要通过Web请求访问客户端信息,因此,需要配置客户端凭据,即可通过管理门户访问客户端Production的用户名和密码(对访问权限的设计和配置,可参见我们之前的文章:IRIS中的权限管理) #### 在Message Bank上配置客户端信息 在Message Bank中的Interoperability菜单中找到“企业系统”项 ![image](/sites/default/files/inline/images/1_12_0.png) 在操作页面上通过“新建连接” ![image](/sites/default/files/inline/images/1_13_0.png) 新建连接添加客户端信息。 ![image](/sites/default/files/inline/images/1_14_0.png) 注意其中的服务Web应用路径为该客户端实例上Production所在的命名空间的Web Application根路径,并引用之前填写的凭据。 如配置正确,可通过企业监视器查看连接状态 ![image](/sites/default/files/inline/images/1_15_0.png) 连接成功的状态如下: ![image](/sites/default/files/inline/images/1_16_0.png) #### 在客户端上添加Message Bank连接信息(可选步骤) 如果需要在客户端上通过链接查看消息仓库的信息,则可以配置链接。 在客户端上,在被采集的Production所在的命名空间的Interoerability菜单中“消息仓库链接”配置 ![image](/sites/default/files/inline/images/1_17_0.png) 输入Message Bank所在的IP、端口和Production所在的命名空间,保存并“开始”即可跳转到Messsage Bank的企业监视器。 ![image](/sites/default/files/inline/images/1_18_0.png) 需要注意的是,该配置固定采用了/csp/[namespace]为Message Bank的Web Application路径,而在Message Bank实例上,这个Web Application默认的路径是/csp/healthshare/messagestorage。可通过在Message Bank上添加一个Web Application,拷贝/csp/healthshare/messagestorage的配置。 ![image](/sites/default/files/inline/images/1_19_0.png) ## 四. Message Bank的实施效果 ### 测试消息 在客户端的Production中触发任意流程产生消息,如下所示: ![image](/sites/default/files/inline/images/1_20_0.png) 此时通过Message Bank中的“消息仓库查看器”即可查询存储在消息仓库中的消息 ![image](/sites/default/files/inline/images/1_21_0.png) 如下: ![image](/sites/default/files/inline/images/1_22_0.png) 可以注意到该消息已被同步到消息仓库。 需要注意,使用“消息仓库查看器”时,查询的是在Message Bank中存储的消息数据,使用在Message Bank上定义的Search Table或索引进行查询;如果通过“企业消息查看器”查询,则是链接到客户端的消息查看器查询,应用的是在客户端上定义的索引。 ### 消息的存储 根据在源系统的消息类型的不同,传递到Message Bank后会以不同的形式保存消息。 #### 虚拟文档 对于HL7 V2等标准消息或基于XML虚拟文档的消息,在Message Bank这一侧也同样以虚拟文档的形式保存。 ![image](/sites/default/files/inline/images/1_23_0.png) 特别注意其中的如下属性: MessageBodyClassName:该类型为消息在Message Bank侧持久化的类型。 ClientBodyClassName:该类型为消息在客户端侧持久化的类型。 在本例中可以看到,客户端通过EnsLib.EDI.XML.Document类型传递的消息,在Message Bank中也是通过EnsLib.EDI.XML.Document保存。 MessageBodyId:消息在Message Bank中的物理主键 ClientBodyId:客户端侧持久化消息的物理主键 ClientSessionId:客户端会话Id #### 结构化消息 对于基于Ens.Request等持久化类型的消息,在Message Bank这一侧则默认使用字符流来保存。 例如,对于如下的客户端结构化消息传输 ![image](/sites/default/files/inline/images/1_24_0.png) 在Message Bank中的保存形式为: ![image](/sites/default/files/inline/images/1_25_0.png) 可见: MessageBodyClassName:消息在Message Bank中以%Stream.GlobalCharacter即字符流进行保存 因此,无论是保存为EnsLib.EDI.XML.Document或是%Stream.GlobalCharacter,在Message Bank中保存的消息本身都缺乏足够的结构化特征和索引以支持对消息体的检索,我们会在下一篇教程《[互操作消息统一管理系列:SearchTable加速检索](https://cn.community.intersystems.com/post/%E4%BA%92%E6%93%8D%E4%BD%9C%E6%B6%88%E6%81%AF%E7%BB%9F%E4%B8%80%E7%AE%A1%E7%90%86%E7%B3%BB%E5%88%97%EF%BC%9Asearchtable%E5%8A%A0%E9%80%9F%E6%A3%80%E7%B4%A2)》中介绍如何通过构建Search Table来检索这些消息。 对于Message Bank相关的内容,可参见: https://docs.intersystems.com/healthconnect20201/csp/docbook/DocBook.UI.Page.cls?KEY=EGDV_message_bank 也欢迎与我们联系获得更详细的信息。
文章
Johnny Wang · 十二月 19, 2021

我们和Oracle、AWS、SAP数据库做了开源速度对比测试

如果您经常阅读我们的博客,您可能记得去年夏天我们进行了 InterSystems 速度测试,该测试由 ESG 验证,旨在测试数据库可以同时摄取和查询的数据量,以及这表现出的具体的影响。从那以后,我们 GitHub 页面的许多访问者一直在根据自己的想法重复验证这个测试 最初,第一次数据库速度测试将 InterSystems IRIS 数据平台与来自许多不同云和数据管理供应商的流行数据库进行了比较。令人兴奋的是,我们现在可以宣布,我们已经将数据库性能测试从 SAP HANA、AWS Aurora MySQL、SAP Sybase ASE 和 AWS RDS SQL Server 扩展到包括 PostgreSQL、MariaDB 和 Oracle Enterprise,所有这些都跑在 Amazon Web Services (AWS )上面。 我们为什么要做这一次开源速度测试? 通过模拟不支持任何特定产品的多工作负载用例,速度测试往往充当了另一种数据库性能测试工具,可以根据测试结果来确定哪个数据库或数据平台最适合业务需求来做出决策。随着许多公司继续进行数字化转型并探索传统技术的替代品,这种能力被证明是至关重要的。 数据库速度测试最大的特点就是您可以轻松地在云端或您的机器上自己体验它,并且由于我们已将测试作为可定制的开源代码发布,它可以扩展到您自己的数据和查询。 许多人将其视为微服务的首选平台,开源速度测试现在也可在 Kubernetes 上运行,以响应该应用程序在 InterSystems 开发人员社区中越来越受欢迎的现状。这意味着除了能够使用 AWS 和 Amazon Elastic Kubernetes Service (EKS) 之外,开发人员还可以了解 InterSystems IRIS 如何在 Kubernetes 集群上执行并利用 InterSystems Kubernetes Operator。 InterSystems IRIS 如何与竞争对手抗衡? 数据库速度测试着眼于同步摄取和查询性能的特定用例,这是医疗、金融、供应链和制造等许多行业实时用例的基本要求。 最新速度测试的结果发现,与 AWS MariaDB 相比,InterSystems IRIS 在测试期间摄取的记录多 620.9%,到最后摄取它们的速度提高了 717.3%。将 InterSystems IRIS 与在公共云上运行的 Sybase ASE 的性能进行比较发现,InterSystems IRIS 在测试期间摄取的记录多 4862.8%,在测试结束时摄取记录的速度提高了 6733.4%。 对于任何希望对其基础架构进行现代化改造以提高实时和接近实时的数据库性能的公司,这些指标都极为重要。此外,对于那些在 SAP Sybase ASE 上运行生产应用程序的公司,InterSystems 对 Transact-SQL 的本机支持允许这些应用程序的无缝迁移,通常不需要重写自定义代码。 不要只相信我们的话,请访问我们的 GitHub 页面,亲自对 InterSystems IRIS 进行测试,或在此处了解有关速度测试如何工作的更多信息。 阅读更多关于 Amir Samary 的 InterSystems IRIS 数据平台速度测试的博客文章 阅读更多关于去年夏天我们进行的 InterSystems 速度测试 关于作者:Amir Samary Amir Samary 已在数据库、互操作性和 InterSystems 技术方面工作了 20 多年。 Amir 致力于为使用 InterSystems 技术为阿根廷、巴西、智利、哥伦比亚、乌拉圭和美国的各个行业构建解决方案的合作伙伴、客户和开发人员提供支持。 这使 Amir 能够理解和试验不同技术、文化和基础设施现实中的模式和架构。 Amir Samary 目前担任解决方案架构经理,他领导 InterSystems 的一组解决方案开发人员。 他主修计算机科学,辅修数学。 查看原文 阅读更多关于 Amir Samary 的 InterSystems IRIS 数据平台速度测试的博客文章 阅读更多关于去年夏天我们进行的 InterSystems 速度测试 这两个还有文中的其他链接可以换成中文链接(如果有)吗?谢谢! 关于:阅读更多关于去年夏天我们进行的 InterSystems 速度测试,请查看链接:https://cn.community.intersystems.com/post/%E6%B4%9E%E5%AF%9F%E6%96%B0%E7%9A%84-intersystems-%E9%80%9F%E5%BA%A6%E6%B5%8B%E8%AF%95 谢谢! 直接在原文里改吧
文章
Hao Ma · 五月 26, 2023

IRIS镜像配置(4)_配置后的步骤

题外话:我刚刚翻译了InterSystems专家Bob Binstock的[Caché Mirroring 101:简要指南和常见问题解答](https://cn.community.intersystems.com/post/cach%C3%A9-mirroring-101%EF%BC%9A%E7%AE%80%E8%A6%81%E6%8C%87%E5%8D%97%E5%92%8C%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E8%A7%A3%E7%AD%94)。 尽管题目是Caché Mirror 101, 而且是写于2016年,但因为讲解的都是Mirror的基本原理,所以在大量使用IRIS的今天也完全适用。 前面的3篇文章,包括了配置Mirror的各个方面。如果您照着操作,现在已经有了一个工作的mirror环境,并加入了您的数据库。然而,还没完,这篇我来讨论一下后面的工作,首先的问题是: **Mirror不复制什么** 简单说,Caché/IRIS镜像是**数据库复制(Database Replication)**。在Caché/IRIS里什么是数据库?也就是**Cache.dat和iris.dat**文件。数据库的修改日志,也就是journal,从主机被传送到其他镜像成员。而除此之外的内容,需要维护人员来分别的个个处理, 解决这些内容在各个镜像成员间的拷贝。需要很多的计划和细心。 >系统数据库, 包括IRISSYS, IRISTEMP, IRISLIB等等, 这些Caché/IRIS本身的数据库不应该被加入Mirror,在大多数Caché/IRIS版本里也都设置成不可以加入入MIRROR。 > >例外的HealthCare产品, HSSYS需要做Mirror, HSCustom可以做Mirror, 而HSLIB不可以Mirror 我们可以把问题转换成下面的题目: ## 需要人工在镜像成员中同步的项目 ### 命名空间(namespace)和Mapping 命名空间是应用开发的概念,它使用数据库。命名空间定义了3种映射关系:Package Mapping, Routing Mapping, Global Mapping。这样在一个命名空间可以使用多个数据库的内容。 通常情况下,用户会在主机创建命名空间的同时,创建一个新的带有mirror属性的数据库,然后会在其他mirror成员中手工一个个的创建命名空间,加入镜像的数据库。之后,管理员无需考虑更多的操作。 然而,对命名空间的修改,比如要添加或者删除命名空间的某些mapping,这偶尔会需要,尤其是应用迭代和系统扩容的情况下,那么,管理员/实施人员,必须清楚Mirror无法同步这个修改,您必须手工同步修改到其他机器去。 如果配置的mapping比较多, 我建议使用Manifest来操作。Mainfest是一个xml的文本,用来安装或者修改Caché/IRIS的配置,你可以参考[在线文档: Using a Manifest](https://docs.intersystems.com/iris20231/csp/docbook/DocBook.UI.Page.cls?KEY=GCI_manifest), 或者社区文章[使用Manifest](https://cn.community.intersystems.com/post/%E4%BD%BF%E7%94%A8manifest)。 这里给一个配置mapping的例子: ```xml ``` 如果是资深的Caché维护工程师,懂得如果修改CPF文件并在不重启实例的情况下应用修改后的内容,可以考虑把主机上的CPF中的mapping部分复制粘贴到其他机器。如果您没有这方面的经验,我不建议这种方式。 另外,在IRIS 2022后的版本中有了一个新工具,Configuration Merge。 文档在[这里](https://docs.intersystems.com/iris20231/csp/docbook/Doc.View.cls?KEY=ACMF)。可惜只有最新版的IRIS或者Health Connect 用户有的用。 ### 数据库的修改 数据库的内容会通过Journal从主机同步到其他成员,但修改不会,一般会遇到的是**压缩和截断**。 由于某种错误操作,某个数据库,会扩展到不正常的大,而当错误修正后,用户可能需要对该数据库进行压缩和截断,以释放被错误占用的空闲的磁盘空间。 由于除主机外,其他镜像成员的数据库都是只读的,这个操作的顺序应该是这样: 1. 在主机A执行压缩和截断 2. 切换到备机B, 再次执行压缩和截断。 3. 异步成员DR。 一种方案是吧DR提升到备机。这时当前的备机A会将为灾备,然后再切换DR为主机,再进行压缩和截断。 还有一个选择,就是重新配置DR上的这个数据库,这需要从主机到DR的数据库备份和恢复。 ### IRIS实例的配置 从最常用的内存的配置,Service的配置, **用户,权限,资源**的配置等等。它们都不会被MIRROR同步。如果您在MIRROR主机里做了修改了缩表的大小,或者启动了一个,比如TELNET服务, 您需要人工在其他机器上做相同操作。 像上面的mapping配置一样,这里还是建议使用Manifest人工同步IRIS得修改。注意的是,Mainfest不保证能支持所有的配置。比如在Caché的版本下, 比如您在主机上启动了TELNET服务, Manifest没有相应的标签。这种情况下, 如果您熟悉ObjectScript语言,可以把ObjectScript实现加入执行Manifest的方法,比如说: ```java ClassMethod main(){ //执行Manifest修改命名空间 Set pVars("Namespace")="MYNAMESPACE" $$$ThrowOnError(..ModifyNamespace(.pVars)) //启动IRIS的TELNET服务 set properties("Enabled")=1 // 有効 set sts=##class(Security.Services).Modify("%Service_Telnet",.properties) } ``` 当然,如果您缺乏开发实施的知识,在用户界面上一个个机器的操作是最省心的办法。 问题是,打开一个服务,修改一个配置参数操作都很简单,但是如果要添加大量的用户和权限怎么办? 用Manifest管理是一个办法。但根本上,如果您经常有大量的用户管理的工作,其实使用Kerberos或者LDAP管理用户身份认证和授权的工作, 在有多个镜像成员的情况下,尤其的合适。 关于这部分内容,请参考[在线文档:Authentication and Authorization](https://docs.intersystems.com/iris20231/csp/docbook/DocBook.UI.Page.cls?KEY=PAGE_security_authentication_authorization) ### 定时任务(TASK) 在主机上创建的定时任务, 您需要人工在其他机器上做相同操作。这里有2个步骤: 1. 在主机上创建新任务的时候,要选择”**应如何为镜像运行任务**“。 这是个下拉菜单,选项有*”仅在主镜像成员上运行“,“仅在非主镜像成员上运行“ ,“在任何镜像成员上运行"。* 选择的出发点是:非主镜像成员的数据库是只读的。因此,比如一个Ensemble的镜像配置中, 删除Ensemble消息的定时任务, 一定是”仅在主镜像成员上运行“。 2. 把新的定时任务从主机同步到其他成员。 ​ 如果是一个或者少量几个TASK, 那么手工在其他各个镜像成员上添加是最简单直接的做法。而如果是有很长 的任务列表,尤其在配置Mirror得时候可以需要同步一个长长的列表时, 您可以考虑**从主机导出Task到其 他机器导入**,我只知道使用ObjectScript命令的方法, 使用`%SYS.Task.ExportTask()`和 `%SYS.Task.ImportTasks()`。 文档在[这里](https://docs.intersystems.com/iris20231/csp/documatic/%25CSP.Documatic.cls?LIBRARY=%25SYS&CLASSNAME=%25SYS.Task)。 ### Web Application 主机上配置的Web Applicaiton 也要同步到其他镜像成员。如果要同步的Web Application比较多,推荐的方式依然是Manifest, 下面是一个例子。 ```xml ``` 麻烦的是不同的版本Caché/IRIS使用的标签上会略有不同,要稍微仔细的查看一下您的版本的文档。 如果您对ZPM, 现在称为IPM熟悉的话, 用ZPM做同步也是个好选择。关于zpm, 您可以参考这个帖子[zpm介绍](https://cn.community.intersystems.com/post/zpm%E4%BB%8B%E7%BB%8D1)。提醒一下的是,程序因为是存在数据库里面的,如果该数据库是被镜像的,您其实不需要用ZPM把程序代码拷贝到其他镜像成员。 ### Gateway 一般用到的有**SQL Gateway**和**External Language Gateway**,它们分别用于连接其他的数据库和使用其他语音的代码包。 SQL Gateway 记录保存在%SYS命名空间的*%Library.sys_SQLConnection*数据表里。简单的方法是使用工具把表记录导入导出。 External Language Gateway(外部语言网关) 新版的IRIS系统内嵌了外部语言服务器,包括%Python Server, %Java Server, %Dotnet Server等。如果您使用的是默认配置,各个镜像成员是一致的,无需操心。如果只是IP端口的修改,手工同步一下也很容易,毕竟工作量有限,只是您需要清楚的记得,这个也是不被Mirror自动同步的。 ### 文件 我把文件分为两类, 一类是“固定文件”,包括一下几个部分, - CSP文件,js文件,css文件,html文件等 - XSLT文件 - 其他语言的程序代码,Java文件,python文件, .Net文件 这类文件上传到主机的时候, 也必须上传到其他镜像成员,这是个简单的操作,别忘了就行。 麻烦的是**流文件**。在ObjectScript里如果使用了%Stream.FileBinary, %Stream.FileCharacter等类,那么数据不是保存到Cache.Dat或者IRIS.data, 而是保存在和.Dat同目录的一个stream的子目录下,而这个目录是不会被镜像同步的。 而且,因为这是实时数据,你也不可能手工的把它拷来拷去。 如果您的应用里用到了文件流,我任务您需要一个文件服务器保证流文件在各个各个镜像成员间的同步。 ### Ensemble Production Consideration 对于Ensemble和Health Connect用户,您需要阅读这部分在线文档: [Production Considerations for Mirroring](https://docs.intersystems.com/iris20223/csp/docbook/DocBook.UI.Page.cls?KEY=GHA_mirror_manage#GHA_mirror_set_ensemble) , 简单总结一下: - 创建的带有ensemble或者Inteoprability的命名空间,数据库要创建为Mirror的数据库。 - **"production是否自动启动“**应该在主机和备机上,甚至DR上都配置为“自动启动”。 在Mirror配置下的Production会先检查这个实例是不是主机,如果不是,“自动启动”的配置也不会生效,这样保证了Production只在主机上运行,而切换后也不需要人工干预。 上面的这些并不是完整的内容,尽管在大多少情况下这些内容差不多够了。如果您想要确保Mirror的主机的工作内容完全同步到了备机和DR, 请仔细阅读在线文档的这一部分:[Mirror Configuration Guidelines](https://docs.intersystems.com/iris20223/csp/docbook/DocBook.UI.Page.cls?KEY=GHA_mirror_set_config#GHA_mirror_set_config_guidelines) 另外,对于各种需要人工同步的内容的操作,还建议阅读[在线文档:Server Migration](https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=AMIG#AMIG_migration_external)。 如果是最新的IRIS用户,请参考[在线文档:Deploy Mirrors Using Configuration Merge](https://docs.intersystems.com/iris20223/csp/docbook/DocBook.UI.Page.cls?KEY=GHA_mirror_set_config#GHA_mirror_set_config_auto_merge)