搜索​​​​

清除过滤器
文章
Jingwei Wang · 九月 28, 2021

IRIS 2021 技术文档 First Look 16 Java 网关(Gateway)和InterSystems 产品

本文档介绍了如何在 InterSystems IRIS®数据平台中使用 Java 网关(Gateway)与 Java 组件进行互操作。在本文中,您将首先创建一个示例 JAR 文件。然后,您将在 InterSystems IRIS 管理门户(Management Portal)中创建并启动 Java 网关(Gateway)。您将以编程方式导入创建的示例 JAR 文件。最后,您将在 ObjectScript 中创建并操作一些代理类(proxy class)和对象(object)。 为了让您体验 Java 网关(Gateway ),而又不陷入细节困境,我们保持了简单的探索。这些活动被设计成只使用默认设置和功能,这样您就可以熟悉功能的基本原理,而不必处理那些离题或过于复杂的细节。当您把 Java 网关(Gateway)引入您的生产系统时,您可能需要做一些不同的事情。请确保不要把这种对 Java 网关(Gateway )的探索与真实的情况相混淆!本文档末尾提供的参考资料将使您对在生产中使用 Java 网关(Gateway)的情况有一个很好的了解。 要浏览所有的技术概要(First Look),包括下面描述的可以在免费的社区版(Community Edition)实例上执行的那些,请参见 InterSystems 技术概要。 为什么 Java 网关(Gateway)很重要 Java 网关(Gateway)为 InterSystems IRIS 与 Java 组件进行互操作提供了一种简单的方法。在使用 Java 网关(Gateway)导入 JAR 文件后,您可以实例化一个外部 Java 对象,并将其作为 InterSystems IRIS 中的本机对象进行操作。您可以从 InterSystems IRIS 和 ObjectScript 内部访问 Java 类和方法! 在内部,Java 网关(Gateway)为每个 Java 类生成一对一的 ObjectScript 代理。通常,使用 Java 网关(Gateway)的最佳方法是构建一个小型 Java 封装类(wrapper class),它只公开您想要的功能,然后为这个封装创建一个代理。 探索 Java 网关(Gateway) 我们开发了一个简短的演示,向您展示如何使用 Java 网关(Gateway)和 InterSystems IRIS。想试试InterSystems IRIS Java 开发和互操作性功能的在线视频演示吗?请查看 Java QuickStart(Java 快速入门)! 用前须知 要使用该程序,您需要在一个系统上工作,应安装 JDK 1.8 版本和您选择的 Java IDE,并连接一个正在运行的 InterSystems IRIS 实例。您对 InterSystems IRIS 的选择包括多种已授权的和免费的评估实例;实例不需要由您正在工作的系统托管(尽管它们必须相互具有网络访问权限)。关于如何部署每种类型的实例的信息(如果您还没有可使用的实例),请参见InterSystems IRIS 基础:连接一个 IDE中的部署 InterSystems IRIS。使用同一文档中的InterSystems IRIS 连接信息和 Java IDE中的信息,将 IDE 连接到您的 InterSystems IRIS 实例。 创建 JAR 文件 使用您选择的 IDE 在 Java 中创建 Person 类。使用以下示例代码: public class Person { public int age; public String name; //constructor public Person (int startAge, String Name) { age = startAge; name = Name; } public void setAge(int newAge) { age = newAge; } public String getName() { return name; } public int getAge() { return age; } public static void main(String []args) { Person myPerson = new Person (5, "Tom"); System.out.println(myPerson.getName()); System.out.println(myPerson.getAge()); }} 编译 Person 类。然后,打开命令提示符(Command Prompt)窗口,并导航到包含 Person.class 文件的目录。使用下面的命令来生成 Person.jar 文件: jar cf Person.jar Person.class 注意文件的位置。 启动 Java 网关(Gateway) 要启动 Java 网关(Gateway),请按照以下步骤操作: 使用InterSystems IRIS 基础:连接一个 IDE中为您的实例描述的 URL,在浏览器中打开您的实例的管理门户(Management Portal)。 导航到 Object Gateways(对象网关)页面。(System Administration(系统管理) > Configuration(配置) > Connectivity(连接) > External Language Servers 选择 Create New External Language Servers 下拉菜单选择 Java 。 服务器名称输入 JavaGatewayTest,端口输入 55555。 点击 Save(保存)。 在新的 Java 网关(Gateway)所在行中,点击 Start(开始)。 生成代理类(Proxy Classes) 在 USER 命名空间(或您在已安装的实例)中创建一个名为 CreateProxyClasses.cls 的新 ObjectScript 类,包名为 JavaGatewayTest。粘贴以下代码,用您的 InterSystems IRIS 实例的连接信息 (如InterSystems IRIS 基础:连接一个 IDE中所述)替换 gwyConn.&Connect 的参数。您可以指定所示的 USER 命名空间,也可以选择实例上创建的另一个命名空间。用双引号括起来的 Person.jar 文件的完整文件路径替换 YOUR FILEPATH HERE。 Class JavaGatewayTest.CreateProxyClasses Extends %Persistent { ClassMethod run() { // get a connection to the Java Gateway set gwyConn = ##class(%Net.Remote.Gateway).%New() set status = gwyConn.%Connect("127.0.0.1", 55555, "USER") if $$$ISERR(status) { write !,"error: "_$system.OBJ.DisplayError(status) quit } // add the jar to the classpath set classpath = ##class(%ListOfDataTypes).%New() do classpath.Insert("YOUR FILEPATH HERE") set status = gwyConn.%AddToCurrentClassPath(classpath) if $$$ISERR(status) { write !,"error: "_$system.OBJ.DisplayError(status) quit } // create the proxy ObjectScript classes that correspond to the Java classes in the jar file set status = gwyConn.%Import("Person",,,,1) if $$$ISERR(status) { write !,"error: "_$system.OBJ.DisplayError(status) quit } // close the connection to the Java gateway set status = gwyConn.%Disconnect() if $$$ISERR(status) { write !,"error: "_$system.OBJ.DisplayError(status) quit }}} 编译并构建该类。 打开 InterSystems 终端(Terminal),并导航到您指定的命名空间。使用以下命令执行 run() 方法: do ##class(JavaGatewayTest.ManipulateObjects).run() 您应该可以看到以下输出: Name: Tom Age: 5 setting age to 100 Age: 100 现在您已经成功地完成了练习,停止您创建的 Java 网关(Gateway)。返回到管理门户(Management Portal)中的 External Language Servers页面,找到您的 JavaGatewayTest 网关,并选择 Stop(停止)。 了解有关 Java 网关(Gateway)的更多信息 要了解有关 Java 网关(Gateway)和 InterSystems IRIS 的更多信息,请参见: 使用 Java 网关(Gateway) 有个疑问,生成代理类(Proxy Classes)该步骤中是不是应该体现为JavaGateway而不是Net.Remote.Gateway。 %Net.Remote.Gateway 是 %Net.Remote.Java.JavaGateway的基类,是用来建立JavaGateway的连接使用的。
文章
jieliang liu · 九月 23, 2021

IRIS 2021 技术文档 First Look 19 使用 InterSystems 产品优化 SQL 性能

html {overflow-x: initial !important;}:root { --bg-color: #ffffff; --text-color: #333333; --select-text-bg-color: #B5D6FC; --select-text-font-color: auto; --monospace: "Lucida Console",Consolas,"Courier",monospace; --title-bar-height: 20px; } .mac-os-11 { --title-bar-height: 28px; } html { font-size: 14px; background-color: var(--bg-color); color: var(--text-color); font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; } body { margin: 0px; padding: 0px; height: auto; inset: 0px; font-size: 1rem; line-height: 1.42857143; overflow-x: hidden; background-image: inherit; background-size: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: inherit; tab-size: 4; background-position: inherit; background-repeat: inherit; } iframe { margin: auto; } a.url { word-break: break-all; } a:active, a:hover { outline: 0px; } .in-text-selection, ::selection { text-shadow: none; background: var(--select-text-bg-color); color: var(--select-text-font-color); } #write { margin: 0px auto; height: auto; width: inherit; word-break: normal; word-wrap: break-word; position: relative; white-space: normal; overflow-x: visible; padding-top: 36px; } #write.first-line-indent p { text-indent: 2em; } #write.first-line-indent li p, #write.first-line-indent p * { text-indent: 0px; } #write.first-line-indent li { margin-left: 2em; } .for-image #write { padding-left: 8px; padding-right: 8px; } body.typora-export { padding-left: 30px; padding-right: 30px; } .typora-export .footnote-line, .typora-export li, .typora-export p { white-space: pre-wrap; } .typora-export .task-list-item input { pointer-events: none; } @media screen and (max-width: 500px) { body.typora-export { padding-left: 0px; padding-right: 0px; } #write { padding-left: 20px; padding-right: 20px; } .CodeMirror-sizer { margin-left: 0px !important; } .CodeMirror-gutters { display: none !important; } } #write li > figure:last-child { margin-bottom: 0.5rem; } #write ol, #write ul { position: relative; } img { max-width: 100%; vertical-align: middle; image-orientation: from-image; } button, input, select, textarea { color: inherit; font-family: inherit; font-size: inherit; font-style: inherit; font-variant-caps: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; } input[type="checkbox"], input[type="radio"] { line-height: normal; padding: 0px; } *, ::after, ::before { box-sizing: border-box; } #write h1, #write h2, #write h3, #write h4, #write h5, #write h6, #write p, #write pre { width: inherit; } #write h1, #write h2, #write h3, #write h4, #write h5, #write h6, #write p { position: relative; } p { line-height: inherit; } h1, h2, h3, h4, h5, h6 { break-after: avoid-page; break-inside: avoid; orphans: 4; } p { orphans: 4; } h1 { font-size: 2rem; } h2 { font-size: 1.8rem; } h3 { font-size: 1.6rem; } h4 { font-size: 1.4rem; } h5 { font-size: 1.2rem; } h6 { font-size: 1rem; } .md-math-block, .md-rawblock, h1, h2, h3, h4, h5, h6, p { margin-top: 1rem; margin-bottom: 1rem; } .hidden { display: none; } .md-blockmeta { color: rgb(204, 204, 204); font-weight: 700; font-style: italic; } a { cursor: pointer; } sup.md-footnote { padding: 2px 4px; background-color: rgba(238, 238, 238, 0.7); color: rgb(85, 85, 85); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; cursor: pointer; } sup.md-footnote a, sup.md-footnote a:hover { color: inherit; text-transform: inherit; text-decoration: inherit; } #write input[type="checkbox"] { cursor: pointer; width: inherit; height: inherit; } figure { overflow-x: auto; margin: 1.2em 0px; max-width: calc(100% + 16px); padding: 0px; } figure > table { margin: 0px; } tr { break-inside: avoid; break-after: auto; } thead { display: table-header-group; } table { border-collapse: collapse; border-spacing: 0px; width: 100%; overflow: auto; break-inside: auto; text-align: left; } table.md-table td { min-width: 32px; } .CodeMirror-gutters { border-right-width: 0px; background-color: inherit; } .CodeMirror-linenumber { } .CodeMirror { text-align: left; } .CodeMirror-placeholder { opacity: 0.3; } .CodeMirror pre { padding: 0px 4px; } .CodeMirror-lines { padding: 0px; } div.hr:focus { cursor: none; } #write pre { white-space: pre-wrap; } #write.fences-no-line-wrapping pre { white-space: pre; } #write pre.ty-contain-cm { white-space: normal; } .CodeMirror-gutters { margin-right: 4px; } .md-fences { font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; overflow: visible; white-space: pre; background-image: inherit; background-size: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: inherit; position: relative !important; background-position: inherit; background-repeat: inherit; } .md-fences-adv-panel { width: 100%; margin-top: 10px; text-align: center; padding-top: 0px; padding-bottom: 8px; overflow-x: auto; } #write .md-fences.mock-cm { white-space: pre-wrap; } .md-fences.md-fences-with-lineno { padding-left: 0px; } #write.fences-no-line-wrapping .md-fences.mock-cm { white-space: pre; overflow-x: auto; } .md-fences.mock-cm.md-fences-with-lineno { padding-left: 8px; } .CodeMirror-line, twitterwidget { break-inside: avoid; } .footnotes { opacity: 0.8; font-size: 0.9rem; margin-top: 1em; margin-bottom: 1em; } .footnotes + .footnotes { margin-top: 0px; } .md-reset { margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: top; text-decoration: none; text-shadow: none; float: none; position: static; width: auto; height: auto; white-space: nowrap; cursor: inherit; line-height: normal; font-weight: 400; text-align: left; box-sizing: content-box; direction: ltr; background-position: 0px 0px; } li div { padding-top: 0px; } blockquote { margin: 1rem 0px; } li .mathjax-block, li p { margin: 0.5rem 0px; } li blockquote { margin: 1rem 0px; } li { margin: 0px; position: relative; } blockquote > :last-child { margin-bottom: 0px; } blockquote > :first-child, li > :first-child { margin-top: 0px; } .footnotes-area { color: rgb(136, 136, 136); margin-top: 0.714rem; padding-bottom: 0.143rem; white-space: normal; } #write .footnote-line { white-space: pre-wrap; } @media print { body, html { border: 1px solid transparent; height: 99%; break-after: avoid; break-before: avoid; font-variant-ligatures: no-common-ligatures; } #write { margin-top: 0px; padding-top: 0px; border-color: transparent !important; } .typora-export * { -webkit-print-color-adjust: exact; } .typora-export #write { break-after: avoid; } .typora-export #write::after { height: 0px; } .is-mac table { break-inside: avoid; } .typora-export-show-outline .typora-export-sidebar { display: none; } } .footnote-line { margin-top: 0.714em; font-size: 0.7em; } a img, img a { cursor: pointer; } pre.md-meta-block { font-size: 0.8rem; min-height: 0.8rem; white-space: pre-wrap; background-color: rgb(204, 204, 204); display: block; overflow-x: hidden; } p > .md-image:only-child:not(.md-img-error) img, p > img:only-child { display: block; margin: auto; } #write.first-line-indent p > .md-image:only-child:not(.md-img-error) img { left: -2em; position: relative; } p > .md-image:only-child { display: inline-block; width: 100%; } #write .MathJax_Display { margin: 0.8em 0px 0px; } .md-math-block { width: 100%; } .md-math-block:not(:empty)::after { display: none; } .MathJax_ref { fill: currentcolor; } [contenteditable="true"]:active, [contenteditable="true"]:focus, [contenteditable="false"]:active, [contenteditable="false"]:focus { outline: 0px; box-shadow: none; } .md-task-list-item { position: relative; list-style-type: none; } .task-list-item.md-task-list-item { padding-left: 0px; } .md-task-list-item > input { position: absolute; top: 0px; left: 0px; margin-left: -1.2em; margin-top: calc(1em - 10px); border: none; } .math { font-size: 1rem; } .md-toc { min-height: 3.58rem; position: relative; font-size: 0.9rem; border-top-left-radius: 10px; border-top-right-radius: 10px; border-bottom-right-radius: 10px; border-bottom-left-radius: 10px; } .md-toc-content { position: relative; margin-left: 0px; } .md-toc-content::after, .md-toc::after { display: none; } .md-toc-item { display: block; color: rgb(65, 131, 196); } .md-toc-item a { text-decoration: none; } .md-toc-inner:hover { text-decoration: underline; } .md-toc-inner { display: inline-block; cursor: pointer; } .md-toc-h1 .md-toc-inner { margin-left: 0px; font-weight: 700; } .md-toc-h2 .md-toc-inner { margin-left: 2em; } .md-toc-h3 .md-toc-inner { margin-left: 4em; } .md-toc-h4 .md-toc-inner { margin-left: 6em; } .md-toc-h5 .md-toc-inner { margin-left: 8em; } .md-toc-h6 .md-toc-inner { margin-left: 10em; } @media screen and (max-width: 48em) { .md-toc-h3 .md-toc-inner { margin-left: 3.5em; } .md-toc-h4 .md-toc-inner { margin-left: 5em; } .md-toc-h5 .md-toc-inner { margin-left: 6.5em; } .md-toc-h6 .md-toc-inner { margin-left: 8em; } } a.md-toc-inner { font-size: inherit; font-style: inherit; font-weight: inherit; line-height: inherit; } .footnote-line a:not(.reversefootnote) { color: inherit; } .md-attr { display: none; } .md-fn-count::after { content: "."; } code, pre, samp, tt { font-family: var(--monospace); } kbd { margin: 0px 0.1em; padding: 0.1em 0.6em; font-size: 0.8em; color: rgb(36, 39, 41); background-color: rgb(255, 255, 255); border: 1px solid rgb(173, 179, 185); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; box-shadow: rgba(12, 13, 14, 0.2) 0px 1px 0px, rgb(255, 255, 255) 0px 0px 0px 2px inset; white-space: nowrap; vertical-align: middle; } .md-comment { color: rgb(162, 127, 3); opacity: 0.8; font-family: var(--monospace); } code { text-align: left; } a.md-print-anchor { white-space: pre !important; border: none !important; display: inline-block !important; position: absolute !important; width: 1px !important; right: 0px !important; outline: 0px !important; text-shadow: initial !important; background-position: 0px 0px !important; } .os-windows.monocolor-emoji .md-emoji { font-family: "Segoe UI Symbol", sans-serif; } .md-diagram-panel > svg { max-width: 100%; } [lang="flow"] svg, [lang="mermaid"] svg { max-width: 100%; height: auto; } [lang="mermaid"] .node text { font-size: 1rem; } table tr th { border-bottom-width: 0px; } video { max-width: 100%; display: block; margin: 0px auto; } iframe { max-width: 100%; width: 100%; border: none; } .highlight td, .highlight tr { border: 0px; } mark { background-color: rgb(255, 255, 0); color: rgb(0, 0, 0); } .md-html-inline .md-plain, .md-html-inline strong, mark .md-inline-math, mark strong { color: inherit; } .md-expand mark .md-meta { opacity: 0.3 !important; } mark .md-meta { color: rgb(0, 0, 0); } @media print { .typora-export h1, .typora-export h2, .typora-export h3, .typora-export h4, .typora-export h5, .typora-export h6 { break-inside: avoid; } } .md-diagram-panel .messageText { stroke: none !important; } .md-diagram-panel .start-state { fill: var(--node-fill); } .md-diagram-panel .edgeLabel rect { opacity: 1 !important; } .md-require-zoom-fix { height: auto; margin-top: 16px; margin-bottom: 16px; } .md-require-zoom-fix foreignObject { font-size: var(--mermaid-font-zoom); } .md-fences.md-fences-math { font-size: 1em; } .md-fences-advanced:not(.md-focus) { padding: 0px; white-space: nowrap; border: 0px; } .md-fences-advanced:not(.md-focus) { background-image: inherit; background-size: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: inherit; background-position: inherit; background-repeat: inherit; } .typora-export-show-outline .typora-export-content { max-width: 1440px; margin: auto; display: flex; flex-direction: row; } .typora-export-sidebar { width: 300px; font-size: 0.8rem; margin-top: 80px; margin-right: 18px; } .typora-export-show-outline #write { --webkit-flex: 2; flex: 2 1 0%; } .typora-export-sidebar .outline-content { position: fixed; top: 0px; max-height: 100%; overflow: hidden auto; padding-bottom: 30px; padding-top: 60px; width: 300px; } @media screen and (max-width: 1024px) { .typora-export-sidebar, .typora-export-sidebar .outline-content { width: 240px; } } @media screen and (max-width: 800px) { .typora-export-sidebar { display: none; } } .outline-content li, .outline-content ul { margin-left: 0px; margin-right: 0px; padding-left: 0px; padding-right: 0px; list-style: none; } .outline-content ul { margin-top: 0px; margin-bottom: 0px; } .outline-content strong { font-weight: 400; } .outline-expander { width: 1rem; height: 1.428571429rem; position: relative; display: table-cell; vertical-align: middle; cursor: pointer; padding-left: 4px; } .outline-expander::before { content: ''; position: relative; font-family: Ionicons; display: inline-block; font-size: 8px; vertical-align: middle; } .outline-item { padding-top: 3px; padding-bottom: 3px; cursor: pointer; } .outline-expander:hover::before { content: ''; } .outline-h1 > .outline-item { padding-left: 0px; } .outline-h2 > .outline-item { padding-left: 1em; } .outline-h3 > .outline-item { padding-left: 2em; } .outline-h4 > .outline-item { padding-left: 3em; } .outline-h5 > .outline-item { padding-left: 4em; } .outline-h6 > .outline-item { padding-left: 5em; } .outline-label { cursor: pointer; display: table-cell; vertical-align: middle; text-decoration: none; color: inherit; } .outline-label:hover { text-decoration: underline; } .outline-item:hover { border-color: rgb(245, 245, 245); background-color: var(--item-hover-bg-color); } .outline-item:hover { margin-left: -28px; margin-right: -28px; border-left-width: 28px; border-left-style: solid; border-left-color: transparent; border-right-width: 28px; border-right-style: solid; border-right-color: transparent; } .outline-item-single .outline-expander::before, .outline-item-single .outline-expander:hover::before { display: none; } .outline-item-open > .outline-item > .outline-expander::before { content: ''; } .outline-children { display: none; } .info-panel-tab-wrapper { display: none; } .outline-item-open > .outline-children { display: block; } .typora-export .outline-item { padding-top: 1px; padding-bottom: 1px; } .typora-export .outline-item:hover { margin-right: -8px; border-right-width: 8px; border-right-style: solid; border-right-color: transparent; } .typora-export .outline-expander::before { content: "+"; font-family: inherit; top: -1px; } .typora-export .outline-expander:hover::before, .typora-export .outline-item-open > .outline-item > .outline-expander::before { content: '−'; } .typora-export-collapse-outline .outline-children { display: none; } .typora-export-collapse-outline .outline-item-open > .outline-children, .typora-export-no-collapse-outline .outline-children { display: block; } .typora-export-no-collapse-outline .outline-expander::before { content: "" !important; } .typora-export-show-outline .outline-item-active > .outline-item .outline-label { font-weight: 700; } .md-inline-math-container mjx-container { zoom: 0.95; } .CodeMirror { height: auto; } .CodeMirror.cm-s-inner { background-image: inherit; background-size: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: inherit; background-position: inherit; background-repeat: inherit; } .CodeMirror-scroll { overflow: auto hidden; z-index: 3; } .CodeMirror-gutter-filler, .CodeMirror-scrollbar-filler { background-color: rgb(255, 255, 255); } .CodeMirror-gutters { border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); background-image: inherit; background-size: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: inherit; white-space: nowrap; background-position: inherit; background-repeat: inherit; } .CodeMirror-linenumber { padding: 0px 3px 0px 5px; text-align: right; color: rgb(153, 153, 153); } .cm-s-inner .cm-keyword { color: rgb(119, 0, 136); } .cm-s-inner .cm-atom, .cm-s-inner.cm-atom { color: rgb(34, 17, 153); } .cm-s-inner .cm-number { color: rgb(17, 102, 68); } .cm-s-inner .cm-def { color: rgb(0, 0, 255); } .cm-s-inner .cm-variable { color: rgb(0, 0, 0); } .cm-s-inner .cm-variable-2 { color: rgb(0, 85, 170); } .cm-s-inner .cm-variable-3 { color: rgb(0, 136, 85); } .cm-s-inner .cm-string { color: rgb(170, 17, 17); } .cm-s-inner .cm-property { color: rgb(0, 0, 0); } .cm-s-inner .cm-operator { color: rgb(152, 26, 26); } .cm-s-inner .cm-comment, .cm-s-inner.cm-comment { color: rgb(170, 85, 0); } .cm-s-inner .cm-string-2 { color: rgb(255, 85, 0); } .cm-s-inner .cm-meta { color: rgb(85, 85, 85); } .cm-s-inner .cm-qualifier { color: rgb(85, 85, 85); } .cm-s-inner .cm-builtin { color: rgb(51, 0, 170); } .cm-s-inner .cm-bracket { color: rgb(153, 153, 119); } .cm-s-inner .cm-tag { color: rgb(17, 119, 0); } .cm-s-inner .cm-attribute { color: rgb(0, 0, 204); } .cm-s-inner .cm-header, .cm-s-inner.cm-header { color: rgb(0, 0, 255); } .cm-s-inner .cm-quote, .cm-s-inner.cm-quote { color: rgb(0, 153, 0); } .cm-s-inner .cm-hr, .cm-s-inner.cm-hr { color: rgb(153, 153, 153); } .cm-s-inner .cm-link, .cm-s-inner.cm-link { color: rgb(0, 0, 204); } .cm-negative { color: rgb(221, 68, 68); } .cm-positive { color: rgb(34, 153, 34); } .cm-header, .cm-strong { font-weight: 700; } .cm-del { text-decoration: line-through; } .cm-em { font-style: italic; } .cm-link { text-decoration: underline; } .cm-error { color: red; } .cm-invalidchar { color: red; } .cm-constant { color: rgb(38, 139, 210); } .cm-defined { color: rgb(181, 137, 0); } div.CodeMirror span.CodeMirror-matchingbracket { color: rgb(0, 255, 0); } div.CodeMirror span.CodeMirror-nonmatchingbracket { color: rgb(255, 34, 34); } .cm-s-inner .CodeMirror-activeline-background { background-image: inherit; background-size: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: inherit; background-position: inherit; background-repeat: inherit; } .CodeMirror { position: relative; overflow: hidden; } .CodeMirror-scroll { height: 100%; outline: 0px; position: relative; box-sizing: content-box; background-image: inherit; background-size: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: inherit; background-position: inherit; background-repeat: inherit; } .CodeMirror-sizer { position: relative; } .CodeMirror-gutter-filler, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-vscrollbar { position: absolute; z-index: 6; display: none; outline: 0px; } .CodeMirror-vscrollbar { right: 0px; top: 0px; overflow: hidden; } .CodeMirror-hscrollbar { bottom: 0px; left: 0px; overflow: auto hidden; } .CodeMirror-scrollbar-filler { right: 0px; bottom: 0px; } .CodeMirror-gutter-filler { left: 0px; bottom: 0px; } .CodeMirror-gutters { position: absolute; left: 0px; top: 0px; padding-bottom: 10px; z-index: 3; overflow-y: hidden; } .CodeMirror-gutter { white-space: normal; height: 100%; box-sizing: content-box; padding-bottom: 30px; margin-bottom: -32px; display: inline-block; } .CodeMirror-gutter-wrapper { position: absolute; z-index: 4; border: none !important; background-position: 0px 0px !important; } .CodeMirror-gutter-background { position: absolute; top: 0px; bottom: 0px; z-index: 4; } .CodeMirror-gutter-elt { position: absolute; cursor: default; z-index: 4; } .CodeMirror-lines { cursor: text; } .CodeMirror pre { border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; border-width: 0px; font-family: inherit; font-size: inherit; margin: 0px; white-space: pre; word-wrap: normal; color: inherit; z-index: 2; position: relative; overflow: visible; background-position: 0px 0px; } .CodeMirror-wrap pre { word-wrap: break-word; white-space: pre-wrap; word-break: normal; } .CodeMirror-code pre { border-right-width: 30px; border-right-style: solid; border-right-color: transparent; width: fit-content; } .CodeMirror-wrap .CodeMirror-code pre { border-right-style: none; width: auto; } .CodeMirror-linebackground { position: absolute; inset: 0px; z-index: 0; } .CodeMirror-linewidget { position: relative; z-index: 2; overflow: auto; } .CodeMirror-wrap .CodeMirror-scroll { overflow-x: hidden; } .CodeMirror-measure { position: absolute; width: 100%; height: 0px; overflow: hidden; visibility: hidden; } .CodeMirror-measure pre { position: static; } .CodeMirror div.CodeMirror-cursor { position: absolute; visibility: hidden; border-right-style: none; width: 0px; } .CodeMirror div.CodeMirror-cursor { visibility: hidden; } .CodeMirror-focused div.CodeMirror-cursor { visibility: inherit; } .cm-searching { background-color: rgba(255, 255, 0, 0.4); } span.cm-underlined { text-decoration: underline; } span.cm-strikethrough { text-decoration: line-through; } .cm-tw-syntaxerror { color: rgb(255, 255, 255); background-color: rgb(153, 0, 0); } .cm-tw-deleted { text-decoration: line-through; } .cm-tw-header5 { font-weight: 700; } .cm-tw-listitem:first-child { padding-left: 10px; } .cm-tw-box { border-style: solid; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-color: inherit; border-top-width: 0px !important; } .cm-tw-underline { text-decoration: underline; } @media print { .CodeMirror div.CodeMirror-cursor { visibility: hidden; } } :root { --side-bar-bg-color: #fafafa; --control-text-color: #777; } @include-when-export url(https://fonts.loli.net/css?family=Open+Sans:400italic,700italic,700,400&subset=latin,latin-ext); /* open-sans-regular - latin-ext_latin */ /* open-sans-italic - latin-ext_latin */ /* open-sans-700 - latin-ext_latin */ /* open-sans-700italic - latin-ext_latin */ html { font-size: 16px; -webkit-font-smoothing: antialiased; } body { font-family: "Open Sans","Clear Sans", "Helvetica Neue", Helvetica, Arial, 'Segoe UI Emoji', sans-serif; color: rgb(51, 51, 51); line-height: 1.6; } #write { max-width: 860px; margin: 0 auto; padding: 30px; padding-bottom: 100px; } @media only screen and (min-width: 1400px) { #write { max-width: 1024px; } } @media only screen and (min-width: 1800px) { #write { max-width: 1200px; } } #write > ul:first-child, #write > ol:first-child{ margin-top: 30px; } a { color: #4183C4; } h1, h2, h3, h4, h5, h6 { position: relative; margin-top: 1rem; margin-bottom: 1rem; font-weight: bold; line-height: 1.4; cursor: text; } h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, h5:hover a.anchor, h6:hover a.anchor { text-decoration: none; } h1 tt, h1 code { font-size: inherit; } h2 tt, h2 code { font-size: inherit; } h3 tt, h3 code { font-size: inherit; } h4 tt, h4 code { font-size: inherit; } h5 tt, h5 code { font-size: inherit; } h6 tt, h6 code { font-size: inherit; } h1 { font-size: 2.25em; line-height: 1.2; border-bottom: 1px solid #eee; } h2 { font-size: 1.75em; line-height: 1.225; border-bottom: 1px solid #eee; } /*@media print { .typora-export h1, .typora-export h2 { border-bottom: none; padding-bottom: initial; } .typora-export h1::after, .typora-export h2::after { content: ""; display: block; height: 100px; margin-top: -96px; border-top: 1px solid #eee; } }*/ h3 { font-size: 1.5em; line-height: 1.43; } h4 { font-size: 1.25em; } h5 { font-size: 1em; } h6 { font-size: 1em; color: #777; } p, blockquote, ul, ol, dl, table{ margin: 0.8em 0; } li>ol, li>ul { margin: 0 0; } hr { height: 2px; padding: 0; margin: 16px 0; background-color: #e7e7e7; border: 0 none; overflow: hidden; box-sizing: content-box; } li p.first { display: inline-block; } ul, ol { padding-left: 30px; } ul:first-child, ol:first-child { margin-top: 0; } ul:last-child, ol:last-child { margin-bottom: 0; } blockquote { border-left: 4px solid #dfe2e5; padding: 0 15px; color: #777777; } blockquote blockquote { padding-right: 0; } table { padding: 0; word-break: initial; } table tr { border: 1px solid #dfe2e5; margin: 0; padding: 0; } table tr:nth-child(2n), thead { background-color: #f8f8f8; } table th { font-weight: bold; border: 1px solid #dfe2e5; border-bottom: 0; margin: 0; padding: 6px 13px; } table td { border: 1px solid #dfe2e5; margin: 0; padding: 6px 13px; } table th:first-child, table td:first-child { margin-top: 0; } table th:last-child, table td:last-child { margin-bottom: 0; } .CodeMirror-lines { padding-left: 4px; } .code-tooltip { box-shadow: 0 1px 1px 0 rgba(0,28,36,.3); border-top: 1px solid #eef2f2; } .md-fences, code, tt { border: 1px solid #e7eaed; background-color: #f8f8f8; border-radius: 3px; padding: 0; padding: 2px 4px 0px 4px; font-size: 0.9em; } code { background-color: #f3f4f4; padding: 0 2px 0 2px; } .md-fences { margin-bottom: 15px; margin-top: 15px; padding-top: 8px; padding-bottom: 6px; } .md-task-list-item > input { margin-left: -1.3em; } @media print { html { font-size: 13px; } table, pre { page-break-inside: avoid; } pre { word-wrap: break-word; } } .md-fences { background-color: #f8f8f8; } #write pre.md-meta-block { padding: 1rem; font-size: 85%; line-height: 1.45; background-color: #f7f7f7; border: 0; border-radius: 3px; color: #777777; margin-top: 0 !important; } .mathjax-block>.code-tooltip { bottom: .375rem; } .md-mathjax-midline { background: #fafafa; } #write>h3.md-focus:before{ left: -1.5625rem; top: .375rem; } #write>h4.md-focus:before{ left: -1.5625rem; top: .285714286rem; } #write>h5.md-focus:before{ left: -1.5625rem; top: .285714286rem; } #write>h6.md-focus:before{ left: -1.5625rem; top: .285714286rem; } .md-image>.md-meta { /*border: 1px solid #ddd;*/ border-radius: 3px; padding: 2px 0px 0px 4px; font-size: 0.9em; color: inherit; } .md-tag { color: #a7a7a7; opacity: 1; } .md-toc { margin-top:20px; padding-bottom:20px; } .sidebar-tabs { border-bottom: none; } #typora-quick-open { border: 1px solid #ddd; background-color: #f8f8f8; } #typora-quick-open-item { background-color: #FAFAFA; border-color: #FEFEFE #e5e5e5 #e5e5e5 #eee; border-style: solid; border-width: 1px; } /** focus mode */ .on-focus-mode blockquote { border-left-color: rgba(85, 85, 85, 0.12); } header, .context-menu, .megamenu-content, footer{ font-family: "Segoe UI", "Arial", sans-serif; } .file-node-content:hover .file-node-icon, .file-node-content:hover .file-node-open-state{ visibility: visible; } .mac-seamless-mode #typora-sidebar { background-color: #fafafa; background-color: var(--side-bar-bg-color); } .md-lang { color: #b4654d; } /*.html-for-mac { --item-hover-bg-color: #E6F0FE; }*/ #md-notification .btn { border: 0; } .dropdown-menu .divider { border-color: #e5e5e5; opacity: 0.4; } .ty-preferences .window-content { background-color: #fafafa; } .ty-preferences .nav-group-item.active { color: white; background: #999; } .menu-item-container a.menu-style-btn { background-color: #f5f8fa; background-image: linear-gradient( 180deg , hsla(0, 0%, 100%, 0.8), hsla(0, 0%, 100%, 0)); } :root {--mermaid-font-zoom:1em ;} @media print { @page {margin: 0 0 0 0;} body.typora-export {padding-left: 0; padding-right: 0;} #write {padding:0;}} 技术概要:使用 InterSystems 产品优化 SQL 性能使用 InterSystems SQL 优化查询使用 EXPLAIN 关键字显示查询计划在管理门户(Management Portal)中使用 SQL 查询接口显示查询计划发现查询计划结果中潜在的性能问题测试查询执行测试位片索引(Bitslice Index)的效果向交易类型字段(TransactionType Field)添加位图索引(Bitmap Index)重新测试查询性能查看随时间变化的查询性能了解有关 InterSystems SQL 的更多信息介绍材料 技术概要:使用 InterSystems 产品优化 SQL 性能 技术概要:使用 InterSystems 产品优化 SQL 性能 本技术概要(First Look)指南向您介绍了 InterSystems SQL 查询优化,包括查询分析工具的使用,几种索引方法以及随着时间的变化查看运行时统计数据的能力。 要浏览所有的技术概要(First Look),包括其他可以在免费的云实例或 web 实例上执行的技术概要(First Look),请参见 InterSystems First Looks(《InterSystems 技术概要》)。 使用 InterSystems SQL 优化查询 InterSystems IRIS®数据平台为 SQL 查询性能调整提供全套工具: 查询计划分析的图形化显示 如位图和位片索引(bitslice index)等索引策略结构紧凑,可由矢量化 CPU 指令有效处理。每种索引类型都为某些查询类型(如逻辑条件、计数和聚合函数)提供了好处。通过索引,您可以在一个核心上实现每秒多达数十亿行的查询性能结果。 随时间变化的 SQL 查询性能指标 重要提示: 下面演示中显示的查询性能数字代表了在一台 Windows 10 笔记本电脑上进行的多次演示试验。根据您的环境,您可能会看到不同的查询性能数字。 想查看 InterSystems IRIS SQL 功能快速演示吗?请查看 SQL QuickStart(SQL 快速入门)! 用前须知 阅读本文之前,建议阅读并完成 First Look:InterSystems SQL(《技术概要:InterSystems SQL》)。在这里,您将再次使用 InterSystems IRIS SQL Shell;您将使用的数据来自您在技术概要:InterSystems SQL的演示时创建的百万记录股票交易数据表。 您还将运行 TuneTableutility,它检查表中的数据并创建 InterSystems SQL 查询优化器(决定如何最好地运行任何查询的引擎)使用的统计数据。这些统计数据包括表的大小(区段大小)和每列唯一值的数量(选择性)。优化器在决定连接顺序等情况下使用表大小,其中最好从较小的表开始。在表有多个索引的情况下,选择性有助于优化器选择最佳索引。在产品实例中,您通常只运行一次 TuneTable:在数据被加载到表中之后和运行之前。 演示:显示和解释优化前的查询计划 First Look:InterSystems SQL (《技术概要:InterSystems SQL》)解释了如何执行在技术概要(First Look)和此处运行演示所需的以下步骤: 选择一个 InterSystems IRIS 实例。您的选择包括多种类型的已授权的和免费的评估实例;关于如何部署每种类型的信息,请参见 InterSystems IRIS Basics: Connecting an IDE(《InterSystems IRIS 基础:连接一个 IDE》)中的 Deploying InterSystems IRIS(部署 InterSystems IRIS)。 打开 InterSystems 终端(Terminal)(简称终端(Terminal))来运行 SQL Shell。 从 GitHub repo https://github.com/intersystems/FirstLook-SQLBasics 获取本指南的实用程序文件和数据,包括 stock_table_demo_two.csv,其中包含一百万行股票表数据 Loader.xml,是一个包含实用程序方法的类文件,用于将 Stock_table_demo_two.csv 中的数据加载到InterSystems IRIS 表中。 运行 TuneTable 实用程序 如果您的 InterSystems IRIS 实例不再包含 StockTableDemoTwotable,请按照 Demo:Using Bitmap Indexing To Maximize Query Performance(《演示:使用位图索引最大化查询性能》)(在执行 SELECT DISTINCT 查询之前停止)的前四个步骤重新创建并加载它。 在 SQL Shell 中,在 FirstLook.StockTableDemoTwo 上运行 TuneTable 实用程序,如下所示: OBJ DO $SYSTEM.SQL.TuneTable("FirstLook.StockTableDemoTwo") 该命令在 SQL Shell 中不会生成可见的输出。 使用 EXPLAIN 关键字显示查询计划 这个演示假设您想获得所有"SELL"交易的平均价格。鉴于该表包含一百万条记录,所需的查询有可能非常慢。 虽然您可能已经想在 Price 和 TransactionType 字段上创建索引,但在开始优化工作之前,查看查询计划将具有指导意义。在 SQL Shell 中,您可以通过在查询前添加 EXPLAIN 关键字来显示查询计划。查询计划显示 SQL 查询优化器将如何使用索引(如果有索引的话),或者是否将直接读取表数据来执行语句。 要使用 EXPLAIN 关键字来显示查询计划,在 SQL Shell 中执行以下语句: EXPLAIN SELECT AVG(Price) As AveragePrice FROM FirstLook.StockTableDemoTwo WHERE TransactionType = 'SELL' 这将返回格式为 XML 的查询计划: xxxxxxxxxx Plan "<plans> <plan> <sql> SELECT AVG ( Price ) AS AveragePrice FROM FirstLook . StockTableDemoTwo WHERE TransactionType = ? /*#OPTIONS {""DynamicSQLTypeList"":""1""} */ </sql> <cost value=""1827000""/> Call module B. Output the row. <module name=""B"" top=""1""> Process query in parallel, partitioning master map FirstLook.StockTableDemoTwo.IDKEY into subranges of T1.ID values, piping results to temp-file A: SELECT count(T1.Price),sum(T1.Price) FROM %NOPARALLEL FirstLook.StockTableDemoTwo T1 where ((%SQLUPPER(T1.TransactionType) = %SQLUPPER(?))) Read temp-file A, looping on a counter. For each row: Accumulate the count([value]). Accumulate the sum([value]). </module> 演示:显示和解释优化前的查询计划 </plan> <plan> <sql> SELECT COUNT ( T1 . Price ) , SUM ( T1 . Price ) FROM %NOPARALLEL FirstLook . StockTableDemoTwo T1 WHERE ( ( %SQLUPPER ( T1 . TransactionType ) = %SQLUPPER ( ? ) ) ) %PARTITION BY T1 . ID > ? AND T1 . ID <= ? </sql> <cost value=""1827000""/> Call module B. Output the row. <module name=""B"" top=""1""> Read master map FirstLook.StockTableDemoTwo.IDKEY, looping on ID (with a range condition). For each row: Accumulate the count(Price). Accumulate the sum(Price). </module> </plan> </plans>" 您将看到为执行 SQL 查询而生成的查询计划可以分为多个模块,每个模块都执行计划的不同部分,例如评估子查询。 实际上,这个查询计划被分为两个独立的计划。顶部计划用于初始查询。它调用一个模块 B,在这个模块中,其中"主映射"被分区,并在每个分区上并行执行子查询。子查询的计划遵循初始查询的计划。 在 "Spotting Potential Performance Issues in Query Plan Results(在查询计划结果中发现潜在的性能问题)"中,您将学习如何识别这个查询的问题。 在管理门户(Management Portal)中使用 SQL 查询接口显示查询计划 InterSystems IRIS 在管理门户(Management Portal)中提供了一个基于 web 的接口,用于 SQL 查询执行和计划分析。要使用管理门户(Management Portal)中的 SQL 查询接口显示查询计划: 使用 InterSystems IRIS Basics:Connecting an IDE(《InterSystems IRIS 基础:连接一个 IDE》)中URL described for your instance(为您的实例描述的 URL),在浏览器中打开您的实例的管理门户(Management Portal)。 请确保您是在 USER 命名空间。 如果您还没有找到,可以: 在屏幕的顶部面板中,点击当前命名空间名称右侧的 SWITCH(交换)。 在弹出的窗口中,选择 USER(用户) 并点击 OK(确定)。 导航到 SQL 页面(System Explorer(系统资源管理器) > SQL)。 省略 EXPLAIN 关键字,将"Using the EXPLAIN Keyword to Show a Query Plan(使用 EXPLAIN 关键字显示查询计划)中的查询粘贴到 Execute Query(执行查询) 标签的文本字段中。 点击 Show Plan(显示计划) 来显示查询计划。 结果将如下所示: 解释这些结果是下一节的主题。 发现查询计划结果中潜在的性能问题 查看查询计划结果,您可以看到这个查询存在一些严重的潜在性能问题。如果您查看子查询的计划,也就是实际工作完成的地方,您可以看到第一个任务是 "读取主映射"。这意味着 InterSystems SQL 查询优化器不会使用任何索引来运行查询; 演示:测试查询优化 相反,查询将遍历表中的所有 ID。特别是在大表的情况下,这表明查询将不能很好地执行。 当您优化查询时,您会看到它的执行时间减少,查询计划也会有明显的变化。 注意: 相对成本可以很好地预测性能,但只是相对于某个特定的查询而言。如果您在表中添加了一个索引,并且看到相对成本下降了,那么很可能这个查询现在会运行得更快。然而,相对成本并不是为了比较两个不同查询的性能。 测试查询执行 要获取有关未优化查询将如何执行的一些实际数据,请在 SQL Shell 中运行它: SELECT AVG(Price) As AveragePrice FROM FirstLook.StockTableDemoTwo WHERE TransactionType = 'SELL' GO 输出将如下所示: xxxxxxxxxx AveragePrice 266.1595139195757844 1 Rows(s) Affected statement prepare time(s)/globals/cmds/disk: 0.0009s/6/1246/0ms execute time(s)/globals/cmds/disk: 0.2599s/1000075/8502571/0ms cached query class: %sqlcq.USER.cls5 语句准备和执行指标分别列出。特别注意以下两项: 执行时间为 0.2599 秒。虽然这个时间看起来并不算长,但可以通过使用索引大大改善。 在执行步骤中读取的 globals 数量为 1,000,075。(Globals 是 InterSystems IRIS 用来存储数据的多维稀疏数组(multidimensional sparse arrays);更多信息,请参见 Introduction to InterSystems IRIS Programming(《InterSystems IRIS 编程简介》)中的Introduction to Globals(Globals 简介)一章。为了提高查询性能,应该减少这个数字。您会在下一节看到这种情况。 重要提示: 准备工作只需一次:第一次重新规划查询。如果修改了相关表或添加或删除了索引,查询将自动重新规划。大多数应用程序只准备一次查询,但会多次执行。因此,在这个演示中,我们的重点将是调整执行性能。 演示:测试查询优化 向价格字段添加位片索引(Bitslice Index) 如果您的查询将包含一个或多个字段上的聚合函数,那么为这些字段中的一个或多个字段添加位片索引(bitslice index)可能会提高性能。 位片索引(bitslice index)将字段中的每个数字数据值表示为二进制位字符串,二进制值中的每个数字都有一个位图,以记录哪些行的二进制数字为 1。 因为我们想要获得所有"SELL"交易的平均价格,所以在 Price 字段中添加一个位片索引(bitslice index)是有意义的。要在 Price 字段上创建位片索引(bitslice index) PriceIdx,请在 SQL Shell 中执行以下语句: 演示:测试查询优化 CREATE BITSLICE INDEX PriceIdx ON TABLE FirstLook.StockTableDemoTwo (Price) CREATE BITSLICE INDEX PriceIdx ON TABLE FirstLook.StockTableDemoTwo (Price) xxxxxxxxxx 0 Rows Affected statement prepare time(s)/globals/cmds/disk: 0.0091s/2000/13151/0ms execute time(s)/globals/cmds/disk: 1.4268s/2087789/55765062/1ms cached query class: %sqlcq.USER.cls7 但是,正如您将在下面看到的,仅仅因为您创建了索引并不一定意味着 InterSystems SQL 查询优化器会使用它 。 测试位片索引(Bitslice Index)的效果 要查看新的位片索引(bitslice index)是否会对查询的执行方式或运行速度产生任何影响,请使用上述任何一种方法(SQL Shell 或管理门户(Management Portal))来显示查询计划。 正如您将看到的,查询计划仍然与以前相同。InterSystems SQL 查询优化器将不会使用新的索引。 运行查询产生的性能统计数据与您创建位片索引(bitslice index)之前几乎相同(0.2559 秒的执行时间与 0.2599 秒相比)。InterSystems IRIS 智能地缓存查询计划和数据,因此同一查询的后续运行可能会提高性能,鉴于查询性能时间略有不同,这里可能就是这种情况。 在机器上运行的其他应用程序也会影响性能。 SELECT AVG(Price) As AveragePrice FROM FirstLook.StockTableDemoTwo WHERE TransactionType = 'SELL' GO SELECT AVG(Price) As AveragePrice FROM FirstLook.StockTableDemoTwo WHERE TransactionType = 'SELL' xxxxxxxxxx AveragePrice 266.1595139195757844 1 Rows(s) Affected statement prepare time(s)/globals/cmds/disk: 0.0569s/35431/227191/0ms execute time(s)/globals/cmds/disk: 0.2559s/1000075/8502571/0ms cached query class: %sqlcq.USER.cls8 如果从查询中删除 WHERE 子句,当您显示查询计划时,将会看到完全不同的结果: 如您所见,读取位片索引(bitslice index)是查询计划的第一步。在这个计划中没有读取到"总映射"。 SQL 查询优化器也使用第二个索引 FirstLook.StockTableDemoTwo.$StockTableDemoTwo。这是位图范围索引, 在执行 CREATE TABLESQL 语句时自动创建。它是表中所有行的位图索引(bitmap index),而不仅仅是一个字段,每个位的值反映了该行是否实际存在。 演示:测试查询优化 然而,我们真正想要运行的查询包含一个 WHERE 子句。因此,我们必须找到一种方法,让 SQL 查询优化器在 WHERE 子句存在时使用索引。 向交易类型字段(TransactionType Field)添加位图索引(Bitmap Index) 如果您阅读了 InterSystems SQL Optimization Guide(《InterSystems SQL 优化指南》),您会发现 InterSystems SQL 查询优化器在与 WHERE 子句字段上的位图索引(bitmap index)结合使用时,通常会使用位片索引(bitslice index)。 这是因为没有 WHERE 子句的聚合查询可以简单地聚合索引中的所有数据。然而,为了只聚合满足 WHERE 条件的行,查询必须掩码那些不满足条件的行的位片索引(bitslice index)的位。在 WHERE 子句中的字段上有一个位图索引(bitmap index),允许有效地构建这个掩码。 幸运的是,查询中的另一个字段 TransactionType 是位图索引(bitmap index)的一个很好的候选者,因为它的可能值计数是两个("SELL"和"BUY")。 要向 TransactionType 字段添加位图索引(bitmap index),请在 SQL Shell 中执行以下语句: CREATE BITMAP INDEX TransactionTypeIdx ON TABLE FirstLook.StockTableDemoTwo (TransactionType) CREATE BITMAP INDEX TransactionTypeIdx ON TABLE FirstLook.StockTableDemoTwo (TransactionType) xxxxxxxxxx 0 Rows Affected statement prepare time(s)/globals/cmds/disk: 0.0069s/2001/13291/0ms execute time(s)/globals/cmds/disk: 1.1046s/2088960/19771584/0ms cached query class: %sqlcq.USER.cls7 重新测试查询性能 现在,您已经添加了位片索引(bitslice index)和位图索引(bitmap index):如果您显示的查询计划 SELECT AVG(Price) as AveragePrice FROM FirstLook.StockTableDemoTwo WHERE TransactionType = 'SELL' 在 SQL Shell 或管理门户(Management Portal)中,您会看到查询优化器使用您创建的两个索引来获得最佳性能。 还请注意,18742 的相对成本只是未优化查询的一小部分,其成本为 1827000。 演示:测试查询优化 最后,如果您在 SQL Shell 中运行这个查询,您会看到对 globals 的更有效使用(594 而不是 1000075)。 最关键的是,有索引的查询运行速度比无索引的查询快了近 85 倍(执行时间为 0.0031 秒,而非 0.2599)。 查看随时间变化的查询性能 SELECT AVG(Price) As AveragePrice FROM FirstLook.StockTableDemoTwo WHERE TransactionType = 'SELL' GO SELECT AVG(Price) As AveragePrice FROM FirstLook.StockTableDemoTwo WHERE TransactionType = 'SELL' xxxxxxxxxx AveragePrice 266.1595139195757844 1 Rows(s) Affected statement prepare time(s)/globals/cmds/disk: 0.0554s/34877/186130/0ms execute time(s)/globals/cmds/disk: 0.0031s/594/2878/0ms cached query class: %sqlcq.USER.cls8 为了随时间跟踪查询的性能,InterSystems IRIS 提供了查询统计数据,您将在下一节了解如何查看这些数据。 查看随时间变化的查询性能 要跟踪运行缓慢的查询或查看新查询在产品中的运行情况,您可以使用管理门户(Management Portal)中的 SQL Statements(SQL 语句)视图。要导航到这个视图,在管理门户(Management Portal)中打开 SQL 查询接口,并点击 SQL Statements(SQL语句)。 例如,如果您上面调整的查询在其原始(未优化的)计划下运行了 9 次,您可能会看到类似这样的结果: 点击 SQL Statement Text column(SQL 语句文本列)中的语句链接, 允许您以 SQL 形式查看查询: 您还可以使用缓存查询类的名称将 SQL 语句执行与 SQL Statements(SQL 语句)视图联系起来,该名称是 SQL Shell 中输出的最后一行,并列在 SQL Statements(SQL 语句) 的 Location(s)(位置)列中。 在您优化查询并运行几次后,您可以期望看到 Total time(总时间)和 Average time(平均时间)的改善列。 了解有关 InterSystems SQL 的更多信息 请注意,Count(计数)的值已经下降。这是因为位图和位片索引(bitslice index)的添加引起了查询计划的改变,这反过来又触发了对相关类的缓存查询的删除。该查询在新查询计划下总共运行了 8 次,平均每天 4 次。 了解有关 InterSystems SQL 的更多信息 要了解更多有关 SQL 和 InterSystems IRIS 的信息,请参见: 介绍材料 First Look:InterSystems SQL(《技术概要:InterSystems SQL》) Using InterSystems SQL(《使用 InterSystems SQL》) InterSystems SQL Reference(《InterSystems SQL 参考书目》) InterSystems SQL Overview(《InterSystems SQL 概述》) SQL 开发 SQL -- Things You Should Know(《SQL -- 您应该知道的事情》) Developing with InterSystems Objects and SQL(《使用 InterSystems Objects 和 SQL 开发》) 查询优化 InterSystems SQL Optimization Guide(《InterSystems SQL 优化指南》) Optimizing SQL Queries(《优化 SQL 查询》) 分片和可扩展性 First Look:Scaling for Data Volume with Sharding(《技术概要:带分片的数据卷扩展》) Scalability Guide(《可扩展性指南》) SQL 搜索 First Look:SQL Search with InterSystems IRIS(《技术概要:使用 InterSystems IRIS 进行 SQL 搜索》) Using InterSystems SQL Search(《使用 InterSystems SQL 搜索》) Creating iFind Indices for Searching Text Fields(《创建用于搜索文本字段的 iFind 索引》) JDBC First Look:JDBC and InterSystems IRIS (《技术概要:JDBC 和 InterSystems IRIS》) Using Java JDBC with InterSystems IRIS(《在 InterSystems IRIS 中使用 Java JDBC》)<文档> Java Overview (《Java 概述》) 了解有关 InterSystems SQL 的更多信息 Using JDBC with InterSystems IRIS(《在 InterSystems IRIS 中使用 JDBC》) <在线学习>
文章
Jingwei Wang · 九月 26, 2021

IRIS 2021 技术文档 First Look 11 用于.Net的InterSystems IRIS Native API

本文解释了如何使用 InterSystems IRIS Native 从 .NET 应用程序访问 InterSystems IRIS®数据平台 globals。IRIS Native 还允许您运行ObjectScript 方法、函数和Routine。在本文中,您首先将连接到 InterSystems IRIS。然后您将在 InterSystems IRIS 中设置和检索一个global 节点的值,并在另一个 global 节点上进行迭代。您还将调用 InterSystems IRIS 类方法。所有这些活动都将在 .NET 应用程序中执行。 为了让您体验 IRIS Native,而又不陷入细节困境,本次探索特意设计得很简洁。这些活动被设计成只使用默认设置和功能,这样您就可以熟悉功能的基本原理,而不必处理那些离题或过于复杂的细节。当您把 IRIS Native 引入您的生产系统时,您可能需要做一些不同的事情。请确保不要把这种对 IRIS Native 的探索与真实的情况相混淆! Globals 简介 InterSystems IRIS 提供了一种易于使用的方法来将数据存储在持久的多维数组中。global 是存储在 InterSystems IRIS 物理数据库中的多维数组。在应用程序中,globals 到物理数据库的映射基于当前命名空间,命名空间提供一个或多个物理数据库的逻辑统一视图。例如,要使用一个名为 ^Settings 的 global 将值 "Red "与键 "Color "关联起来,打开 InterSystems 终端并输入以下代码: set ^Settings("Color")="Red" 您可以利用 globals 的多维特性来定义一个更复杂的结构: set ^Settings("Auto1","Properties","Color") = "Red" set ^Settings("Auto1","Properties","Model") = "SUV" set ^Settings("Auto2","Owner") = "Mo" set ^Settings("Auto2","Properties","Color") = "Green" 探索 IRIS Native 现在,您已经准备好实验 IRIS Native 了。下面的简短演示将向您演示如何在 .NET 应用程序中使用 IRIS Native。(想试试 InterSystems IRIS .NET 开发和互操作性功能的在线视频演示?请看.NET 快速入门! 用前须知 要使用该程序,您需要一个安装了 .NET 框架和 Visual Studio 的 Windows 系统,以及一个运行中的 InterSystems IRIS 实例来连接。您对InterSystems IRIS 的选择包括多种类型的已授权的和免费的评估实例;实例不需要由您正在工作的系统托管(尽管它们必须相互具有网络访问权限)。关于如何部署每种类型的实例的信息(如果您还没有可使用的实例),请参见InterSystems IRIS 基础:连接一个 IDE中的部署InterSystems IRIS 。使用同一文档中的InterSystems IRIS 连接信息和 .Net IDE 中的信息将 Visual Studio 连接到您的 InterSystems IRIS 实例。 配置 Visual Studio 项目 首先,打开 Visual Studio 并创建一个新的控制台应用程序项目,选择 Visual C# 和 Console App (.NET Framework)(控制台应用程序(.NET 框架))选项。对于 Name(名称)字段,输入 netnative。 添加程序集引用 InterSystems.Data.IRISClient.dll 程序集必须存在于您的本地系统中;您可以从 https://github.com/intersystems/quickstarts-dotnet/tree/master/EFPlay/bin/Debug 下载它。如果 InterSystems IRIS 安装在您的本地系统或您可以访问的另一个系统上,则该程序集已经安装在子目录 install-dir\dotnet\bin\v4.5 中,其中 install-dir 是该实例的安装目录。 要将 InterSystems.Data.IRISClient.dll 的程序集引用添加到项目: 从 Visual Studio 主菜单中,选择 Project(项目) > Add Reference(添加引用)... 在出现的窗口中,点击 Browse(浏览).... 浏览到 InterSystems.Data.IRISClient.dll 文件的位置。 选择文件并点击 Add(添加)。 点击 OK(確定)。 在 Visual Studio Solution Explorer 中, InterSystems.Data.IRISClient.dll 程序集现在应该列在 Reference(引用)下。 使用 IRIS Native 现在,您已经准备好实验 IRIS Native 了。打开在创建 Visual Studio 项目时创建的文件(例如,Program.cs)。删除文件的默认内容并粘贴以下代码,用您的 InterSystems IRIS 实例的连接信息替换 conn.ConnectionString 中的值。(logfile 的值必须是本地系统上的可写路径)。您可以指定所示的 USER命名空间,也可以选择在实例上创建另一个命名空间。 using System; using InterSystems.Data.IRISClient; using InterSystems.Data.IRISClient.ADO; public class IRISNative { public static void Main(String[] args) { try { // open connection to InterSystems IRIS instance using connection string IRISConnection conn = new IRISConnection(); // edit this ConnectionString to match your environment conn.ConnectionString = "Server=localhost; Port=1972; Namespace=User; Password=SYS; User ID=_system; logfile=c:\\sandbox\\dbnative.log"; conn.Open(); // create IRIS Native object IRIS iris = IRIS.CreateIRIS(conn); Console.WriteLine("[1. Setting and getting a global]"); // setting and getting a global // ObjectScript equivalent: set ^testglobal("1") = 8888 iris.Set(8888, "^testglobal", "1"); // ObjectScript equivalent: set globalValue = $get(^testglobal("1")) Int16? globalValue = iris.GetInt16("^testglobal", "1"); Console.WriteLine("The value of ^testglobal(1) is " + globalValue); Console.WriteLine(); Console.WriteLine("[2. Iterating over a global]"); // modify global to iterate over // ObjectScript equivalent: set ^testglobal("1") = 8888 // ObjectScript equivalent: set ^testglobal("2") = 9999 iris.Set(8888, "^testglobal", "1"); iris.Set(9999, "^testglobal", "2"); // iterate over all nodes forwards Console.WriteLine("walk forwards"); IRISIterator subscriptIter = iris.GetIRISIterator("^testglobal"); foreach (var node in subscriptIter) { Console.WriteLine("subscript=" + subscriptIter.CurrentSubscript + ", value=" + node); } Console.WriteLine(); Console.WriteLine("[3. Calling a class method]"); // calling a class method // ObjectScript equivalent: set returnValue = ##class(%Library.Utility).Date(5) String returnValue = iris.ClassMethodString("%Library.Utility", "Date", 5); Console.WriteLine(returnValue); Console.WriteLine(); // close IRIS object and connection iris.Close(); conn.Close(); } catch (Exception ex) { Console.WriteLine(ex.Message); }}} 示例代码分为三个部分: 第一部分展示了如何设置一个 global 的值以及稍后如何检索它。这部分里面执行的命令等同于 ObjectScript 的 SET 和 GET 命令。 第二部分展示了如何迭代 global 的子节点,类似于ObjectScript中的 $ORDER函数。 第三部分展示了如何使用 IRIS Native 从您的 .NET 应用程序调用 ObjectScript 类方法。 如果您使用的是本地安装的实例,并且因此连接使用 localhost 作为服务器地址,那么程序可能使用本地共享内存连接,这比标准的 TCP/IP 连接还要快。 注意: ObjectScript 中的 Globals 以插入符号(^)开头。在使用 InterSystems IRIS Native API 的 .NET 应用程序中,这不是必需的。 运行练习 现在可以按 Ctrl+F5 运行演示应用程序了。如果示例执行成功,您应该会看到带有示例代码结果的打印输出: [1. Setting and getting a global] The value of ^testglobal(1) is 8888 [2. Iterating over a global] walk forwards subscript=1, value=8888 subscript=2, value=9999 [3. Calling a class method] Oct 30, 2018 在管理门户(Management Portal)中确认变更 接下来,使用以下程序在管理门户(Management Portal)中确认您的结果: 使用InterSystems IRIS 基础:连接一个 IDE中为您的实例描述的 URL,在浏览器中打开您的实例的管理门户(Management Portal)。 如果您不在代码指定的命名空间中,请切换到该命名空间。 导航到 Globals 页面(System Explorer(系统资源管理器) > Global)。您应该会看到在示例代码中创建的 testglobal。点击 VIEW 来查看其内容。 您应该会看到 global 的两个节点:^testglobal(1) = 8888 和 ^testglobal(2) = 9999。 了解有关 IRIS Native 的更多信息 有关 IRIS Native、globals 和 InterSystems IRIS 的更多信息,请参见: 用于.Net的 Native API 技术概要:用于Java的 InterSystems IRIS Native API 使用 Globals 用于.NET 的 InterSystems Managed Provider
文章
Michael Lei · 五月 17, 2022

InterSystems 最佳实践系列之使用Doker和配置文件模版来部署Sharded 集群

在这篇文章中,我们将使用docker和 参数配置文件模版 这一新特性来运行IRIS集群且轻松配置好。 在 UNIX® 和 Linux 上,您可以使用声明式参数配置合并文件来修改默认的 iris.cpf。合并文件是一个部分 CPF,在实例启动时为任何数量的参数设置所需的值。CPF 合并操作对每个实例只起一次作用。 我们的集群架构非常简单,它将由一个主节点(Node1)和两个数据节点(检查所有可用角色)组成。不幸的是,docker-compose不能部署到几个服务器上(尽管它可以部署到远程主机上),所以这对本地开发分片的数据模型、测试等很有用。如果是生产的InterSystems IRIS集群部署,你应该使用ICM云管理器或IKO K8S调度器。 Docker-compose.yml 我们从docker-compose 配置开始: docker-compose.yml version: '3.7' services: iris1: image: containers.intersystems.com/intersystems/iris:2020.3.0.221.0 init: true command: --key /ISC/iris.key hostname: iris1 environment: - ISC_DATA_DIRECTORY=/ISC/iris.sys.d/sys1 - ISC_CPF_MERGE_FILE=/ISC/CPF2merge-master-instance.conf volumes: - ./:/ISC:delegated ports: - 9011:1972 - 9012:52773 iris2: image: containers.intersystems.com/intersystems/iris:2020.3.0.221.0 command: --key /ISC/iris.key --before 'sleep 60' init: true hostname: iris2 environment: - ISC_DATA_DIRECTORY=/ISC/iris.sys.d/sys2 - ISC_CPF_MERGE_FILE=/ISC/CPF2merge-data-instance.conf volumes: - ./:/ISC:delegated depends_on: - iris1 ports: - 9021:1972 - 9022:52773 iris3: image: containers.intersystems.com/intersystems/iris:2020.3.0.221.0 command: --key /ISC/iris.key --before 'sleep 60' init: true hostname: iris3 environment: - ISC_DATA_DIRECTORY=/ISC/iris.sys.d/sys3 - ISC_CPF_MERGE_FILE=/ISC/CPF2merge-data-instance.conf volumes: - ./:/ISC:delegated depends_on: - iris1 ports: - 9031:1972 - 9032:52773 正如你所看到的,我们正在运行一个默认的intersystems/iris:2020.3.0.221.0镜像,提供License Key(必须支持Sharding),使用持久化的%SYS特性持久化数据,并提供ISC_CPF_MERGE_FILE,指向我们的合并文件(主节点和数据节点的文件不同)。此外,数据节点的启动晚了一分钟来让主节点先启动,这是个非常保守的估计,在一个正常的硬件上,启动时间最多几秒钟。 集群配置在CPF合并文件中,让我们来看看 Cluster configuration happens at CPF merge files, let's check them out CPF2merge-data-instance.conf [Startup] PasswordHash=FBFE8593AEFA510C27FD184738D6E865A441DE98,u4ocm4qh ShardRole=node1 [config] MaxServerConn=64 MaxServers=64 globals=0,0,400,0,0,0 errlog=1000 routines=32 gmheap=256000 locksiz=1179648 发生了什么情况? 在[Startup] 部分,我们通过为我们的集群分配主节点角色来启用分片。在[config]中,我们将服务器扩大一些,允许更多的缓存和连接。就这么简单! CPF2merge-data-instance.conf [Startup] ShardClusterURL=IRIS://iris1:1972/IRISCLUSTER ShardRole=DATA 针对数据节点, 我们需要提供主节点和节点角色的URL. 试一下 试试这个repository 或者运行这段代码: git clone https://github.com/intersystems-ru/iris-container-recipes.git cd iris-container-recipes cd cluster // copy iris.key in cluster folder docker-compose up -d 启动InterSystems IRIS集群后你可以从 浏览器 访问. 用户名/密码 is: _SYSTEM/SYS. 结论 合并CPF文件 是一个很好用而简单的工具来配置InterSystems IRIS的实例。 感谢@Luca.Ravazzolo提供的代码并回答了我所有的问题。
文章
Hao Ma · 十一月 2, 2021

IRIS 2021 技术文档 First Look 18 - 在 InterSystems 产品中开发 REST 接口

本文档将向您展示如何开发 REST 接口。您可以将这些 REST 接口与 UI 工具(如 Angular)一起使用,以提供对数据库和互操作性产品的访问。您也可以使用它们支持外部系统访问 InterSystems IRIS®数据平台应用程序。要浏览所有的技术概要(First Look),包括可以在 InterSystems IRIS 免费的评估实例上执行的那些,请参见 InterSystems First Looks(《InterSystems 技术概要》)。 1 为什么提供 REST 接口如果您需要从外部系统访问 InterSystems IRIS 数据库中的数据,或者想为这些数据提供用户界面,您可以通过定义 REST 接口来实现。REST——REpresentational State Transfer——是一种使用公开的 URL 从另一个系统检索、添加、更新或删除数据的方法。REST 基于 HTTP,并使用 HTTP 动词 POST、GET、PUT 和 DELETE 映射到数据库应用程序常见的创建、读取、更新和删除 (CRUD)功能。您还可以在 REST 中使用其他 HTTP 动词,如 HEAD、PATCH 和 OPTIONS。REST 是在应用程序之间共享数据的众多方式之一,因此,如果您选择直接使用其他协议,如 TCP、SOAP 或 FTP 进行通信,则您可能并不总是需要设置 REST 服务。但是使用 REST 有以下优点:• REST 通常只有一个很小的开销(overhead)。它通常使用 JSON,这是一个轻量级的数据包装器。JSON 使用标签标识数据,但标签未在正式模式定义中指定,也没有显式的数据类型。REST 通常比 SOAP 更易于使用,SOAP 使用 XML 且开销(overhead)更大。• 通过在 REST 中定义一个接口,可以很容易地将客户端和数据库服务器之间的依赖关系降到最低。这使您可以在不影响数据库实现的情况下更新用户界面。您还可以在不影响用户界面或访问 REST API 任何外部应用程序的情况下更新数据库实现(database implementation)。 2 对 InterSystems IRIS 的 REST 调用 在定义 REST 接口之前,您应该了解 REST 调用如何流经 InterSystems IRIS。首先,考虑 REST 调用的部分,如:GET http://localhost:52773/rest/coffeemakerapp/coffeemakers 这由以下部分组成:• GET——这是 http 动词。• http:——这指定了通信协议。• //localhost:52773——这指定了托管 REST 接口的 InterSystems IRIS 实例的服务器和端口号。 如何在 InterSystems IRIS 中定义 REST 接口 • /rest/coffeemakerapp/coffeemakers——这是 URL 的主要部分,标识了 REST 调用所指向的资源。在下面的讨论中,URL 指的是 REST 调用的这一部分。 注意: 尽管这个技术概要(First Look)使用安装了 InterSystems IRIS 的 Web 服务器(在本例中是在本地系统的端口 52773 上),但对于部署的任何代码,您都应该使用一个商业 Web 服务器来替换它。安装 InterSystems IRIS 的 web 服务器仅供开发代码时临时使用,并不具备商业 web 服务器的强大功能。 当客户端应用程序进行 REST 调用时:1. InterSystems IRIS 将它指向与 URL 对应的 web 应用程序。例如,以 /coffeemakerapp 开头的 URL 将被发送到处理咖啡机的应用程序,以 /api/docdb 开头的 URL 将被发送到处理文档数据模型(Document Data Model)的 web 应用程序。2. Web 应用程序根据 HTTP 动词(verb)和 URL 中标识 web 应用程序的部分之后的任何部分,将调用指向一个方法。它通过将动词(verb)和 URL 与称为 URLMap 的结构进行比较来实现这一点。3. 该方法使用 URL 来标识 REST 调用所指定的资源,并根据动词(verb)来执行操作。例如,如果动词(verb)是 GET,该方法返回关于资源的一些信息;如果动词(verb)是 POST,该方法创建一个新的资源实例;如果动词(verb)是 DELETE,该方法删除指定资源。对于 POST 和 PUT 动词(verb),通常有一个提供更多信息的数据包。4. 该方法执行所请求的操作,并向客户端应用程序返回一个响应信息。 REST 调用需要执行方法和访问数据所需的任何权限。用户或 web 应用程序都可以拥有这些权限。您可以把这些权限分配给角色,然后把角色分配给用户或 web 应用程序。在这个示例中,%All 角色被分配给 web 应用程序。这允许未知用户(未经身份认证)访问 coffeemakerapp 数据。如果您没有把这个角色分配给 web 应用程序,未知用户将收到 401 未经授权的错误。您必须在 REST 身份认证调用中指定一个有足够权限的用户。详情请参见 "Securing REST Services(《确保 REST 服务》)"。 3 如何在 InterSystems IRIS 中定义 REST 接口在 InterSystems IRIS 中,有两种方法来定义 REST 接口:• 定义 OpenAPI 2.0 规范,然后使用 API 管理(API Management)工具来生成 REST 接口的代码。• 手动编码 REST 接口,然后在管理门户(Management Portal)中定义一个 web 应用程序。 本技术概要(First Look) 展示了如何手动编码 REST 接口,包括开发一个调度类(dispatch class)。如果您更喜欢使用规范优先的方法,这些调度类(dispatch class)是自动生成的,不应该被编辑。使用规范优先定义的优点包括能够从规范自动生成文档和客户端代码,但您可以使用任何一种方式来定义 REST 接口。有关使用规范定义 REST 接口的更多信息,请参见 Creating Rest Services(《创建 REST 服务》)。 4 手动编码 REST 接口 手动编码 REST 接口包括以下步骤:• 创建 %CSP.REST 的子类并定义 UrlMap。 手动编码 REST 接口 • 对处理 REST 调用的方法进行编码。• 定义 web 应用程序——您通常使用管理门户(Management Portal)来执行此操作。 本技术概要(First Look)使用了一个访问咖啡机数据库的示例应用程序 coffeemakerapp 来演示如何手动编码 REST 接口。Coffeemakerapp 提供 REST 接口来获取咖啡机的信息、在数据库中创建新的记录、更新现有的记录或删除记录。以下几节通过注释探讨了此示例应用程序的类定义和一些方法,以增强您对代码的理解。您将在完成本技术概要(First Look) 的练习部分后,从 GitHub 下载整个应用程序的代码。 4.1 创建 %CSP.REST 的子类并定义 URLMap 这是 demo.CoffeeMakerRESTServer 类定义的第一部分。它扩展了 %CSP.REST 类。 Class Demo.CoffeeMakerRESTServer Extends %CSP.REST{Parameter HandleCorsRequest = 1;XData UrlMap [ XMLNamespace = "http://www.intersystems.com/urlmap" ]{<Routes><Route Url="/test" Method="GET" Call="test"/><Route Url="/coffeemakers" Method="GET" Call="GetAll" /><Route Url="/coffeemaker/:id" Method="GET" Call="GetCoffeeMakerInfo" /><Route Url="/newcoffeemaker" Method="POST" Call="NewMaker" /><Route Url="/coffeemaker/:id" Method="PUT" Call="EditMaker" /><Route Url="/coffeemaker/:id" Method="DELETE" Call="RemoveCoffeemaker"/></Routes>} 查看路由(Route)要素。每个都有三个属性:• Url——这标识了可由该路由(Route)处理的 REST URL。由于 IRIS 指向以 /rest/coffeemakerapp 开头的 URL,因此这个属性指定了紧随其后的 URL 部分。如果 Url 属性是 /cof- feemakers,则此路由( Route)处理以 /rest/coffeemakerapp/coffeemakers 开头的 URL。• Method(方法)——这标识了路由(Route)处理的动词。请注意,最后两行的 Url 值相同,/coffeemaker/:id。有 PUT 方法的路由(Route)将处理以 /rest/coffeemaker- app/coffeemaker/:id 开头的 URL 的 PUT 动词,有 DELETE 方法的路由(Route)将处理具有相同开头的 URL 的 DELETE 动词。• Call(调用)——指定要调用的方法来处理这个 REST 调用。该方法会被传递完整的 URL 和任何数据,以便它可以根据 URL 做出响应。 Url 值中以 : 开头的部分表示通配符。也就是说,/coffeemaker/:id 将匹配 /coffeemaker/5,/coffeemaker/200,甚至是 /coffeemaker/XYZ。被调用的方法将在参数中获得 :id 的值。在这种情况下,它标识了要更新(使用 PUT)或删除的咖啡机的 ID。Url 值有额外的选项,允许您将 REST URL 转发到 %CSP.REST 子类的另一个实例,但您不需要在这个技术概要(First Look)中处理它。HandleCorsRequest 参数指定浏览器是否应该允许跨源资源共享(Cross-origin Resource Sharing,CORS),即在一个域中运行的脚本试图访问在另一个域中运行的 REST 服务,但这也是一个高级主题。 4.2 编码方法 GetAll 方法检索有关所有咖啡机的信息。以下是其代码: 手动编码 REST 接口 ClassMethod GetAll() As %Status{Set tArr = []Set rs = ##class(%SQL.Statement).%ExecDirect(,"SELECT * FROM demo.coffeemaker") While rs.%Next() {Do tArr.%Push({"img": (rs.%Get("Img")), "coffeemakerID": (rs.%Get("CoffeemakerID")), "name": (rs.%Get("Name")),"brand": (rs.%Get("Brand")),"color": (rs.%Get("Color")),"numcups": (rs.%Get("NumCups")),"price": (rs.%Get("Price"))})}Write tArr.%ToJSON() Quit $$$OK} 此方法的注意事项:• 没有任何参数。每当这个方法被调用时,它就会执行一个 SQL 语句,从 demo.coffeemaker 数据库中选择所有记录。• 对于数据库中的每条记录,它都会将这些值作为名称、值对附加到数组中。• 它将数组转换为 JSON,并通过将 JSON 编写到 stdout 来将 JSON 返回给调用的应用程序。• 最后,它成功退出。 NewMaker() 方法没有参数,但有一个 JSON 有效负荷,用于指定要创建的咖啡机。以下是其代码:ClassMethod NewMaker() As %Status{If '..GetJSONFromRequest(.obj) {Set %response.Status = ..#HTTP400BADREQUEST Set error = {"errormessage": "JSON not found"} Write error.%ToJSON()Quit $$$OK}If '..ValidateJSON(obj,.error) {Set %response.Status = ..#HTTP400BADREQUEST Write error.%ToJSON()Quit $$$OK}Set cm = ##class(demo.coffeemaker).%New() Do ..CopyToCoffeemakerFromJSON(.cm,obj)Set sc = cm.%Save() Set result={}do result.%Set("Status",$s($$$ISERR(sc):$system.Status.GetOneErrorText(sc),1:"OK")) write result.%ToJSON()Quit sc} 此方法的注意事项:• 首先,它通过调用类中稍后定义的 GetJSONFromRequest() 和 ValidateJSON() 方法,测试有效负荷是否包含有效的 JSON 对象。• 然后它使用 JSON 对象创建一个新的 demo.coffeemaker,将记录保存在数据库中。• 它通过将状态写入 stdout 来返回状态。 最后,RemoveCoffeemaker() 方法展示了 Url 的:id部分是如何作为参数传递给该方法的: 为自己定义 REST 接口 ClassMethod RemoveCoffeemaker(id As %String) As %Status{Set result={}Set sc=0if id'="",##class(demo.coffeemaker).%ExistsId(id) { Set sc=##class(demo.coffeemaker).%DeleteId(id)do result.%Set("Status",$s($$$ISERR(sc):$system.Status.GetOneErrorText(sc),1:"OK"))}else {do result.%Set("Status","")}write result.%ToJSON() quit sc} 总之,由路由调用(Route Call)属性指定的方法通过以下方式处理 REST 调用:• 获得任何参数作为调用参数。• 通过 obj 值访问有效负荷。• 通过将响应写入 stdout,将其返回给客户端应用程序。 5 为自己定义 REST 接口本节逐步向您展示如何使用咖啡机应用程序来处理 REST 调用。与其让您编写 REST 接口的代码,不如从 GitHub 下载完成的应用程序。构建应用程序后,您将定义 Web 应用程序,然后通过 REST 调用测试应用程序。 5.1 用前须知要使用这个程序,您需要在一个系统上工作,安装一个 REST API 应用程序(如 Postman、Chrome 高级 REST 客户端(Chrome Advanced REST Client)或 cURL),并连接一个正在运行的 InterSystems IRIS 实例。您对 InterSystems IRIS 的选择包括多种类型的已授权的和免费的评估实例;该实例不需要由您正在工作的 系统托管(尽管它们必须相互具有网络访问权限)。关于如何部署每种类型的实例的信息(如果您还没有可使用的实例),请参见 InterSystems IRIS Basics: Connecting an IDE(《InterSystems IRIS 基础:连接一个 IDE》)中的 Deploying InterSystems IRIS(部署 InterSystems IRIS)。有关将 REST API 应用程序连接到您的 InterSystems IRIS 实例所需的信息,请参见同一文档中的 InterSystems IRIS Connection Information(InterSystems IRIS 连接信息)。 5.2 下载示例应用程序通过克隆或下载已完成的 coffeemakerapp 应用程序来开始这个练习,该应用程序包括测试对 InterSystems IRIS 的 REST 调用所需的所有 REST 接口。这个技术概要-REST(FirstLook-REST)示例代码可以在以下网站获得:https://github.com/intersystems/FirstLook-REST。从 GitHub 下载的内容必须能从您的 InterSystems IRIS 实例访问。下载文件的程序取决于您所使用的实例类型,如下所示:• 如果您使用的是 ICM 部署的实例:1. 使用带有 -machine 和 -interactive 选项的 icm ssh 命令,在托管实例的节点上打开默认 shell,例如:icm ssh -machine MYIRIS-AM-TEST-0004 -interactive 为自己定义 REST 接口 2. 在 Linux 命令行上,使用以下命令之一将 repo 克隆到实例的数据存储卷(data storage volume)。例如,对于部署在 Azure 上的配置,数据卷的默认挂载点(default mount point)是 /dev/sdd,因此您可以使用如下命令: $ git clone https://github.com/intersystems/FirstLook-REST /dev/sdd/FirstLook-REST OR$ wget -qO- https://github.com/intersystems/FirstLook-REST/archive/master.tar.gz | tar xvz -C/dev/sdd 这些文件现在对容器文件系统上 /irissys/data/FirstLook-REST 中的 InterSystems IRIS 可用。 • 如果您正在使用通过其他方式部署的容器化实例(授权版或社区版(Community Edition)):1. 在主机上打开 Linux 命令行。(如果您在云节点上使用社区版(Community Edition),请使用 SSH 连接该节点,如在 Deploy and Explore InterSystems IRIS(《部署和探索 InterSystems IRIS》) 中所述。)2. 在 Linux 命令行上,使用 git clone 或 wget 命令,如上所述,将 repo 克隆到容器中挂载为卷的存储位置。– 对于社区版(Community Edition)实例,您可以克隆到实例的持久化 %SYS 目录 (存储指定于实例的配置数据的目录)。在 Linux 文件系统中,这个目录是 /opt/ISC/dur。这使得文件对容器文件系统上 /ISC/dur/FirstLook-REST 中的 InterSystems IRIS 可用。 – 对于已授权的容器化实例(containerized instance),选择容器中作为卷挂载的任何存储位置(如果使用它,包括持久化 %SYS 目录)。例如, 如果您的 docker run 命令包含选项 -v /home/user1:/external,并且您将 repo 克隆到 /home/user1,则文件对容器文件系统上 /external/FirstLook-REST 中的 InterSystems IRIS 可用。 • 如果您使用的是 InterSystems 学习实验室(Learning Labs)实例:1. 在集成 IDE 中打开命令行终端。2. 将目录更改为 /home/project/shared 并使用 git clone 命令克隆 repo: $ git clone https://github.com/intersystems/FirstLook-REST 该文件夹被添加到左边资源管理器(Explorer)面板的 Shared (共享)下,并且该目录对 /home/project/shared 中的 InterSystems IRIS 可用。• 如果您使用的是已安装的实例:– 如果实例的主机是安装了 GitHub 桌面(GitHub Desktop)和 GitHub 大文件存储(GitHub Large File Storage)的 Windows 系统:1. 在主机的 web 浏览器中进入 https://github.com/intersystems/FirstLook-REST。2. 选择 Clone or download(克隆或下载) 然后选择 Open in Desktop(在桌面上打开)。 这些文件对您的 GitHub 目录中的 InterSystems IRIS 可用,例如在 C:\Users\User1\Documents\GitHub\FirstLook-REST 中。– 如果主机是 Linux 系统,只需在 Linux 命令行上使用 git clone 命令或 wget 命令,就可以将 repo 克隆到您所选择的位置。 5.3 创建一个支持互操作性的命名空间如果您已经有了一个互操作性命名空间,就可以使用它。否则,创建一个命名空间:1. 使用 InterSystems IRIS Basics:Connecting an IDE(《InterSystems IRIS 基础:连接一个 IDE》)中为您的实例描述的 URL,在浏览器中打开您的实例的管理门户(Management Portal)。 为自己定义 REST 接口 2. 通过导航到命名空间(Namespaces)页面(System Administration(系统管理) > Configuration(配置) > System Configuration(系统配置) > Namespaces(命名空间)), 并按照 System Administration Guide(《系统管理指南》)的“Configuring InterSystems IRIS(《配置 InterSystems IRIS》)”一章中创建/修改命名空间中的使用新命名空间(New Namespace)页面的说明,点击 Create New Namespace(创建新命名空间)按钮,创建一个支持互操作性的命名空间。 5.4 构建示例代码 要构建示例代码,请按照以下步骤操作1. 使用 InterSystems IRIS Basics:Connecting an IDE(《InterSystems IRIS 基础:连接一个 IDE》)中为您的实例描述的程序,打开 InterSystems 终端(Terminal)。2. 将命名空间设置为您将使用的互操作性命名空间。输入以下命令,替换 <Namespace-name> 与您所使用的命名空间。set $namespace = "<namespace-name>" 3. 输入以下命令,替换 <path> 与您下载示例的路径。 该目录包含许可证(License)和 Readme 文件以及 buildsample 目录:do $system.OBJ.Load("<path>/buildsample/Build.RESTSample.cls","ck") do ##class(Build.RESTSample).Build()4. 当出现提示时,输入下载此示例的目录的完整路径。然后,该方法加载和编译代码,并执行其他需要的设置步骤。注意: FirstLook-REST / README.md 文件包含这些说明的一个版本。5.5 定义一个 web 应用程序 现在您已经使用 REST 接口构建了示例应用程序,您需要定义一个 web 应用程序:1. 使用 InterSystems IRIS Basics:Connecting an IDE(《InterSystems IRIS 基础:连接一个 IDE》)中为您的实例描述的 URL,在浏览器中打开您的实例的管理门户(Management Portal)。2. 选择 System Administration(系统管理) > Security(安全) > Applications(应用程序) > Web Applications(web 应用程序)。3. 选择 Create New Web Application(创建新的 web 应用程序) 并输入以下设置• Name(名称): /Rest/coffeemakerapp——它指定了将由这个 web 应用程序处理的 URL。InterSystems IRIS 会将所有以 /rest/coffeemakerapp 开头的 URL 定向到这个 web 应用程序。• Namespace(命名空间): 您创建的支持互操作性的名称空间的名称。• Enable(支持): 选择 REST。• Dispatch Class(调度类): Demo.CoffeeMakerRESTServer——这是定义了 URLMap 的类。• Security Settings/Allowed Authentication Methods(安全设置/允许的身份认证方法): 同时选择 Unauthenticated(未经身份认证) 和 Password(密码) 复选框。4. 选择 Save(保存)。5. 要允许此示例的未经身份认证的访问,必须赋予 web 应用程序 %All 角色。要执行此操作:a. 选择 Save(保存)后,选择 Application Roles(应用程序角色)标签。b. 从 Available(可用的)角色中选择 %All 角色。c. 点击右箭头(选择)按钮,将 %All 角色移动到 Selected(选定的) 角色。 为自己定义 REST 接口 d. 点击 Assign(分配) 按钮。e. %All 角色现在被列为 Application Role(应用程序角色)。这确保了来自未经身份认证用户的 REST 调用将拥有访问 coffeemakerapp 数据所需的权限。如果没有这个角色,REST 调用将必须为拥有足够权限的用户指定身份认证。 5.6 访问 REST 接口 CoffeeMaker REST 应用程序现在可以工作了。您将输入 REST 命令来访问咖啡机数据库。在您的 REST API 工具(如 Postman)中,按照以下步骤操作:1. 指定 REST POST 请求来添加新的咖啡机,必要时使用您的 InterSystems IRIS 实例的指定信息• HTTP 操作: POST• URL: http://server:port/rest/coffeemakerapp/newcoffeemaker,其中服务器和端口是您的实例的主机标识符和 web 服务器端口。• 您的实例的登录凭证。• 输入数据: {"img":"img/coffee3.png","coffeemakerID":"99","name":"Double Dip","brand":"Coffee+", "color":"Blue","numcups":2,"price":71.73} 尽管数据包含一个 coffeemakerID 的值,但这是一个计算字段,并且在添加记录时会分配一个新值。该调用返回成功状态:{"Status":"OK"} 2. 重复上一步骤两次,添加以下两个咖啡机: {"img":"img/coffee4.png","coffeemakerID":"99","name":"French Press","brand":"Coffee For You", "color":"Blue","numcups":4,"price":50.00}{"img":"img/coffee9.png","coffeemakerID":"99","name":"XPress","brand":"Shiny Appliances", "color":"Green","numcups":1,"price":95.00} 3. 使用相同的实例指定信息,指定一个 REST GET 请求,以获得数据库中的咖啡机列表:• HTTP 操作: GET• URL: http://server:port/rest/coffeemakerapp/coffeemakers• 您的实例的登录凭证。 该调用返回一个咖啡机列表,如: [{"img":"img/coffee3.png","coffeemakerID":"1","name":"Double Dip","brand":"Coffee+", "color":"Blue","numcups":2,"price":71.73},{"img":"img/coffee4.png","coffeemakerID":"2","name":"French Press","brand":"Coffee For You", "color":"Blue","numcups":4,"price":50},{"img":"img/coffee9.png","coffeemakerID":"3","name":"XPress","brand":"shiny Appliances", "color":"Green","numcups":1,"price":95}] 4. 指定以下 REST 调用,删除 ID=2 的咖啡机:• HTTP 操作:DELETE• URL: http://server:port/rest/coffeemakerapp/coffeemaker/2• 您的实例的登录凭证。 为自己定义 REST 接口 该调用返回成功状态: {"Status":"OK"} 5. 重复 REST GET 请求。该调用返回一个咖啡机列表,如: [{"img":"img/coffee3.png","coffeemakerID":"1","name":"Double Dip","brand":"Coffee+", "color":"Blue","numcups":2,"price":71.73},{"img":"img/coffee9.png","coffeemakerID":"3","name":"XPress","brand":"Shiny Appliances", "color":"Green","numcups":1,"price":95}] 5.7 记录 REST 接口 当您向开发者提供 REST 接口时,您应该提供文档,以便他们知道如何调用接口。您可以使用 Open API Spec 来记录 REST 接口,并使用工具,如 Swagger 来编辑和格式化文档。InterSystems 正在开发一项功能来支持这个文档。这个版本包含了 API 管理(API Management)中的一个功能,它可以为您的 REST API 生成文档框架。您仍然需要编辑生成的文档,以添加注释和额外的信息,例如参数和 HTTP 返回值的内容。要为 CoffeeMakerApp REST 示例生成文档,请输入以下 REST 调用,使用您的 InterSystems IRIS 实例的指定信息和您创建的命名空间的名称:• HTTP 操作:GET• URL: http://server:port/api/mgmnt/v1/namespace/spec/rest/coffeemakerapp/• 您的 InterSystems IRIS 实例的登录凭证。 您可以把这个调用的输出粘贴到 swagger 编辑器中。它将 JSON 转换为 YAML(另一种标记语言(Yet Another Markup Language))并显示文档。您可以使用 swagger 编辑器向文档中添加更多信息。Swagger 编辑器显示的文档如下所示: 有关 InterSystems IRIS 和 REST 的更多信息 6 有关 InterSystems IRIS 和 REST 的更多信息 有关在 InterSystems IRIS 中创建 REST 服务的更多信息,请参见以下内容:• Setting Up RESTful Services(设置 RESTful 服务) 是 InterSystems 的在线课程,它使用与本文档相同的咖啡机应用程序,但更详细。 您需要登录 learning.intersystems.com 才能参加这个课程。如果您没有账户,可以创建一个。• Creating REST Services(《创建 REST 服务》)• Using REST Services and Operations in Productions(《在产品中使用 REST 服务和操作》)
文章
Michael Lei · 十一月 9, 2021

转发InterSystems全球副总裁精彩博客--客户至上理念如何推动卓越与创新?

照顾好我们的客户并打造伟大的产品。这是我们InterSystems自从1978年成立以来的座右铭。40多年很快过去,这始终驱动着我们前进。过去是,未来也将一直是我们的基因。 追求卓越和持续提升的价值观是我们的“客户至上”方法取得成功的关键。这也是过去三年我们一直积极参与Gartner及其Peer Insights项目的原因,该项目主要征求最终用户的反馈意见和对厂商的打分。InterSystems连续两年荣膺Peer Insights运营数据库管理系统(ODBMS)客户选择奖,而且是该类别评分最高的供应商之一。 此外,InterSystems还入选Gartner目前推出的Gartner“客户至上”计划,并获得相应标识(上图),这一计划认可了我们为客户提供的坦诚分享、公正反馈的环境,根据用户反馈,我们不断改善产品和服务,从而最大程度地满足不断变化的客户需求。简言之,我们没有人为影响客户在Gartner网站上对我们的评价。这体现了我们对客户的承诺以及开放沟通渠道的重要性。InterSystems IRIS®数据平台是Gartner最新的云数据库管理系统魔力象限(Cloud Database Management Systems Magic Quadrant)中唯一获得此标识的产品,我们对此深感自豪。 以客户为中心的基因DNA 在我尚未加入InterSystems前,作为一家医学实验室的IT人员,我曾体验到InterSystems的“客户至上”。当时,我们还与另一家供应商合作,但他们的支持服务并不理想——我们需要等待很长时间;需要向很多人重复说明问题,而这些人并不能解决问题;我们还需要不断进行身份验证。他们的服务与其说是提供帮助,不如说是让人筋疲力尽,我认为他们从未将我的利益放在心上。 而InterSystems提供了天壤之别的客户体验。我第一次遇到问题呼叫InterSystems支持中心时,一位名叫Terry的人立刻接通了电话。他没有让我验证身份,而是让我解释了一下问题,并一直在线提供支持直到问题得到解决。当时我并不知道这位Terry居然就是InterSystems的创始人兼首席执行官(现在仍是)。显然,对InterSystems来说,最重要的事情是解决问题、让客户满意。尽管现在许多事情已经发生了改变(我们现在有不同人员负责处理服务请求了),但我们依然坚持这一理念。(注明:作者在1984年加入Intersystems 公司,这段故事发生在将近40年前公司刚刚成立的时候) 每周一上午,InterSystems总部都会召开高管例会,然而会议重点并非是公司的销售指标或收入,而是探讨如何通过改善产品、业务模式和支持服务来增强客户的成功。会议至少有一半的时间是在讨论客户支持服务——哪位客户提出了问题、我们是如何解决的、客户是否满意等等。 我们将客户支持作为我们的一大亮点,因为它能够彰显我们帮助客户成功的热情。如果我们能够助力客户实现自身目标,这比签署了一笔大单更有价值。这能够培育不可复制的客户信任。我们现在的客户留存率在99%以上,结果不言自明。 “如果你不清楚应该帮助客户还是帮助公司,那么请选择帮助客户。”——Terry Ragon,InterSystems CEO 未来之路 我们对获得Gartner的“客户至上”标识深感自豪。对我们而言,这不仅是又一次的认可,更是一个信号——我们正沿着正确的方向为用户提供卓越客户服务,其结果和客户口碑能说明一切。 借助Gartner的帮助,我们希望继续努力提供尽可能最佳的客户支持,我们的员工配备也反映了这一目标。我们授予支持团队与工程师和开发人员同等重要的权利,这样他们就可以为客户做出重要决定,而不必来回请示。对InterSystems而言,这不仅是客户支持的未来之路,也是我们提供最佳产品和服务的前进方向。 注:作者John Paladino是InterSystems负责客户服务的全球副总裁。自1984年加入InterSystems以来,他帮助开发了自动化支持跟踪系统和针对所有InterSystems软件产品的客户培训项目,制定并实施了旨在改善响应性、提高客户满意度的服务标准,并主导多个国内外团队建设计划。在加入InterSystems之前,他曾在New England Pathology担任三年的系统工程经理,负责获取、实施和管理各类信息技术。Paladino曾在伍斯特理工学院和马萨诸塞大学卢维尔分校攻读电机工程专业。 博客原文:How Can Putting the Customer First Drive Excellence and Innovation? 微信公众号原文:深深植根于InterSystems的DNA中,客户至上理念如何推动卓越与创新? 欢迎大家参与评审给我们提供宝贵意见:参与gartner-peer-insights同业评审,赢取价值25美元的礼品卡
文章
Nicky Zhu · 八月 10, 2022

InterSystems 数据平台互操作功能运行维护管理基础 - Production管理与优化

Production是开发者基于InterSystems IRIS产品家族实现互操作功能的主要技术架构。而每个Production的运行依赖于三类主要的业务组件: 业务服务(Business Service) 业务流程(Business Process) 业务操作(Business Operation) 三类组件间以消息作为信息交换的主要媒介。因此,要保障互操作业务的稳定运行,除保障软件平台有充足的软硬件资源运行外,保障Production中消息的高效传输以及三类组件的平稳、高效运行则是运维团队面临的主要目标。下面列举的若干项任务则有助于运维团队实现这些目标: 优化业务组件运行效率 Production中的业务组件都有运行池的概念,允许业务组件运行在多个进程上并发工作以提高整体性能。运行池又可以分为私有池和参与者池两类。 私有运行池 业务组件的CPU作业的数量 默认为1即可工作 通过性能测试确定最佳的池大小 拥有私有池的业务组件从私有队列中处理消息 无适配器业务服务的池大小需设置为0 需要多进程时,池大小设为1可以保证信息以先进先出(FIFO)顺序处理 整个消息流中的所有业务组件的池大小都不应大于1 参与者池 Production级的参与者池设置 用于处理公共队列中的请求的CPU作业的数量 由使用公共队列的所有业务流程共享。 业务流程和业务操作的池大小设置。 0 - 使用参与者池和公共队列 非0--使用私人队列,有指定的CPU作业数 对于FIFO,设置池大小=1 参与者池与业务流程的启停 任何一个池大小为0(仅使用公有池)的业务流程被禁用时,所有的参与者都不会启动 防止任何池大小为0的业务组件从公共队列中处理消息 要保障其他参与者正常工作 将要被禁用的组件的Pool Size改为大于0并应用 如果已经禁用,要启动其他参与者 保持组件禁用,但将其Pool Size为大于0并应用 Production的运行维护 Production本身具备一系列的运行状态管理功能,帮助运维团队在生产环境上处理Production运行的一系列任务,包括 Production运行维护 – 自动启动 Ensemble > 管理 自动启动Production > 选择要自动启动的Production 在Ensemble启动时自动启动Production 如果需要的话,会自动调用RecoverProduction()方法,恢复Production状态 系统级启用开关 系统管理 > 配置 > 其他设置 > 启动 > EnsembleAutoStart 默认情况下为 "真"。 在排除故障时,可能需要禁用。 Production运行维护 – 强制关闭 如果Production关闭得太慢 等待关闭超时 显示强制关闭按钮 Production运行维护 – 更新 运行的组件与它的底层类代码不同步,需要Production来更新 业务组件进程因错误而意外关闭,需要重新启动 设置的改变需要业务组件重新启动 更新按钮出现在Production配置页面。 Production运行维护 – 恢复 在非正常关机和一些错误之后出现 如果需要,自动启动Production将在启动前自动恢复 Production运行维护 – 中断 在配置页面上显示 Production被关闭时如果满足如下条件之一: 有同步消息在等待响应。 或者,队列非空,意味着Production还没处理完消息 或者,消息仍在被组件处理 或者,有级别较高的告警请求 Production中断后的选项 重新启动Production以完成消息的处理 如果启动后消息仍然被组件不断重试(提示故障未排除) 修复导致重试的问题 挂起消息 中断消息 在测试系统中(决不能是生产系统),如果你决定必须完全清除所有队列,请调用Ens.Director类的CleanProduction()方法 do ##class(Ens.Director).CleanProduction() Production的配置 Production本身是基于InterSystems的大量预制组件经开发或直接部署产生的软件包,因此将包含大量不需要在代码中固定写好的配置项,例如远程连接的地址,扫描数据表使用的SQL等。运维团队的工作之一,就是在外部环境发生变化,例如第三方系统部署地址发生变化时,通过调整Production的设置来应对变化,而无需更改已部署的代码。 凭据 用于登录远程系统。 在出站适配器的凭据设置中指定 在Ensemble > 配置 > 凭据中设置 Production配置数据的来源 导出Production 配置页 > Production设置 > 操作选项卡 > 导出 创建与类/部件相关的XML文件 验证是否需要额外的类 可用于 部署到另一个实例中 部署到另一个命名空间 保存Production 部署Production Ensemble à 管理 à 部署变化 à 部署 会创建回滚文件及部署日志 导出与部署的当前限制 除业务组件外,组件配置、规则、DT等组件运行依赖的部件不会自动被识别,需要人工选取并导出 DSN, 凭据等含安全信息的配置不会被导出(实例级配置,非Production级配置),需要手动在部署端配置。产品化研发中需要考虑通过自定义任务定期同步到备机或其他实例。 清除Production数据 当Production在生产中运行较长时间后,往往会累积大量的告警,事件日志和互操作消息等运行数据。用户可选择定期删除一部分不再需要的数据,保证磁盘空间的充裕。 直接执行 Ensemble > 管理 > 清除管理数据 直接执行清除任务受限于浏览器进程超时设置,如在清除过程中触发超时,将导致清除进程失效退出,中断消息清除过程。 对于大量消息累积的场景,请使用后台任务周期性执行确保清除效果。由于删除数据时也将产生大量的journal,删除前应在journal所在存储上保留充裕的空间。 后台任务 系统操作 > 任务管理器 > 新任务 空闲超时设置 组件被标记为不活动,如果 组件在给定的#秒内没有活动 黄色表示不活动的组件 Production监控 配置页面 Production运行监控 IRIS平台为用户提供了一系列工具展现组件的运行状态。运维团队需要频繁地使用这些工具用于定位和诊断故障,识别性能瓶颈等。 Production监视器 一页式监控业务服务、业务操作、队列和错误。 点击单个组件了解详情 提供对队列和事件日志的访问 事件日志 检索、过滤和清除日志 联合使用事件日志和可视化追踪 错误、警报和警告事件日志条目显示在可视化追踪中 点击图标可以查看事件日志详情 业务规则日志 Ensemble > 查看 > 业务规则日志 每次尝试匹配规则时的条目 空原因表示没有匹配的规则 日志内容 I/O 记录 组件配置 除了Production信息外,还对输入和输出数据进行存档 TCP输入输出 文件输入输出 输入和输出数据显示在可视化跟踪窗口中 组件状态指示器 状态指示器提供业务组件健康状况的可视化 绿色 – 正常 黄色 – 不活动(项目空闲时间超过其空闲超时设置) 红色 - 错误。 灰色 - 禁用。 暗绿色或暗灰色 -- Production目前没有运行 紫色 - 遇到失败但在重试的信息 消息队列 要查看队列 Ensemble > 监视器 > 队列 > 在队列中点击消息查看详情 Production配置页 > 选择组件 > 队列Tab 组件在给定的#秒内没有活动 可以查看、挂起和中断整个队列的消息处理 消息队列与性能调优 消息队列的变化常常可以用于评估组件性能及识别性能瓶颈,运维团队可以根据瓶颈所在位置和上下游组件队列的变化情况调整Pool Size获得优化性能。 忽然暴增的队列 如果发生在业务操作 通常由外部系统导致 连接中断 暴增的业务高峰 如果发生在业务流程 业务流程可能出现错误,导致它以某种方式 "卡住” 长期堆积的队列 如果发生在业务操作 可能是在向外部应用程序发送消息时出现持续延迟 看看外部系统的性能问题是否能得到解决。 如果不能,考虑增加池子的大小设置(除非需要FIFO) 如果发生在业务流程 考虑增加池(私有池或参与者池)大小的设置(除非需要FIFO) 可能有算法缺陷,考虑优化 多个队列长期堆积 潜在的原因 主机容量不够(内存/CPU/IO) Ensemble需要更多的资源(内存、CPU) 底层的Caché实例可能需要调整(内存、IO) 永远不要低估低速存储的影响 作业 提供关于CPU作业运行配置项的信息 Job # = 操作系统进程ID 状态 Dequeuing - 等待信息被处理(最常看到的) Running - 工作,重试或无限循环 Quiescent - 生产关闭期间的状态 Error 能够按照日程周期运行的作业非常适合用于执行系统运维任务 周期性清除过期Production数据 按照备份策略周期性执行全量、累积和增量备份 定期清除过期的数据库journal 定期对数据库进行一执行检查 定期对数据库执行压缩截断释放空间(慎用) 定期将无法通过mirror同步的配置推送到备机(例如DSN、凭据等) 活动量监控 活动量和持续时间监视器显示每个接口的统计数据 Ensemble > 监视器 > 活动量和持续时间。 按时间段过滤(分钟、小时、天、周、月、年) 可以根据需要建立自己的仪表盘来显示所需数据 也可通过SQL查询 启用活动量收集 将Ens.Activity.Operation.Local Business Operation添加到Production中 为统计数据指定存储命名空间 为Production启用统计数据收集 do ##class(Ens.Util.Statistics).EnableStatsForProduction() 为命名空间的Web应用程序启用DeepSee 系统管理 > 安全 > 应用程序 > Web应用程序 使用PurgeActivityData任务进行清理。 使用作业控制周期任务
文章
Nicky Zhu · 八月 10, 2022

InterSystems 数据平台互操作功能运行维护管理基础 - 互操作告警与通知

在互操作功能运行过程中,IRIS可以识别异常情况的发生,自动生成告警事件并通过预制的互操作组件将告警转发给干系人;另外,IRIS也内建了告警工作台,可供运维团队基于工作流实现对告警的管理。 告警设置与转发 Production告警设置 在Production级别可以设置对连接异常的告警设置,及告警组件设置 组件告警设置 控制何时触发警报。 队列计数警报。 队列中的信息数量 队列等待警报。 消息在队列中停留的秒数 不活动超时。 业务组件不活动的秒数 在v2012.1之前,不会触发告警 警报宽限期 与外部连接有关的错误可以有宽限期设置 设置重新建立连接的时间,超时且连接不成功才会发出告警 业务服务 警报宽限期 业务操作 再次发出警报宽限期 转发告警 EnsLib.EMail.AlertOperation 内置告警转发组件 通过Email转发 路由到其他接口转发 REST - 短信 REST - 钉钉 用户需要执行的规划 什么样的告警需要即时通知 告警的通知是否应该成为一个应用 包含告警组件的Production 包含告警组件与业务组件的Production形态如下 其中,用于告警的业务组件包括 告警托管 在互操作功能中产生的告警可以选择转发到第三方系统触发告警运维工作流,也可以在IRIS中进行托管,通过IRIS提供的较为简单的工作流定义实现对告警的统一存储及管理。 工作原理 告警管理BP 决定是否产生新的托管告警 可以设置告警的所有者 通知管理BP 决定如何发送告警 决定谁将收到通知 将告警通知发送到指定组件 告警监视BS(可选组件) 可以将过期告警升级 决定是否发出提醒 典型的告警数据流如下 其中,告警数据产生后经过平台自动丰富,最终形成可供运维人员操作的托管告警 托管告警流程 告警处理流程可能的选择: 告警仅写入日志文件,没有自动通知。 一个简单的告警通知系统,所有告警都通过Email发给用户 根据告警类型和生成告警的组件,将选定告警通过路由发送给不同的用户 实现告警管理框架,用于发送告警通知、升级未解决的告警,并记录当前状态和为解决告警而采取的操作的历史记录 告警路由规则 在告警管理器中可以应用规则,通过路由规则来定义告警的流向。 来源 允许根据不同的故障组件采取不同的行动 消息类 Ens.AlertRequest或无 目标 其他消息路由,用于进一步的路由 业务操作作为最终目标 可以通过数据转换根据传递目标定制告警 托管告警管理 通过Ensemble > 监视器 > 我的托管警报可进入告警管理页 该功能基于IRIS自身的用户设置。告警责任人的分配、告警级别管理、后续操作时限等,都可以在该页上进行操作。 也可通过SQL查询和操作已产生的告警 对于需要自定义工作流的用户,也可以考虑在此基础上实现流程。 祝老师您好,请问系统级别的告警如何推送,就像系统日志中告警级别为2的日志。 Hi, 有两个办法: 1. 自己写代码扫描messages.log获得这些告警 2. 扩展平台内置的告警通知类,可以自定义传感器、消息订阅器和通知分发器,参见 https://docs.intersystems.com/irisforhealth20223/csp/docbook/DocBook.UI.Page.cls?KEY=GCM_healthmon#GCM_healthmon_sysmon_userdefined 感谢祝老师的答复,第一种方法已经实现,我研究下第二种方法。
公告
Claire Zheng · 九月 4, 2022

InterSystems开发者社区中文版首届技术征文大赛正式启动!

嗨,开发者们! 秋高气爽之际,我们很高兴地宣布启动🏆InterSystems开发者社区中文版首届技术征文大赛 🏆(←点击链接进入参赛页面,浏览所有参赛文章)! 从2022年9月5日-10月24日(北京时间),欢迎热爱InterSystems技术的你来投稿,撰写与InterSystems技术相关的文章。 🎁参与奖 我们为每一位成功参赛的作者准备了礼品! 🏆优秀文章大奖 Apple iPad; Beats Fit Pro; HUAWEI WATCH; Logitech键盘; WD固态硬盘;KEEP智能手环 奖品设置 1. 专家提名奖:活动期间发布文章且成功参赛后,由InterSystems专家评选得出 🥇一等奖1名,Apple iPad 🥈二等奖2名,Beats Fit Pro 🥉三等奖3名,WD 固态硬盘 2. 开发者社区奖:活动期间发布文章且成功参赛后,由社区成员点赞评选得出,点赞前五名获得以下奖品 🥇第一名,HUAWEI WATCH 3智能手表 🥈第二名,罗技(Logitech)ERGO键盘 🥉第三名-第五名,KEEP 运动智能手环 3. 入围奖:在征文大赛期间,所有在InterSystems开发者中文社区发布文章且成功参赛的其余用户都将获得特别奖励。 请注意: 每位作者只可以获得一个奖项(即:您只可以获得一次专家提名奖/开发者社区奖/入围奖); 当出现票数相当的平手情况时,将以专家评选投票数作为最终票数高低的判断标准。 谁可以参加? 任何开发者社区成员,除了InterSystems的员工。创建一个账户! 关键时间节点 📝 2022年9月5日-10月23日(北京时间),文章发布与点赞收集!在社区发布文章,呼朋引伴来点赞。越早发布文章,就越有时间收集更多点赞(这是您获得“开发者社区奖”的关键)。 🎉 2022年10月24日(北京时间),公布获奖名单。 参赛要求 ❗️ 征文期间,发布在中文社区的文章只要满足以下要求,将自动参加比赛,无需额外提交: 文章必须与InterSystems技术有关 文章必须以中文撰写 文章必须是100%的原创文章(可以是现有文章的延续) 文章应在InterSystems开发者中文社区首发,严禁从其他社区进行搬运 文章严禁抄袭或翻译社区现有文章 社区成员可以发布多篇文章参赛 文章字数应不少于800字,写作时请关注编辑器右下角的计数器 额外奖励 您可以自由选择任何与InterSystems技术相关的主题,如果您提交的文章中符合以下要求,将获得额外奖励票数。 资源助力 ➡️ 文章样例:同一主题系列文章 数据平台与三级等保第一篇 精华文章-基于docker的一体化集成ai环境中部署机器学习深度学习模型 数据平台互操作功能运行维护管理基础-互操作消息管理 互操作进阶-第二篇:规则引擎-(第一部分) 精华文章-webgateway系列1-web-gateway介绍 ➡️ 文章样例:独立主题文章 精华文章-从软件架构发展谈业务集成技术演进与展望 精华翻译文章:什么是智慧医院数字孪生? 在intersystems-iris医疗行业版的fhir服务器上测试和开发smart-fhir应用 ➡️ 操作指导:首次加入社区,如何发帖? 如何发帖以及其他常见问题解答 如何在开发者社区上发布包含代码的帖子? 如何从 Word 或 Google Docs 发布文章 如何在帖子中突出显示 ObjectScript? 如何在其他开发者社区翻译并发布帖子 ➡️ 操作指导:更多开发者社区技能学习 【精华置顶帖】一文了解关于InterSystems开发者社区的各种积分福利! 快乐分享技术,期待您的大作!✨ 🏆InterSystems开发者社区中文版首届技术征文大赛 🏆(←点击链接进入参赛页面,浏览所有参赛文章) 如果有疑问/高见,欢迎跟帖留言!
文章
Jingwei Wang · 九月 16, 2022

.Net 应用程序连接到InterSystems IRIS数据库 - 使用.net客户端组件

连接前准备: .Net 开发环境 InterSystems IRIS 客户端组件:InterSystems.Data.IRISClient.dll,这个dll文件可以再IRIS安装包中找到,在IRIS/dev/dotnet/bin/version Connection String 步骤: 在.Net 开发环境中,配置客户端组件InterSystems.Data.IRISClient.dll作为一个dependency Connection String:其中using InterSystems.Data.IRISClient 用来导入需要使用的dll文件,string connectionString = "Server = localhost; Port = 1972; Namespace = User; User ID = SQLAdmin; Password = deployment-password"是connection string。 using System; using InterSystems.Data.IRISClient; namespace ADODemo { internal class Program { static void Main(string[] args) { string connectionString = "Server = localhost; Port = 1972; Namespace = User; User ID = SQLAdmin; Password = deployment-password"; IRISADOConnection connection = new IRISADOConnection(connectionString); connection.Open(); // when finished, use the line below to close the connection // connection.Close(); } } }
文章
Qiao Peng · 三月 29, 2021

CDC系列之四:使用DSTIME特性在InterSystems IRIS/Caché上实现CDC功能

前面介绍了通过mirroring或shadow,使用journal日志过滤器的方式,在不改动数据模型的情况下实现InterSystems IRIS/Caché上的CDC能力。但如果你可以修改InterSystems IRIS/Caché上的数据模型,也可以考虑使用DSTIME特性实现变更数据捕获。 DSTIME特性 DSTIME特性是InterSystems IRIS/Caché的嵌入式实时BI工具DeepSee用于跟踪数据变更的。InterSystems IRIS和2011版之后的Caché,都支持DSTIME特性。它会自动记录数据库中SQL表记录或持久化对象的变更,并将变更记录写入持久化的多维数组^OBJ.DSTIME中。 DSTIME特性是针对于SQL表/持久化类的,因此需要为每个需要记录变化的表/持久化类开启。当开启了DSTIME的表/持久化类,有记录插入、更新、删除时,InterSystems IRIS/Caché引擎会自动在^OBJ.DSTIME中记录这些操作。其格式为:^OBJ.DSTIME(类名,DSTIME,对象ID) = 执行的操作代码 DSTIME:当SQL表/持久化类的参数DSINTERVAL未被设置时,DSTIME=0;当SQL表/持久化类的参数DSINTERVAL被设置时,DSTIME=1840/12/31午夜12点到记录发生时的秒数。 执行的操作代码: 代码 操作 0 更新 1 插入 2 删除 例如:^OBJ.DSTIME(“Demo.Patient”,5673588714,2) = 1^OBJ.DSTIME(“Demo.Patient”,5673588735,3) = 1^OBJ.DSTIME(“Demo.Patient”,5673588784,4) = 1^OBJ.DSTIME(“Demo.Patient”,5673588791,2) = 0 因此,如果你没有使用DeepSee,就可以监控^OBJ.DSTIME即可获得IRIS/Cache’的数据变更。注:DeepSee在同步cube时,会自动查询、操作、删除^OBJ.DSTIME的数据,因此,将会对于直接使用它进行数据变更捕获产生影响。如果你的确在使用DeepSee自动同步cube,也想使用^OBJ.DSTIME实现CDC,请联系InterSystems获得解决方案。这不在本文的讨论范围。 开启DSTIME特性 InterSystems IRIS/Caché可以在SQL表建模时、对象模型上开启DSTIME特性。涉及2个类参数:DSTIME 和DSINTERVAL DSTIME 类参数设置为AUTO时,InterSystems IRIS/Caché会自动记录对应SQL表/持久化类的数据变更。DSINTERVAL类参数控制记录的数据变更如何分组。用于实现CDC时,建议设置该参数=1。 1. 通过SQL建模,新建SQL表 在使用SQL新建表时,可以增加对于类参数DSTIME 和DSINTERVAL的设置。 例如下面的SQL创建一个开启了DSTIME的表Demo.Patient: CREATE TABLE Demo.Patient ( %CLASSPARAMETER DSTIME = 'AUTO', EMPNUM INT NOT NULL, NAMELAST CHAR(30) NOT NULL, NAMEFIRST CHAR(30) NOT NULL, CONSTRAINT EMPLOYEEPK PRIMARY KEY (EMPNUM)) 其中%CLASSPARAMETER DSTIME = 'AUTO'开启表的DSTIME特性。 2. 通过面向对象建模,修改持久化的对象模型 对于对象模型,在建模时或之后修改模型,设置DSTIME类参数的方式开启:Parameter DSTIME = "AUTO";: Class Demo.Patient Extends %Persistent [ ClassType = persistent, DdlAllowed, Final, Owner = {_SYSTEM}, ProcedureBlock, SqlRowIdPrivate, SqlTableName = PATIENT ] { Property NAME As %Library.String(MAXLEN = 1) [ SqlColumnNumber = 2 ]; Property DOB As %Library.Date [ SqlColumnNumber = 3 ]; Parameter USEEXTENTSET = 1; Parameter DSTIME = "AUTO"; Parameter DSINTERVAL = 1; } 3. 对批量对象模型增加DSTIME特性 如果想对整个SQL Schema下对所有表或一个包下的所有对象类增加DSTIME,也可以定义一个持久化的纯虚基类,在基类中定义DSTIME类参数,然后让所有的类继承于这个基类。示例代码如下: 定义基类: Class Demo.Super Extends %Persistent [ Abstract, NoExtent ] { Parameter DSTIME = "AUTO"; } 注意,基类应该设置NoExtent关键字,以避免编译器生成存储模型。 修改子类,使Demo.Super成为其主父类: Class Demo.Patient Extends Demo.Super [ Language = objectscript ] { Property NAME As %Library.String(MAXLEN = 100) [ SqlColumnNumber = 2 ]; Property DOB As %Library.Date [ SqlColumnNumber = 3 ]; } 测试 开启DSTIME后,就可以使用SQL或对象操作方式,在InterSystems IRIS/Caché中执行一些插入、更新、删除操作,并观察持久化多维数组^OBJ.DSTIME的记录。 总结 通过开启DSTIME特性,让InterSystems IRIS/Caché自动记录需要追踪的SQL表或持久化类的数据变更,可以方便的通过^OBJ.DSTIME捕获这些变更,并处理数据变更或将变更记录输出到下游系统。 其它注意事项:应考虑在处理/输出完数据变更后,记录上次处理的DSTIME值,或删除^OBJ.DSTIME中已处理过的记录,以方便持续的、高效的捕获新的数据变更。 CDC系列 更多的CDC选项实现,请参考: 1. CDC系列之一 :使用Dejournal Filter在InterSystems IRIS/Caché上通过Mirroring实现CDC功能 2. CDC系列之二 :使用Dejournaling filter routine在Caché上通过Shadow实现CDC 3. CDC系列之三 :建立InterSystems IRIS/Caché的Global数据变更与SQL表记录的对应关系 4. CDC系列之四:使用DSTIME特性在InterSystems IRIS/Caché上实现CDC功能
文章
Michael Lei · 六月 8, 2023

使用 InterSystems 互操作性在TeleGram中使用 Open AI ChatGPT 进行自己的聊天

嗨社区! 想与您分享我在Telegram中使用GPT创建“我自己的”聊天的练习。 这个应用需要用到 Open Exchange 上的两个组件:@Nikolay.Soloviev 的Telegram Adapter和@Francisco.López1549的IRIS Open-AI 因此,通过此示例,您可以在 Telegram 中使用 ChatGPT 设置自己的聊天。 让我们看看如何让它发挥作用! 前提条件 使用@BotFather 帐户创建一个机器人并获取机器人令牌。然后将机器人添加到电报聊天或频道中并赋予其管理员权限。在https://core.telegram.org/bots/api了解更多信息 在https://platform.openai.com/上打开(如果没有,请创建)一个帐户,并获取您的Open AI API Key和Organization id 。 确保您的 InterSystems IRIS 中安装了 IPM。如果没有,这里有一个要安装的衬垫: USER> s r = ##class ( %Net.HttpRequest ). %New (), r .Server= "pm.community.intersystems.com" , r .SSLConfiguration= "ISC.FeatureTracker.SSL.Config" d r .Get( "/packages/zpm/latest/installer" ), $system .OBJ.LoadStream( r .HttpResponse.Data, "c" ) 或者您可以像这样使用带有 IPM 的社区 docker 图像: 安装 在启用互操作性的命名空间中安装 IPM 包。 USER>zpm“安装 Telegram-gpt” 用法 打开Production 将机器人的 Telegram Token 放入 Telegram business service 和 Telegram Business operation 中: 同时使用您的聊天 GPT API 密钥和组织 ID 初始化 St.OpenAi.BO.Api.Connect 操作: 启动Production。 在Telegram聊天中提出任何问题。您将通过 Chat GPT 获得答案。尽情享受吧! 在可视化追中: 细节 本示例使用 3.5 版本的 Chat GPT Open AI。它可以在模型参数的数据转换规则中更改。
文章
Claire Zheng · 六月 8, 2023

恭喜InterSystems 同事祝麟喜获重磅证书——HL7 FHIR R4 Proficiency Exam

亲爱的开发者们, 向大家分享一个好消息!InterSystems开发者中文社区版主、InterSystems高级销售工程师祝麟 ( @Lin.Zhu ) 于近日顺利通过“HL7 FHIR R4 Proficiency Exam”并取得资格认证证书! HL7 FHIR(R4)能力证书可以证明在最新和最热门的HL7标准方面达到行业公认的专业水平。考试涵盖了以下内容:FHIR原则;FHIR资源的基本概念;交换机制;一致性和实施指导;如何使用术语;如何建立安全和可靠的FHIR解决方案;FHIR维护过程;以及如何使用和处理FHIR许可和知识产权(IP)。 FHIR®(快速医疗互操作性资源)是HL7的下一代标准框架,2023年,我们推出的InterSystems IRIS医疗版互联互通套件3.0版(V3.0)就实现了与FHIR更深层的生态互通——借助强大的、打通多个互操作生态的能力,用户可以将互联互通服务和消息与其它互操作标准(如FHIR、HL7 V2)进行自动双向转换,为数据挖掘与价值实现带来更多可能性。 举例来说,在数据利用上,通过V3.0内置的FHIR资源仓库,用户可按业务需要,将互联互通文档和消息通过FHIR资源投射为SQL结构化数据,为实现“原始数据不出院、数据可用不可见”提供了技术基础,从而极大加强现有院内临床数据中心、科研数据中心以及区域健康大数据平台等数据仓库类系统的数据利用,使BI、AI/ML过程可基于数据发掘实现价值洞察,进行数字化业务闭环,把数据转化为可以持续产生价值的资产,为医院实现数据资产变现提供必要条件,全面助力医院数字化转型和生产力提升。 在应用创新上,V3.0 提供强大的FHIR生态能力(包含SMART on FHIR与CDS Hooks),支持快速开发和集成任意支持FHIR标准的、即插即用的第三方创新应用,实现数据利用最大化,为包括居民健康管理、临床决策支持、专科电子病历等打造坚实的数据和平台基础。 InterSystems作为FHIR标准在全球范围的推广者,已经在美国和亚太地区拥有众多成功案例,并且帮助越来越多的国家部署并遵循这种标准。 再次恭喜 @Lin.Zhu ✿✿ヽ(°▽°)ノ✿ 获得这一证书!
公告
Claire Zheng · 十一月 2, 2022

广而告之!寻找第500名InterSystems开发者社区(中文版)会员!

Hi!大家好! 截至11月3日中午,我们的开发者社区会员共495人!我们准备了一份惊喜礼品,准备送给第500名开发者社区会员! 那么,谁是第500名幸运的开发者社区成员呢?我们拭目以待!(*^▽^*)