0 关注者 · 478 帖子

SQL 是在关系数据库中存储、操作和检索数据的标准语言。

文章 姚 鑫 · 四月 8, 2021 8m read

第二十章 用户、角色和权限

InterSystems IRIS®具有系统级安全性,以及一组与sql相关的额外安全性特性。 在数据库级保护之外,InterSystems SQL安全性提供了额外级别的安全功能。 SQL和系统级安全性之间的一些关键区别是:

  • SQL保护比系统级保护更细粒度。可以为表、视图和存储过程定义特权。
  • SQL权限既可以授予用户,也可以授予角色。 系统级权限只分配给角色。
  • 持有SQL特权会隐式授予执行SQL操作所需的任何相关系统特权。 (相反,系统级特权并不意味着表级特权。)

InterSystems SQL在InterSystems IRIS数据平台上对ODBC、JDBC、Dynamic SQL和SQL Shell接口进行权限检查。 嵌入式SQL语句不执行特权检查; 假定使用嵌入式SQL的应用程序在使用嵌入式SQL语句之前会检查特权。

SQL权限和系统权限

要通过特定于SQL的机制操作表或其他SQL实体,用户必须具有适当的SQL权限。 系统级权限不足。 用户可以直接被授予SQL权限,也可以属于具有SQL权限的角色。

注意:角色是由SQL和系统级安全共享的:单个角色可以包括系统和SQ权限。

下面的例子,以Windows机器上的InterSystems IRIS为例:

  • 在用户名称空间中有一个名为User.MyPerson的持久化类。 这个类被投影到SQL中作为SQLUser.MyPerson表。
  • 有一个名为Test的用户,他不属于任何角色(因此没有系统权限),并且拥有SQLUser.MyPerson表的所有权限(没有其他SQL权限)。
  • 还有第二个用户,名为test2。此用户被分配给以下角色:%DB_USER(因此可以读取或写入用户数据库上的数据);%SQL(因此可以通过%Service_BINDINGS服务访问SQL);并且通过自定义角色具有使用控制台和%Development的权限。

如果测试用户尝试通过任何特定于SQL的机制(如使用ODBC的机制)在SQLUser.MyPerson表中读取或写入数据,则尝试将成功。这是因为InterSystems IRIS使测试用户成为%SQL角色(包括%SERVICE_SQL:USE权限)和%DB_USER角色的成员,因此该用户具有建立连接所需的权限;这在连接生成的审核事件(如%SYSTEM/%Login/Login event)中可见。(如果测试用户尝试使用终端对象机制,则这些尝试将失败,因为用户对这些机制没有足够的权限。)

如果Test2用户尝试通过任何特定于SQL的机制(如使用ODBC的机制)在SQLUser.MyPerson表中读取或写入数据,则该尝试将失败,因为该用户没有足够的权限访问该表。(如果Test2用户尝试使用对象机制查看终端中的相同数据,则尝试成功-因为该用户有足够的权限进行这种类型的连接。)

用户

InterSystems SQL用户与为InterSystems安全性定义的用户相同。可以使用SQL命令或管理门户定义用户。

  • 在SQL中,可以使用CREATE USER语句创建用户。这只会创建一个用户名和用户密码。新创建的用户没有角色。必须使用GRANT语句为用户分配权限和角色。可以使用ALTER USERDROP USER语句修改现有用户定义。
  • 在管理门户中选择System Administration(系统管理),选择Security(安全性),然后选择Users(用户)。单击页面顶部的Create New User(创建新用户)按钮。这会将带到编辑用户页,可以在其中指定用户名、用户口令和其他参数。创建用户后,其他选项卡即可用,可以在其中指定用户拥有哪些角色、用户拥有哪些常规SQL权限、用户拥有哪些表级权限、哪些视图可用以及可以执行哪些存储过程。

如果用户具有SQL表权限或一般SQL权限,则在用户的角色选项卡上授予或撤消的角色不会影响用户通过基于SQL的服务(如ODBC)对表的访问。这是因为,在基于SQL的服务中,基于表的权限优先于基于资源的权限。

image

可以使用%Library.SQLCatalogPriv类查询列出:

  • 所有用户SQLUsers()
  • 授予指定用户SQLUserPrivs(“username”)的所有权限
  • 授予指定用户SQLUserSysPrivs(“username”)的所有系统权限
  • 授予指定用户SQLUserRole(“username”)的所有角色

以下示例列出了授予当前用户的权限:

/// d ##class(PHA.TEST.SQL).Sqluser2()
ClassMethod Sqluser2()
{
	SET statemt=##class(%SQL.Statement).%New()
	SET cqStatus=statemt.%PrepareClassQuery("%Library.SQLCatalogPriv","SQLUserPrivs")
	IF cqStatus'=1 {WRITE "%PrepareClassQuery failed:" DO $System.Status.DisplayError(cqStatus) QUIT}
	SET rset=statemt.%Execute($USERNAME)
	WRITE "Privileges for ",$USERNAME
	DO rset.%Display()
}

架构形式的用户名

在某些情况下,用户名可以隐式用作SQL模式名称。如果用户名包含SQL标识符中禁止的字符,这可能会带来问题。例如,在多域配置中,用户名包含“@”字符。

根据分隔标识符配置参数的设置,InterSystems IRIS会以不同的方式处理此情况:

  • 如果启用了分隔标识符的使用,则不会进行特殊处理。
  • 如果禁用分隔标识符的使用,则会从用户名中删除所有禁用字符,以形成架构名称。例如,用户名“Documentation@intersystems.com”将成为模式名称“Documentationintersystemscom”

这不会影响SQL CURRENT_USER函数返回的值。它始终与$USERNAME相同。

角色

将SQL权限分配给用户或角色。角色使能够为多个用户设置相同的权限。角色由SQL和系统级安全性共享:单个角色可以同时包括系统权限和SQL权限。

管理门户、系统管理、安全性、角色页提供了InterSystems IRIS实例的角色定义列表。要查看或更改特定角色的详细信息,请选择该角色的名称链接。在出现的编辑角色页面上,有关于角色权限以及哪些用户或角色拥有该权限的信息。

常规选项卡列出角色对系统间安全资源的权限。如果角色仅拥有SQL权限,则一般信息选项卡的资源表会将该角色的权限列为“未定义”。

SQL权限选项卡列出了角色对InterSystems SQL资源的权限,其中命名空间的下拉列表允许查看每个命名空间的资源。因为权限是按名称空间列出的,所以在特定名称空间中没有权限的角色的列表显示为“None”

注:应该使用角色定义权限,并将特定用户与这些角色相关联。这有两个原因:

  1. 与检查单个用户条目相比,SQL引擎通过检查相对较小的角色数据库来确定权限级别的效率要高得多。
  2. 与具有多个单独用户设置的系统相比,使用少量角色集管理系统要容易得多。

例如,可以定义具有特定访问权限的名为“ACCOUNTING”的角色。随着 Accounting Department的发展,可以定义新用户并将其与会计角色相关联。如果需要修改Accounting权限,只需修改一次,系统会自动覆盖Accounting Department的所有成员。

一个角色可以担任其他角色。例如,会计角色可以拥有BILLINGCLERK角色。被授予会计角色的用户将同时拥有会计角色和BILLINGCLERK角色的权限。

还可以使用以下SQL命令定义用户和角色:CREATE USERCREATE ROLEALTER USERGRANTDROP USERDROP ROLE

可以使用%Library.SQLCatalogPriv类查询列出:

  • 所有角色SQLRoles()
  • 授予指定角色SQLRolePrivileges(“Rolename”)的所有权限
  • 授予指定角色SQLRoleUser(“Rolename”)的所有角色或用户
  • 授予指定用户SQLUserRole(“username”)的所有角色

SQL权限

将SQL权限分配给用户或角色。角色使能够为多个用户设置相同的权限。

InterSystems SQL支持两种类型的权限:管理权限和对象权限。

  • 管理权限是特定于命名空间的。

管理权限包括创建、更改和删除对象类型,例如创建表所需的%CREATE_TABLE权限。不仅需要%ALTER_TABLE特权来更改表,还需要%ALTER_TABLE特权来创建或删除索引、创建或删除触发器以及运行TUNE TABLE

管理权限还包括%NOCHECK%NOINDEX%NOLOCK%NOTRIGGER,它们确定用户在执行INSERTUPDATEINSERTUPDATEDELETE时是否可以应用相应的关键字限制。用户需要分配%NOTRIGGER管理权限才能执行TRUNCATE TABLE

  • 对象权限特定于表、视图或存储过程。它们指定对特定命名SQL对象的访问类型(在SQL意义上:表、视图、列或存储过程)。如果用户是SQL对象的所有者(创建者),则会自动向该用户授予该对象的所有权限。

表级对象权限提供对表或视图的所有列中的数据的访问(%ALTERDELETESELECTINSERTUPDATEEXECUTEREFERENCES),包括当前存在的列和任何后续添加的列。

列级对象权限仅提供对表或视图的指定列中的数据的访问权。不需要为具有系统定义的值(如RowIDIdentity)的列分配列级权限。

存储过程对象权限允许将过程的EXECUTE权限分配给指定的用户或角色。

授予SQL权限

可以通过以下方式授予权限:

  • 使用管理门户。从系统管理中选择安全性,然后选择用户或角色。选择所需的用户或角色,然后选择相应的选项卡:管理权限的SQL权限、对象权限的SQL表、SQL视图或SQL过程。
  • 在SQL中,使用GRANT命令向指定用户或角色(或用户或角色列表)授予特定管理权限或对象权限。可以使用REVOKE命令删除权限。
  • 在ObjectScript中,使用$SYSTEM.SQL.Security.GrantPrivileve()方法将特定对象权限授予指定用户(或用户列表)。

列出SQL权限

  • 使用管理门户。从系统管理中选择安全性,然后选择用户或角色。选择所需的用户或角色,然后选择相应的选项卡:管理权限的SQL权限、对象权限的SQL表、SQL视图或SQL过程。
  • 在SQL中,使用%CHECKPRIV命令确定当前用户是否具有特定的管理或对象权限。
  • 在ObjectScript中,使用$SYSTEM.SQL.Security.CheckPrivileve()方法确定指定用户是否具有特定的对象权限。

审核权限错误

当InterSystems IRIS进程调用用户没有特权的SQL语句时,操作将失败,并生成SQLCODE-99错误。启用审核事件%SYSTEM/%SQL/PrivilegeFailure时,将在Audit数据库中为遇到的每个SQLCODE-99错误放置一条记录。默认情况下,此审核数据库选项处于禁用状态。

1
0 442
文章 王喆 👀 · 九月 12, 2022 2m read

  大家都用过IRIS的消息查看器吧,其实这个页面有一个隐藏的功能,就是显示【显示查询】,如图所示:

那么这个东西是如何打开的呢?如果一句话概括就是: Terminal下输入: Set ^Ens.Debug("UtilEnsMessages","sql")=1,这个也是打开这个功能的思路,下面是详细的说明。

开启功能的步骤:

    1、进入Terminal,输入用户名\密码;

  2、进入需要打开显示查询的命名空间:如 zn "BKIP"

  3、执行 【Set ^Ens.Debug("UtilEnsMessages","sql")=1】打开显示查询。

打开消息查看器页面刷新:

成功!!! 

他有什么作用呢?举个栗子,当我修改基本条件为会话开始,点击【搜索】然后再点击【显示查询】

可以看到刚才在页面点击的操作生成了SQL并且显示在新的弹框里面了 すごい(四个一)。

基于此我们能做什么呢?一方面,指导我们分析IRIS的表结构,让我们得以去进行一些数据分析的操作。另一方面,在我们日常运维的时候,当我们需要直接知道消息的日吞吐量的时候,可以通过SQL去计算,具体的操作是这样:

1、修改开始和结束时间,把类型修改为会话开始点击显示查询会出现一段SQL,如图所示:

2、把这段SQL复制,打开【系统资源管理器】--【SQL】,粘贴SQL稍加修改执行,就能得到,上游请求集成平台次数,如图所示:

   

0
0 388
文章 Michael Lei · 八月 9, 2022 23m read

在这篇文章中,你可以访问InterSystems开发者社区中与学习InterSystems IRIS最相关主题的文章库。找到按机器学习、嵌入式Python、JSON、API和REST应用、管理和配置InterSystems环境、Docker和云、VSCode、SQL、分析/BI、全局、安全、DevOps、互操作性、Native API排列的顶级发表的文章。快来享受学习的乐趣吧!

机器学习

机器学习是建立先进的数据分析和自动化人工活动的一种必要的技术,具有很好的效率。它可以创建认知模型,从现有的数据中学习,并根据其自我调整的算法进行预测、概率计算、分类、识别和 "非创造性 "的人类活动的自动化。

在所有情况下,InterSystems IRIS作为一个数据平台和环境来创建、执行、提供和使用这些机器学习模型。IRIS能够从SQL命令(IntegratedML)中使用ML,使用嵌入式Python和PMML(预测模型标记语言)来执行ML。你可以在以下文章中查看它的功能:

1
0 290
文章 Michael Lei · 八月 7, 2022 2m read

根据日期范围查询的SQL性能让你失望?  我有一个比较特别的技巧,可能会帮助你解决这个问题! (SQL开发人员讨厌这个!)*

如果你有一个类,在添加数据时记录时间戳,那么这些数据将与你的IDKEY值保持顺序--也就是说,当且仅当ID1<ID2时,表内所有ID和时间戳值的TimeStamp1<TimeStamp2--那么你可以利用这一知识来提高对时间戳范围的查询性能。  考虑一下下面这个表:

Class User.TSOrder extends %Persistent 
{ 

Property TS as %TimeStamp;

Property Data as %String (MAXLEN=100, MINLEN=200);

Index TSIdx on TS;

Index Extent [type=bitmap, extent];

}

用过去30天内的30,000,000条随机行来填充,每天将得到1,000,000条行。 现在,如果我们想查询某一天的信息,你可以这样写:

SELECT ID, TS, Data 
FROM TSOrder
WHERE 
     TS >= '2016-07-01 00:00:00.00000' AND 
     TS <= '2016-07-01 23:59:59.999999'
0
0 538
文章 Jingwei Wang · 七月 29, 2022 33m read

什么时候使用索引

索引提供了一种机制,通过维护常用数据的分类子集来优化查询。确定哪些字段应该被编入索引需要一些思考:太少或错误的索引,关键查询会运行得太慢;太多的索引会减慢INSERT和UPDATE的性能(因为索引值必须被设置或更新)。

索引什么

为了确定添加索引是否能提高查询性能,从管理门户的SQL界面运行查询,并在Performance中注意 global引用的数量。添加索引,然后重新运行查询,注意 global引用的数量。一个有用的索引应该减少 global引用的数量。你可以通过使用%NOINDEX关键字作为WHERE子句或ON子句条件的前言来阻止索引的使用。

你应该对JOIN中指定的字段(属性)进行索引。例如,LEFT OUTER JOIN从左表开始,然后查看右表,因此,你应该对右表的字段进行索引。在下面的例子中,你应该为T2.f2编制索引。一个INNER JOIN应该在两个ON子句字段上都有索引。

  FROM Table1 AS T1 LEFT OUTER JOIN Table2 AS T2 ON T1.f1 = T2.f2
0
0 165
文章 Jingwei Wang · 七月 28, 2022 8m read

InterSystems SQL支持在InterSystems IRIS数据平台数据库中将流数据存储为BLOB(Binary Large Objects 二进制大对象)或CLOB(Character Large Objects字符大对象)的能力。

InterSystems SQL支持两种流字段:

  • 字符流:用于大量的文本。
  • 二进制流:用于图像、音频或视频。

BLOBs和CLOBs可以存储多达4GB的数据(JDBC和ODBC规范规定的限制)。除了在通过ODBC或JDBC客户端访问时如何处理字符编码转换(如Unicode到多字节)外,BLOB和CLOB的操作在各方面都是相同的:BLOB中的数据被视为二进制数据,决不转换为其他编码,而CLOB中的数据被视为字符数据,在必要时进行转换。

如果一个二进制流文件(BLOB)包含单一的非打印字符$CHAR(0),它被认为是一个空的二进制流。它相当于""空二进制流值:它存在(不是空的),但长度为0。

从对象的角度来看,BLOB和CLOBs被表示为流对象。更多信息请参见定义和使用类中的 "与流合作 "一章。

定义流数据字段

InterSystems SQL支持流字段的各种数据类型名称。这些InterSystems的数据类型名称是对应于以下的同义词。

0
0 179
文章 Jingwei Wang · 七月 28, 2022 4m read

定义 Stored Procedures

可以使用以下方式定义stored procedures

  1. 使用DDL定义存储过程
  2. 使用类方法定义存储过程

使用DDL定义存储过程

CREATE PROCEDURE 可以创建一个查询,它总是作为一个存储过程被预测。一个查询可以返回一个单一的结果集。

CREATE PROCEDURE AgeQuerySP(IN topnum INT 10, IN minage INT 20)
BEGIN
SELECT TOP :topnum Name, Age FROM Sample.Person
WHERE Age > :minage;
END

列表中的每个参数声明包括(按顺序)。指定参数模式是IN(输入值),OUT(输出值),还是INOUT(修改值)。如果省略,默认的参数模式是IN,参数名称是区分大小写的。

CREATE QUERY 可以创建一个可以选择作为存储过程投射的查询。一个查询可以返回一个单一的结果集,这个查询可能是也可能不是作为存储过程公开的。要创建一个作为存储过程公开的查询,你必须指定PROCEDURE关键字作为其特征之一。你也可以使用CREATE PROCEDURE语句来创建一个作为存储过程公开的查询。

0
0 212
文章 Jingwei Wang · 七月 21, 2022 3m read

在InterSystems IRIS数据平台管理门户中,有一些工具用于导入和导出数据。这些工具使用动态SQL,这意味着查询是在运行时准备和执行的。可以导入或导出的行的最大尺寸是3,641,144个字符。

你也可以使用%SQL.Import.Mgr类或LOAD DATA SQL命令导入数据,并使用%SQL.Export.Mgr类导出数据。

从文本文件中导入数据(.csv 和.txt)

你可以从一个文本文件中导入数据到一个合适的InterSystems IRIS类。当你这样做时,系统会在该类的表中创建并保存新的行。该类必须已经存在并且必须被编译。

步骤如下:

  1. 从管理门户中 选择系统资源管理器,然后选择SQL。用页面顶部的切换选项选择一个命名空间;这会显示可用的命名空间的列表。
  2. 在页面顶部,点击向导下拉列表,并选择数据导入。 
  3. 在向导的第一页,首先指定外部文件的位置。对于导入文件所在的位置,点击要使用的服务器的名称。
  4. 然后输入文件的完整路径和文件名,文件可以是.csv 和 .txt格式。
  5. 然后选择你想要导入到schema的名称。
  6. 选择表名。
  7. 然后点击下一步。
  8. 在向导的第二页,选择需要导入数据的列。
  9. 然后点击下一步。
  10. 在向导的第三页,描述外部文件的格式。
0
0 281
文章 Jingwei Wang · 七月 21, 2022 4m read

本章介绍了如何将SQL code从文本文件导入InterSystems SQL。当你导入SQL code时,InterSystems IRIS 数据平台使用动态SQL准备并执行每一行的SQL。如果遇到无法解析的SQL code行,SQL导入会跳过该行code,继续准备和执行后续的code行,直到到达文件的末端。所有的SQL code导入操作都会导入到当前的命名空间。

SQL导入主要用于导入数据定义语言(DDL)命令,如CREATE TABLE,并使用INSERT、UPDATE和DELETE命令来填充表。SQL导入可以准备和执行SELECT查询,但不创建结果集。

SQL导入可以用来导入InterSystems的SQL code。它也可以用于code迁移,从其他供应商(FDBMS、Informix、InterBase、MSSQLServer、MySQL、Oracle、Sybase)导入SQL。来自其他供应商的code被转换为InterSystems的SQL并执行。SQL导入不能将所有的SQL命令导入到InterSystems SQL中。它导入的是那些与InterSystems IRIS实现的SQL标准兼容的命令和条款。不兼容的特征通常会被解析,但会被忽略。

SQL导入可以成功地准备一个SQL查询--在适当的时候创建一个相应的缓存查询--但它不会执行查询。

0
0 182
文章 Jingwei Wang · 七月 21, 2022 5m read

视图为存储查询,提供了物理表的所有灵活性和安全权限。所有的视图都是可更新的或只读的。

注意:不能对只读的数据库中的数据创建视图。不能对存储在通过ODBC或JDBC网关连接的Informix表中的数据创建视图。这是因为InterSystems IRIS查询转换在FROM子句中使用子查询,而Informix不支持FROM子句的子查询。

创建视图

视图名称可以是合格的的或不合格的。一个没有限定的视图名称是一个简单的标识符。MyView。一个合格的视图名称由两个简单的标识符组成,一个schema名称和一个视图名称,用句号隔开, 例如MySchema.MyView。视图名和表名遵循相同的命名规则,并对未限定的名称执行相同的schema名称解析。同一模式中的视图和表不能有相同的名称。

你可以通过几种方式定义视图:

  1. 使用SQL CREATE VIEW命令(在DDL脚本中或通过JDBC或ODBC)。
    CREATE VIEW MySchema.MyView (ViewCol1, ViewCol2, ViewCol3) AS
    SELECT TableCol1, TableCol2, TableCol3
    FROM MyTable
0
0 258
问题 Michael Lei · 六月 16, 2021

在Ensemble中使用SQL进行批量插入
你好,社区。

我试图在一个表中插入多个值。下面是简单的SQL语句。

插入到表X中

values ('Name', 'Address', 'Phone')

我怎样才能在一条语句中进行多次插入(行)?

数值不在另一个表中,所以我不能使用选择进入。

谢谢。

吉米-克里斯蒂安

Hello Community,

I am trying to insert multiple values in a table. Below is the simple sql statement.

Insert Into TableX

values ('Name', 'Address', 'Phone')

How can i do multiple inserts(rows) in one single statement?

Values are not in another table, so i cannot use Select into.

Thanks,

Jimmy Christian.

1
0 183
问题 Michael Lei · 四月 29, 2022

我想知道是否有更好的方法来使用动态SQL对数据集进行分页,而不是我下面使用的方法。问题是,当潜在的数据池变大时,这段代码就会变慢,以至于无法使用。在分析下面的每一行代码时,似乎速度变慢与最初的rset.%Next()迭代有关。 有没有什么不需要子查询/%VID的可用方法,比如简单的LIMIT/OFFSET?

我的代码类似于:

s sql=##class(%SQL.Statement).%New()

s query="SELECT *,%VID FROM (SELECT prop FROM table WHERE prop=x) WHERE %VID BETWEEN 1 AND 100"             

s sc=sql.%Prepare(query)

s rset=sql.%Execute()

while rset.%Next() {.....

1
1 502
问题 Michael Lei · 四月 27, 2022

Hi, 请问如何更改表(有数据)上的主键?谢谢!

答:

如果数据已经存在,那么这是一项必须重视的任务,特别是如果存在继承或父/子关系,因为这将导致你的数据存储方案的改变。

最简单的方法是通过一个中间(临时)表来实现。

创建一个具有相同结构的新类,但有一个新的主键。
使用SQL(不是合并命令)将数据从旧的类中移到它里面。
删除旧类中的数据/索引,然后改变其中的主键。
使用合并命令,将数据从新类移到旧类中。
删除带有数据的新类。
重建索引(如果有的话)。

几个有用的链接:
        MERGE

        持久性对象和InterSystems IRIS SQL

        持久性对象的介绍

如果仍然有问题,最好向WRC寻求帮助。

1
0 171
文章 Jingwei Wang · 七月 14, 2022 6m read

本文概述了InterSystems SQL的特点,特别是那些没有被SQL标准所涵盖或与InterSystems IRIS 数据平台的统一数据架构有关的特点。假定你已有SQL的知识,本文不是SQL概念或语法的介绍。

 

在InterSystems SQL中,数据是在表内呈现的。每个表都被定义为包含若干列。一个表可以包含零个或多个数据值的行。以下术语大致上是等同的。

数据 关系型数据库术语 InterSystems SQL术语 InterSystems IRIS术语
database          schema schema package
database table table persistent class
field column column property
record row row  

schema

SQL schema提供了一种将相关表、视图、存储过程和缓存查询分组的方法。模式的使用有助于防止在表一级的命名冲突,因为一个表、视图或存储过程的名称必须只在其schema内是唯一的。一个应用程序可以在多个schema中指定表。

0
0 604
文章 Jingwei Wang · 七月 14, 2022 6m read

 


InterSystems SQL为存储在IRIS数据库中的数据提供不折不扣的、标准的关系型访问。

InterSystems SQL具有以下优点。

高性能和可扩展性 - InterSystems SQL的性能和可扩展性优于其他关系型数据库产品。

与IRIS对象技术的集成 - InterSystems SQL与IRIS对象技术紧密集成。你可以混合使用关系型和对象型的数据访问,而不牺牲任何一种方法的性能。

低维护 - 与其他关系型数据库不同,IRIS应用程序不需要在部署的应用程序中重建索引和压缩表。

支持标准SQL查询 - InterSystems SQL支持SQL-92标准语法和命令。

你可以将InterSystems SQL用于许多目的,包括。

基于对象和Web应用程序 - 你可以在InterSystems Object和Caché Server Page应用程序中使用SQL查询,以执行强大的数据库操作,如查询和搜索。

在线事务处理 - InterSystems SQL为插入和更新操作以及通常在事务处理应用程序中发现的查询类型提供出色的性能。

BI和数据仓库 - IRIS多维数据库引擎和位图索引技术的结合使其成为数据仓库式应用的绝佳选择。

点对点查询和报告 - 你可以使用InterSystems SQL包含的全功能ODBC和JDBC驱动来连接到流行的报告和查询工具。

0
0 320
文章 姚 鑫 · 七月 13, 2022 3m read

第四章 使用嵌入式 Python (一)

嵌入式 Python 允许使用 Python 作为编程 IRIS 应用程序的本机选项。

预备知识

使用嵌入式 Python 所需的 Python 版本取决于运行的平台。

在 Microsoft Windows 上,IRIS 安装工具包安装正确版本的 Python(当前为 3.9.5),仅用于嵌入式 Python。如果在开发机器上并希望将 Python 用于一般用途,建议从 https://www.python.org/downloads/ 下载并安装相同的版本。

许多基于 UNIX 的操作系统都安装了 Python。如果需要安装,请使用包管理器为操作系统推荐的版本,例如:

  • macOS:使用 Homebrew 安装 Python 3.9(https://formulae.brew.sh/formula/python@3.9)
  • Ubuntu: apt-get install python3
  • Red Hat Enterprise Linux or Oracle Linux: yum install python3
  • SUSE: zypper install python3

如果收到“无法加载 python”的错误,这意味着没有安装 Python,或者系统上安装了意外版本的 Python。使用上述方法之一安装或重新安装。

在基于 UNIX 的系统上,可能希望使用 pip3 命令安装 Python 包。如果尚未安装 pip3,请使用系统的包管理器安装包 python3-pip

要防止在运行 Embedded Python 时出现 IRIS_ACCESSDENIED 错误,请启用 %Service_Callin。在管理门户中, System Administration > Security > Services,选择 %Service_CallIn,然后选中启用服务框。

运行嵌入式 Python

本节详细介绍了运行 Embedded Python 的几种方法:

从Python Shell

可以从终端会话或命令行启动 Python shell

从终端启动 Python Shell

通过调用 %SYS.Python 类的 Shell() 方法,从 终端会话启动 Python shell。这将以交互模式启动 Python 解释器。终端会话中的用户和命名空间被传递给 Python shell

通过键入命令 quit() 退出 Python shell

以下示例在终端会话中从 USER 命名空间启动 Python shell。它打印斐波那契数列中的前几个数字,然后使用 IRIS SYSTEM.OBJ.ShowClasses() 方法打印当前命名空间中的类列表。

USER>do ##class(%SYS.Python).Shell()
 
Python 3.9.5 (default, Jul  6 2021, 13:03:56) [MSC v.1927 64 bit (AMD64)] on win32
Type quit() or Ctrl-D to exit this shell.
>>> a, b = 0, 1
>>> while a < 10:
...     print(a, end=' ')
...     a, b = b, a+b
...
0 1 1 2 3 5 8 >>>
>>> status = iris.cls('%SYSTEM.OBJ').ShowClasses()
User.Company
User.Person
>>> print(status)
1
>>> quit()
 
USER>

方法 %SYSTEM.OBJ.ShowClasses() 返回一个 IRIS %Status 值。在这种情况下,1 表示未检测到错误。

注意:使用 %SYS.Python 类的 Shell() 方法运行 Python shell 时,不需要显式导入 iris 模块。继续使用该模块。

从命令行启动 Python Shell

使用 irispython 命令从命令行启动 Python shell。这与从终端启动 shell 的工作方式大致相同,但必须传入 IRIS 用户名、密码和命名空间。

以下示例从 Windows 命令行启动 Python shell

C:\InterSystems\IRIS\bin>set IRISUSERNAME = <username>

C:\InterSystems\IRIS\bin>set IRISPASSWORD = <password>

C:\InterSystems\IRIS\bin>set IRISNAMESPACE = USER

C:\InterSystems\IRIS\bin>irispython
Python 3.9.5 (default, Jul  6 2021, 13:03:56) [MSC v.1927 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>

在基于 UNIX 的系统上,使用 export 而不是 set

/InterSystems/IRIS/bin$ export IRISUSERNAME=<username>
/InterSystems/IRIS/bin$ export IRISPASSWORD=<password>
/InterSystems/IRIS/bin$ export IRISNAMESPACE=USER
/InterSystems/IRIS/bin$ ./irispython
Python 3.9.5 (default, Jul 22 2021, 23:12:58)
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

注意:如果尝试运行 import iris 并看到一条消息说 IRIS_ACCESSDENIED,请启用 %Service_Callin。在管理门户中,转至 System Administration > Security > Services,选择 %Service_CallIn,然后选中启用服务框。

0
0 250
文章 姚 鑫 · 七月 10, 2022 5m read

第一章 嵌入式Python概述(一)

嵌入式 Python 允许将 Python 与 IRIS 数据平台的本地编程语言 ObjectScript 一起使用。当使用嵌入式 PythonIRIS 类中编写方法时,Python 源代码与编译后的 ObjectScript 代码一起编译为在服务器上运行的目标代码。与使用网关或 PythonNative SDK 相比,这允许更紧密的集成。还可以导入 Python 包,无论它们是自定义的还是公开的,并在ObjectScript 代码中使用它们。 Python 对象是 ObjectScript 中的一等公民,反之亦然。

  • 使用来自 ObjectScriptPython 库 - 此方案假设 ObjectScript 开发人员,并且希望利用 Python 开发人员社区可用的众多 Python 库的强大功能。
  • Python 调用 IRIS API — 此方案假定您是一名 Python 开发人员,对 IRIS 不熟悉,并且想知道如何访问 API
  • 一起使用 ObjectScriptPython — 这个场景假设在一个由 ObjectScriptPython 开发人员组成的混合团队中,并且想知道如何一起使用这两种语言。

将需要 2021.2 或更高版本的正在运行的 IRIS 实例,以及取决于操作系统的一些先决条件。还需要知道如何访问终端,即 IRIS 命令行工具。

本文档中的一些示例使用来自 GitHubSamples-Data 存储库的类:https://github.com/intersystems/Samples-Data。 建议创建一个名为 SAMPLES 的专用命名空间并将样本加载到该命名空间中。如果想查看或修改示例代码,则需要设置集成开发环境 (IDE)。推荐使用 Visual Studio Code

本篇并不试图提供嵌入式 Python 或使用 IRIS 进行编程的全面概述。

使用 ObjectScript 中的 Python 库

使用 Embedded PythonObjectScript 开发人员可以轻松地使用来自 IRIS 的众多可用 Python 库(通常称为“包”)中的任何一种,从而无需开发自定义库来复制现有功能。 IRIS<installdir>/mgr/python 目录中查找已安装的 Python

ObjectScript 准备 Python 包以供使用是一个两步过程:

  1. 从命令行,从 Python 包索引(或其他索引)安装所需的包。
  2. ObjectScript 中,导入已安装的包以加载包并将其作为对象返回。然后,可以像使用实例化的 ObjectScript 类一样使用该对象。

安装 Python 包

在将 Python 包与 Embedded Python 一起使用之前,请从命令行安装。使用的命令会有所不同,具体取决于使用的是 Windows 还是基于 UNIX 的系统。 建议将软件包安装到目录 <installdir>/mgr/python

Windows 上,使用 <installdir>/bin 目录中的 irispip 命令:irispip install --target <installdir>\mgr\python <package>.

例如,可以在 Windows 机器上安装 numpy 包,如下所示:

C:\InterSystems\IRIS\bin>irispip install --target C:\InterSystems\IRIS\mgr\python numpy

在基于 UNIX 的系统上,使用 pip3 命令:pip3 install --target <installdir>/mgr/python <package>

例如,可以在 Linux 机器上安装 numpy 包,如下所示:

$ pip3 install --target /InterSystems/IRIS/mgr/python numpy

注意:如果基于 UNIX 的系统没有安装 pip3,请使用系统的包管理器安装包 python3-pip

导入 Python 包

%SYS.Python 类包含从 ObjectScript 使用 Python 所需的功能。可以在任何 ObjectScript 上下文中使用 %SYS.Python,例如类、终端会话或 SQL

要从 ObjectScript 导入 Python 包或模块,请使用 %SYS.Python.Import() 方法。

例如,假设在 USER 命名空间中,以下命令会在终端中导入数学模块:

USER>set pymath = ##class(%SYS.Python).Import("math")

数学模块与标准 Python 版本打包在一起,因此无需在导入之前安装它。通过在 pymath 对象上调用 zwrite,可以看到它是内置数学模块的一个实例:

USER>zwrite pymath
pymath=1@%SYS.Python  ; <module 'math' (built-in)>  ; <OREF>

注意:包是 Python 模块的集合,但是当导入包时,创建的对象始终是模块类型。

现在,可以像访问任何 ObjectScript 对象一样访问数学模块属性和方法:

USER>write pymath.pi
3.141592653589793116
USER>write pymath.factorial(10)
3628800

示例

此示例使用 geopy 包来访问 OpenStreetMapNominatim 地理编码工具。地理编码是获取基于文本的位置描述(例如地址或地名)并返回地理坐标(例如纬度和经度)以精确定位地球表面位置的过程。

首先,从命令行安装 geopy,如下 Windows 示例所示:

C:\InterSystems\IRIS\bin>irispip install --target C:\InterSystems\IRIS\mgr\python geopy
Collecting geopy
  Using cached geopy-2.2.0-py3-none-any.whl (118 kB)
Collecting geographiclib<2,>=1.49
  Using cached geographiclib-1.52-py3-none-any.whl (38 kB)
Installing collected packages: geographiclib, geopy
Successfully installed geographiclib-1.52 geopy-2.2.0

在基于 UNIX 的系统上,使用:

$ pip3 install --target /InterSystems/IRIS/mgr/python geopy

然后在终端中运行以下命令来导入和使用模块:

USER>set geopy = ##class(%SYS.Python).Import("geopy")
 
USER>set args = { "user_agent": "Embedded Python" }
 
USER>set geolocator = geopy.Nominatim(args...)
 
USER>set flatiron = geolocator.geocode("175 5th Avenue NYC")
 
USER>write flatiron.address
Flatiron Building, 175, 5th Avenue, Flatiron District, Manhattan, New York County, New York, 10010, United States
USER>write flatiron.latitude _ "," _ flatiron.longitude
40.74105919999999514,-73.98964162240997666
USER>set cityhall = geolocator.reverse("42.3604099,-71.060181")
 
USER>write cityhall.address
Government Center, Cambridge Street, Downtown Crossing, West End, Boston, Suffolk County, Massachusetts, 02203, United States

此示例将 geopy 模块导入 ObjectScript。然后它使用 Nominatim 模块创建一个地理定位器对象。该示例使用地理定位器的 geocode() 方法在给定字符串的情况下查找地球上的位置。然后它调用 reverse() 方法来查找给定纬度和经度的地址。

需要注意的一点是 Nominatim() 采用命名关键字参数,ObjectScript 不直接支持这种结构。解决方案是创建一个包含参数列表的 JSON 对象(在这种情况下将 user_agent 关键字设置为值“嵌入式 Python”),然后使用 args... 语法将其传递给方法。

与前面示例中导入的数学模块相比,对 geopy 对象调用 zwrite 表明它是安装在 C:\InterSystems\iris\mgr\python 中的 geopy 包的一个实例:

USER>zwrite geopy
geopy=2@%SYS.Python  ; <module 'geopy' from 'c:\\intersystems\\iris\\mgr\\python\\geopy\\__init__.py'>  ; <OREF>
0
0 265
文章 姚 鑫 · 七月 8, 2022 2m read

第二十二章 安全自定义 Web 应用程序登录

除了推荐的 REST 应用程序支持之外,产品还支持两种类型的传统 Web 应用程序:CSPZen。在配置使用 CSPZen 的自定义登录页面时,遵循推荐的协议很重要。这些协议提供了更高的安全性,并最大限度地减少了升级到新产品或版本时的不兼容性。

关于创建自定义 CSP 登录页面

创建自定义 CSP 登录页面:

  1. 创建 %CSP.Login 页面的子类。
  2. 要自定义应用程序的行为,请覆盖子类的 Draw 方法,以便页面看起来像想要的那样。其中包括修改登录页面外观的方法和修改安全令牌页面外观的方法(如果使用双因素身份验证):
  • 登录页面方法——DrawCSS3STYLEDrawHEADDrawSTYLEDrawTitle
  • 安全令牌 (ST) 页面方法 — DrawSTHEADDrawSTTitle

请注意,DrawTitleDrawSTTitle 方法调用 DrawTitleSection 方法。

  1. 在应用程序中根据需要调用子类。

重要提示:创建自定义登录页面时,必须使用 %CSP.Login 的子类。在 CSP 应用程序中创建登录页面的其他方法可能会导致各种问题。 如果编写了不使用 %CSP.Login 子类的自定义登录页面,并且应用了来自任何用于升级或保护实例的更改,那么登录页面可能会失败而没有错误消息。例如,用户可能会尝试使用有效的用户名和密码登录,但他们的登录将在没有任何明显原因的情况下失败。这种情况可能表明需要更改自定义登录以使用所需的方法。

关于创建自定义 Zen 登录页面

Zen已被废弃

0
0 121
文章 姚 鑫 · 七月 7, 2022 4m read

第二十一章 使用工作队列管理器(四)

分离和附加工作队列

通常,初始化一组工作程序,将工作项排队,然后等待工作程序完成工作项。但是,可能会遇到工作人员作业完成工作项所需的时间比预期更长的情况,或者无法将单个进程专门用于等待。因此,工作队列管理器使能够将工作队列与进程分离,然后将工作队列附加到同一进程或不同的进程。

例如,假设队列引用了初始化的工作队列。还假设向工作队列中添加了几个工作项。在调用 Wait() 或 WaitForComplete() 来确定正在处理的工作的状态之前,可以使用以下方法:

Detach()

method Detach(ByRef token As %String, timeout As %Integer=86400) as Status 从初始化工作队列时创建的对象引用中分离工作队列对象。 Detach() 方法使任何正在进行的工作能够继续并保留工作队列的当前状态。

token 参数表示一个安全令牌,可以使用它随后将工作队列附加到另一个进程。 timeout 参数是可选的,它指示系统保留分离的工作队列对象的时间量(以秒为单位)。超时期限过后,系统会删除与工作队列关联的所有工作人员作业和信息。超时的默认值为 1 天。

调用 Detach() 方法后,对分离对象引用的大多数调用都会返回错误。但是,NumActiveWorkers() 和 NumWorkers() 方法返回 -1。

Attach()

Attach(token, ByRef sc As %Status) as WorkMgr 如果工作队列对象仍在内存中,则将新对象引用附加到先前分离的工作队列对象。 Attach() 方法返回与工作队列关联的工作队列管理器的新实例。可以随后调用工作队列上的方法。例如,可以调用超时值为 0 的 Wait() 方法来确定队列在分离之前是否已完成任何工作项。

token 参数表示之前在工作队列上调用的 Detach() 方法返回的安全令牌。

例如,可以分离然后附加一个工作队列,如下所示:

s sc = queue.Detach(.token,60)
if $$$ISERR(sc) {
	ret sc
}
s queue = $system.WorkMgr.Attach(token,.sc)
if $$$ISERR(sc) {
	ret sc
}

停止工作队列并删除工作项

可以停止工作队列、中断正在进行的任何工作项并移除任何排队的工作项。为此,请调用工作队列的 Clear() 方法。

method Clear(timeout As %Integer = 5) as %Status 给定超时时间超时(以秒为单位),此方法等待工作人员作业完成其当前任务,然后终止作业。系统删除然后重新创建工作队列,不附加任何工作项。之后,系统立即从 Wait() 或 WaitForComplete() 返回。

指定安装和拆卸处理

每个工作队列通常有多个worker jobs。如果工作项多于工作项,则工作项将执行多个工作项,一次一个。在这些工作项开始之前确定所需的任何设置步骤并在将工作项添加到队列之前调用所有此类逻辑是很有用的。

%SYSTEM.WorkMgr 类提供方法 Setup() 和 TearDown(),可以使用它们来定义工作人员作业的设置活动和清理活动。例如,使用 Setup() 设置在工作作业中使用的公共变量,并使用 TearDown() 杀死这些变量。还可以使用 Setup() 取出锁并设置进程私有全局变量,并且将使用 TearDown() 释放这些锁并删除这些全局变量。

在任何一种情况下,都必须在调用 Queue() 或 QueueCallback() 之前调用 Setup()、TearDown() 或两者。 Setup() 和 TearDown() 方法将信息保存在仅供工作队列管理器使用的内部全局变量中。当任何工作人员作业从该队列开始其第一个工作项时,该工作人员作业首先检查工作管理器队列全局变量以查看是否有任何设置逻辑。如果是这样,worker 作业将执行该逻辑,然后启动工作项。 worker 作业不会再次执行设置逻辑。类似地,在任何工作作业完成队列中的最后一个工作项后,该工作作业检查是否有任何拆卸逻辑。如果是这样,worker 作业将执行该逻辑。

下面提供了这些方法的详细信息:

Setup()

method Setup(work As %String, args... As %String) as %Status 指定工作进程在处理队列中的第一项之前要调用的代码。如果使用此方法,则必须在调用 Queue() 或 QueueCallback 方法之前调用它。 Setup() 接受以下参数:

work - 要执行的设置代码。此参数支持的语法与 Queue() 方法的 work 参数支持的语法相同,这在上一节中进行了描述。 args - 此代码的参数的逗号分隔列表。要将多维数组作为参数传递,可以在该参数前面加上句点,以便通过引用传递它。应该保持在这些参数中传递的数据的大小相对较小。要提供大量信息,可以使用全局而不是传递参数。 TearDown()

method TearDown(work As %String, args... As %String) as %Status 指定工作进程在处理完队列中的最后一项后调用以将进程恢复到其先前状态的代码。如果使用此方法,则必须在调用 Queue() 或 QueueCallback 方法之前调用它。

TearDown() 接受与 Setup() 方法相同的参数。但是,work 参数指定要执行的拆解代码。

0
0 114
文章 姚 鑫 · 七月 6, 2022 4m read

第二十章 使用工作队列管理器(三)

管理类别

一个类别是一个独立的worker jobs池。当初始化一组worker jobs时,可以指定提供worker的类别。如果集合中的任何worker jobs在执行work项时请求额外的worker jobs,则新的worker jobs来自同一类别。

例如,假设系统提供的 SQL 类别分配了最多 8 个worker。然后,假设与BusinessIntelligence相关的流程创建了一个类别,并将最多四个worker分配给该类别。如果 SQL 池中的所有worker在给定时间都参与了工作,则 BusinessIntelligence 类别中的worker可能仍然可以立即处理工作项。

系统包括两个不能删除的类别:SQLDefault。 SQL 类别适用于系统执行的任何 SQL 处理,包括查询的并行处理。当在未指定类别的情况下初始化一组worker jobs时,默认类别提worker jobs。

每个类别都具有影响该类别中每个工作队列的行为的属性。这些属性是:

DefaultWorkers

当创建此类别中的工作队列且未指定worker job 计数时,这将成为工作队列中worker job 的数量。此属性的默认值是核心数。

MaxActiveWorkers

在此类别的job服务请求池中保留的活动worker job的最大数量。检测到空闲job并自动启动新job以将最大活动job数保持在此限制附近。默认值为核心数的两倍。

MaxWorkers

此类别中工作队列的最大worker job数。如果在创建工作队列时指定了更多的worker job,则使用此限制。默认值为核心数的两倍。

要创建类别、调整类别属性和删除自定义类别,请导航到System Administration > Configuration > System Configuration > WQM Categories.。自定义类别的名称区分大小写,可能包含字母、数字、下划线、破折号和句点。

使用回调

回调是工作队列管理器在完成工作项后必须执行的代码。可以使用回调有两个原因:

  • 执行依赖于工作项完成的工作
  • 如果选择异步完成工作项,则表示所有排队的工作都已完成

包括工作项的回调

要添加回调,请在将工作项添加到工作队列时调用 QueueCallback() 方法而不是 Queue() 方法:

method QueueCallback(work As %String, callback As %String, args... As %String) as %Status

work 和 args 方法与 Queue() 方法相同。但是,回调参数使用以下语法指定要执行的回调代码:

  • ##class(Classname).ClassMethod 用于类方法
  • $$entry^rtn 用于子程序

类方法或子例程必须以相同的顺序接受与主工作项相同的参数。主进程将相同的参数传递给主工作项和回调代码。

回调代码可以访问以下公共变量:

  • %job,其中包含实际完成工作的进程的作业 ID
  • %status,其中包含工作单元返回的%Status
  • %workqueue,即工作队列实例的OREF

这些公共变量在回调中可用,但在工作项中不可用。

包括回调以确定完成

可以轮询工作队列管理器以确定完成,而不是使用 WaitForComplete() 方法等待工作队列中的所有排队工作完成后再返回主进程,如下所示:

  • 如上一节所述,使用 QueueCallback() 方法而不是 Queue() 方法将工作项添加到工作队列。
  • 当所有工作项的工作完成后,在回调代码中将公共变量 %exit 设置为 1
  • 使用 Wait() 方法而不是 WaitForComplete() 方法:
method Wait(qspec As %String, byRef AtEnd As %Boolean) as %Status

Wait() 方法等待来自回调的信号返回给调用者。具体来说,它等待回调代码将公共变量 %exit 设置为等于 1Wait() 通过引用返回 AtEndAtEnd1 时,所有工作都已完成。或者,如果 AtEnd0,则一个或多个工作项未完成。

控制当前设备的输出

默认情况下,如果工作项向当前设备生成输出(WRITE 语句),工作队列会将输出保存在缓冲区中,直到 WaitForComplete()Wait() 结束。如果希望工作项更早地生成输出,请让该工作项调用 %SYSTEM.Context.WorkMgr 类的 Flush() 类方法,例如:

set sc = $system.Context.WorkMgr().Flush()

当工作项调用此方法时,会导致父工作队列写入工作项的所有已保存输出。

此外,可以使用 -d 标志来禁止对当前设备的所有输出。在这种情况下,Flush() 方法什么也不做,因为没有输出。

暂停和恢复工作队列

%SYSTEM.WorkMgr 类提供了可用于在工作队列中暂停和恢复工作的方法:

Pause()

method Pause(timeout As %Integer, ByRef completed As %Boolean = 0) as %Status

阻止与此工作队列关联的worker jobs接受来自此工作队列的其他项目。 Pause() 方法还会停止任何正在进行的工作项。

timeout 参数表示方法在停止正在进行的工作项之前等待的时间量(以秒为单位)。超时时间过后,该方法返回完成值,该值指示调用 Pause() 方法时正在进行的工作项是否已完成。因此,可以传入超时值 0 以立即知道worker jobs是否完成了工作队列中的所有工作项。

Resume()

method Resume() as %Status

如果之前已使用 Pause() 方法暂停,则恢复此工作队列中的工作。具体来说,此方法使工作队列进程能够接受并启动工作队列中的任何其他项目。

0
0 122
文章 姚 鑫 · 七月 5, 2022 5m read

第十九章 使用工作队列管理器(二)

基本工作流程

可以通过执行以下步骤来使用工作队列管理器:

  1. ObjectScript 代码划分为工作单元,这些工作单元是满足特定要求的类方法或子例程。
  2. 创建一个工作队列,它是 %SYSTEM.WorkMgr 类的一个实例。为此,请调用 %SYSTEM.WorkMgr 类的 %New() 方法。该方法返回一个工作队列。

可以指定要使用的并行worker jobs的数量,也可以使用默认值,这取决于机器和操作系统。此外,如果已创建类别,则可以指定应从中获取job的类别。

创建工作队列时,工作队列管理器会创建以下工件:

  • 包含有关工作队列的信息的全局变量,例如工作队列在哪个命名空间中运行
  • 工作队列必须处理的序列化工作单元的位置和事件队列
  • 在工作队列完成处理工作单元时创建的完成事件的位置和事件队列
  1. 将工作单元(也称为工作项)添加到工作队列。为此,可以调用 Queue()QueueCallback() 方法。作为参数,传递类方法(或子例程)的名称和任何相应的参数。

对添加到队列的项目立即开始处理。

如果队列中的项目多于队列可用的worker jobs,则job会竞争清空队列。例如,如果有 100 个项目和四个job,则每个job从队列的头部移除一个项目,处理它,然后返回到队列的头部以移除并处理另一个项目。这种模式一直持续到队列为空。

工作队列管理器在运行工作项时使用调用者的安全上下文。

当对工作项进行排队时,工作队列管理器会执行以下任务:

  • 序列化构成工作单元的参数、安全上下文和类方法或子例程,然后将序列化的数据插入到列出与工作队列关联的工作单元的全局global中
  • 发出工作队列上的事件信号
  • 如果需要额外的worker jobs并且可用于处理工作单元,则导致worker jobs附加到工作队列并减少可worker jobs的数量
  1. 等待工作完成。为此,可以调用工作队列的 WaitForComplete() 方法。

工作队列管理器然后执行以下任务:

  • 等待完成事件
  • 向终端显示工作负载指标等输出
  • 收集与工作单元相关的任何错误
  • 如果使用 QueueCallback() 方法将工作单元添加到工作队列,则运行回调代码
  1. 根据应用程序继续处理。

以下示例显示了这些基本步骤:

ClassMethod WorkJob(){
	s queue = ##class(%SYSTEM.WorkMgr).%New() 
	for i = 1 : 1 : filelist.Count() {
		s sc = queue.Queue("..Load", filelist.GetAt(i))
		if $$$ISERR(sc) { 
		    ret sc
		}
	}
	s sc = queue.WaitForComplete()
	if $$$ISERR(sc) { 
		ret sc
	}
}

该代码初始化工作队列管理器,然后遍历文件列表。对于每个文件,代码添加一个加载文件的工作队列项。添加所有工作队列项后,代码等待工作完成。

注意: %SYSTEM.WorkMgr 类支持更复杂的工作流以及本文档后面描述的方法。

基本方法

要完成上一节中描述的步骤,可以使用 %SYSTEM.WorkMgr 类的以下三个方法:

%New()

classmethod %New(qspec As %String = "", numberjobs As %Integer, category) as WorkMgr

创建、初始化并返回一个工作队列,它是可用于执行并行处理的 %SYSTEM.WorkMgr 类的一个实例。该方法接受以下参数:

  • qspec - 影响在此工作队列中运行的代码的一串编译器标志和限定符。
  • numberjobs - 在此工作队列中使用的最大并行worker jobs数。默认值取决于机器和操作系统的特性。
  • category - 提供要在此工作队列中使用的 worker jobs的类别的名称。

系统在创建时不会将任何工作任务分配给队列。只有在将工作单元添加到工作队列后,才会分配工作人员作业。

Queue()

method Queue(work As %String, args... As %String) as %Status

将工作单元添加到工作队列。该方法接受以下参数:

  • work

要执行的代码。通常,代码应该返回一个 %Status 值来指示成功或失败。

如果代码返回 %Status 值,可以使用以下语法:

  • ##class(Classname).ClassMethod 用于类方法,其中 Classname 是类的完全限定名称,ClassMethod 是方法的名称。如果方法在同一个类中,可以使用语法 ..ClassMethod,如示例中所示。
  • $$entry^rtn 用于子例程,其中 entry 是子例程的名称,rtn 是例程的名称。

如果代码未返回 %Status 值,请改用以下语法:

  • =##class(Classname).ClassMethod 用于类方法(或 =..ClassMethod 如果方法在同一个类中)

  • entry^rtn 子程序

  • args

类方法或子例程的参数的逗号分隔列表。要将多维数组作为参数传递,请照常在该参数前面加上句点,以便通过引用传递。

在这些参数中传递的数据的大小应该相对较小,以充分利用框架。要传递大量信息,请使用全局而不是参数。

当对工作单元进行排队时,系统会一次分配一个工作程序作业,最多为创建工作队列时指定的 numberjobs 值或最多为默认值。此外,调用者的安全上下文被记录下来,每个工作项都在该安全上下文中运行。

WaitForComplete()

method WaitForComplete(qspec As %String, errorlog As %String) as %Status

等待工作队列完成所有项目,然后返回一个 %Status 值来指示成功或失败。 %Status 值包含来自工作项返回的所有 %Status 值的信息。该方法接受以下参数:

  • qspec - 一串编译器标志和限定符。
  • errorlog - 任何错误信息的字符串,作为输出返回。

工作队列的属性

每个工作队列(或 %SYSTEM.WorkMgr 的实例)都具有以下属性:

NumWorkers

分配给工作队列的worker jobs数。

NumActiveWorkers

当前活跃worker的数量。

此外,工作队列所属类别的属性决定了工作队列的行为。

0
0 111
文章 姚 鑫 · 七月 4, 2022 7m read

第十八章 使用工作队列管理器(一)

工作队列管理器是的一项功能,使能够通过以编程方式将工作分配给多个并发进程来提高性能。在引入工作队列管理器之前,可能已经使用 JOB 命令在应用程序中启动多个进程并使用自定义代码管理这些进程(以及任何导致的故障)。工作队列管理器提供了一个高效且直接的 API,使能够卸载流程管理。

代码在多个地方内部使用工作队列管理器。可以将它用于自己的需求,如以下部分中的高级描述。

背景

计算机硬件开发的最新创新趋向于高性能、多处理器或多核架构。与此同时,内存和网络设备的速度也只是慢慢地提高了。 开发了工作队列管理器以响应这些趋势并根据以下原则:

  • 硬件资源,包括 CPU 和 I/O、内存和网络设备,都是固定的。
  • 必须尽可能高效地使用硬件资源,以最大限度地提高其执行业务任务的速度。
  • 为了实现最大效率,工作队列管理器必须改善在执行ObjectScript 代码时可能出现的 CPU 利用率不足的问题。
  • 解决 CPU 利用率不足的方法包括排队和优先级划分。

尽管整个数据平台旨在尽可能高效地利用系统中的硬件资源,但该平台的工作队列管理器功能专门设计用于利用现代硬件配置中可用的额外 CPU 资源。工作队列管理器有两个关键用途:

  • 提供一个框架,使能够将大型编程任务分解成更小的块,以便在多个并发进程中执行。通过一次使用多个 CPU,工作队列管理器显着减少了处理大型工作负载所需的时间。
  • 通过管理系统任务一次处于活动状态的JOB的数量来控制系统上的总 CPU 负载。

ObjectScript CPU 利用率

通常,ObjectScript 代码在单个进程中运行并且仅使用一个处理器内核。对于处理相对较少的指令和事务之间的全局引用的事务数据库应用程序,这种方法效果很好。事实上,数据平台的一个关键特性是事务工作负载的大规模可扩展性。该平台优化处理大量用户一次请求的大量相对较小的工作单元。

一些较新类型的工作负载(例如,分析工作负载)与 最初为优化而构建的工作负载不同。例如,较新的工作负载可能涉及处理一个需要对数百万行执行各种操作的 SQL 查询。为了加快此类工作负载的处理速度,开发了工作队列管理器,它将整体工作负载分解为更小的块,并行处理这些块,并将每个块的结果中继回父进程,然后父进程可以中继结果还给你。换句话说,工作队列管理器是一种类似于Queues 的机制,它使在中构建其应用程序的开发人员能够将大型任务分解为并行处理的较小任务。

工作队列管理器的功能

工作队列管理器包括几个关键特性:

  • 低延迟和开销
  • 可扩展性
  • 与操作系统的合作
  • 灵活性
  • 高水平的控制和报告

低延迟和开销

工作队列管理器专为低延迟和低开销而设计。例如,考虑一个程序化任务,系统需要 10 分钟才能按顺序处理。如果系统有 10 个内核,那么将任务拆分并在每个内核上并行处理十分之一的工作会更有效率。实际上,如果拆分任务、排队每个任务、启动工作作业和收集每个任务完成的通知所涉及的开销不需要任何额外的时间,那么可以得到结果 10快几倍。工作队列管理器被设计成开销任务导致低延迟。

可扩展性

为了最大限度地提高性能,工作队列管理器能够使用系统上的所有 CPU 资源来处理单个任务。实际上,工作队列管理器会限制给定类型的任务可以使用的核心数量,以确保系统上的所有工作负载都可以得到有效处理。

与操作系统的合作

使用用于大型事务性数据库应用程序的传统 ObjectScript 代码,操作系统可能会花费大量资源在编程任务之间切换,这有时称为上下文切换。由于工作队列管理器在每个内核上都采用了排队机制,因此对上下文切换的需求大大减少。只有当工作队列管理器管理的活动作业的数量超过可用内核的数量时,操作系统才需要进行上下文切换。这样,排队工作通常会提高性能。

灵活性

工作单元是采用一组参数并满足关于工作单元中描述的要求的类方法或子例程。可以在这些约束中表示的任何逻辑都可以由工作队列管理器处理,从而为提供极大的灵活性。

高水平的控制和报告

工作队列管理器为提供对系统上 CPU 资源使用方式的高级控制。例如,可以创建job类别并定义分配给这些类别的工作人员job的数量。此外,工作队列管理器提供工作负载指标,以便可以实时监控系统上的负载。

关于工作单元

工作队列管理器通过处理工作单元(也称为工作项)来发挥作用,这些工作单元是满足以下要求的 ObjectScript 类方法或子例程:

  • 类方法或子程序可以独立处理。例如,一个工作单元不能依赖于不同工作单元的输出。由于工作单元可以按任何顺序处理,因此需要独立性。但是,如果需要,可以使用回调按顺序执行工作。
  • 类方法或子例程的大小约为数千行 ObjectScript 代码。此要求确保框架的开销不是一个重要因素。

此外,最好使用大量(例如,100 个)较小的工作单元,而不是使用少量非常大的工作单元(例如,4 个)。以这种方式分配工作允许系统在更多 CPU 内核可用时进行扩展。

  • 该代码返回一个 %Status 值来指示成功或失败,以便 WaitForComplete() 方法可以返回一个 %Status 值来指示整体成功或失败。或者,工作单元可以抛出异常,该异常被捕获、转换为 %Status 值并在主进程中返回。
  • 如果代码将相同的全局更改为不同的工作单元,则必须采用锁定策略来确保一个work JOB在另一个work正在读取它时不能更改全局。
  • 该代码不包括 news, kills, unlocks,因为这些会干扰框架。
  • 如果代码包含用于存储数据的进程私有全局变量,则这些进程私有全局变量不会从主进程或任何其他块访问。这个要求是必要的,因为多个作业处理每个块。
  • 作为类方法或子例程的一部分调用的任何逻辑都被正确清理,以便分区中没有变量、锁、进程专用全局变量或其他工件。此要求很重要,因为随后将使用相同的流程来处理完全独立的工作项。

要使用工作队列管理器,必须将一些程序化工作划分为工作单元。

关于 Worker Jobs

Worker jobs是为工作队列管理器完成工作单元的进程。通过使用 %SYSTEM.Process 类,可以像其他进程一样查看、管理和监视工作作业。如果需要知道给定进程是否是工作作业,可以在进程内调用 $system.WorkMgr.IsWorkerJob()

工作队列管理器使用控制器进程来指导工作job,控制器进程是一个执行以下操作的专用进程:

  • 启动worker jobs
  • 管理worker jobs的数量
  • 检测并报告暂停的worker jobs
  • 记录工作负载指标
  • 检测非活动工作队列
  • 删除工作队列

worker jobs可以处于以下任何状态:

  • 等待附加到工作队列
  • 等待工作单位。在发布之前,worker jobs只能处于这种状态很短的时间。
  • 激活。只有当它在执行一个工作单元时进行转发过程时,worker jobs才处于活动状态。
  • 在处理工作单元时被锁或事件阻塞。被阻止的worker jobs未处于活动状态。如果worker jobs被阻塞并且工作队列中有额外的job,工作队列管理器可以激活不工作的woker或启动新woker。当worker jobs不再被阻塞时,活动worker jobs的数量可能会超过为工作队列指定的活动worker jobs的最大数量。如果发生这种情况,控制器进程将淘汰下一个完成工作单元的worker jobs。因此,当worker jobs的活动数量超过为给定工作队列指定的worker jobs的最大数量时,可能会有很短的时间段。
  • 已停用并可快速激活

未使用的worker jobs 在短时间内仍可供其他工作队列管理器队列使用。超时期限可能会发生变化,并且故意未记录在案。超时期限到期后,worker 被移除。

如果worker jobs正在积极处理已删除或清除的队列的工作项,则系统会等待很短的时间,然后发出 EXTERNAL INTERRUPT 错误。如果worker jobs在错误后继续处理,系统会等待 DeleteTimeout 属性中指定的秒数,然后强制终止worker并启动新worker来处理工作单元。

超级服务器启动worker jobs,这意味着它们以超级服务器进程使用的操作系统用户的名称运行。此用户名可能与当前登录的操作系统用户不同。

0
0 123
文章 姚 鑫 · 七月 3, 2022 5m read

第十七章 进程内存

介绍

进程使用许多不同的资源来实现其目标。其中包括部分或全部 CPU 周期、内存、外部存储、网络带宽等。这篇文章是关于内存使用的。具体来说,它处理为数据存储分配的内存,例如:

  • 公共和私有变量

当第一次为它们分配值时,它们被分配了内存空间。在局部数组的情况下,局部变量名称加上所有下标的值的组合引用单个变量值。

除了包含极长字符串的变量外,变量会占用与 $STORAGE 相关的空间。包含极长字符串的变量以不同方式存储,并且不占用 $STORAGE 中的空间。

  • 对象实例

每当实例化一个对象时,都会分配空间来保存对象的当前内容以及它所引用的对象。删除最后一个对象引用时返回该空间。

  • 本地 I/O 缓冲区

将与该进程正在使用的设备相关联的 I/O 缓冲区存储在进程空间中。

管理进程空间

进程从用于上述实体的初始内存池开始。当应用程序创建它们时,它们会消耗池中的内存;当应用程序删除它们时,它们的内存将返回到池中。例如,当一个例程开始执行时,总是会创建消耗一些内存的局部变量;当例程返回并且这些变量超出范围时,这些变量使用的内存将被返回并可供重用。

当应用程序需要内存,并且进程在其内存池中没有足够大(连续)的可用内存区域来满足需求时,该进程会从底层操作系统请求额外的内存块以添加到其池中。稍后,如果该内存块完全未使用,它将返回给操作系统。由于为实体分配内存的顺序和从内存中删除这些实体的顺序不一定是彼此的镜像,因此随着执行的进行,内存在某种程度上会变得碎片化。这会影响上述操作系统的内存分配和释放。

$ZSTORAGE

进程最多可使用 2TB 内存。为了帮助管理内存使用, 为管理员或应用程序提供了一种方法来设置较小的内存消耗限制。该值存储在每个进程的系统变量 $ZSTORAGE 中,因此 $ZSTORAGE 始终包含进程内存的最大允许大小(以 KB 为单位)。

$ZSTORAGE 的值以 1KB 为单位指定。允许的最小值为 256,即 256KB。进程可以为 $ZSTORAGE 设置的最大值是 2TB (231 * 1KB) 内存。尝试设置小于最小值或大于最大值的值将分别默认为最小值或最大值。

每个进程内存最大值

此值通过管理门户设置。要访问相关页面,请选择系统管理 > 配置 >系统配置 > 内存和启动。在出现的页面上,设置Maximum Per-Process Memory (KB) 中的值。

在配置文件 (iris.cpf) 中,此参数称为 bbsiz。此值是进程启动时 $ZSTORAGE 的初始值。

注意:也可以在通过 ObjectScript JOB 命令启动进程时设置进程的内存限制。

$STORAGE

系统变量 $STORAGE 表示仍在运行的进程可用的存储量。它以字节为单位。当进程对内存的请求大于 $STORAGE 中的值或从操作系统分配内存的请求失败时,它会生成 <STORE>错误。

<STORE> 错误

当满足进程的内存请求会导致 $STORAGE 的值变为负数,或者操作系统分配内存的请求失败时,它会生成 <STORE>错误。关于处理 $STORAGE 变为负数的 <STORE> 错误, 进程可以被认为处于以下两种模式之一:正常模式和低内存模式。

  • 正常模式

当进程处于正常模式并请求内存,否则会导致 $STORAGE 变为负数时,该进程会抛出 <STORE> 错误并进入低内存模式。

  • 低内存模式

在低内存模式下,允许操作将 $STORAGE 推为负数,以便为应用程序提供一些额外的内存来处理错误和清理。当处于低内存模式的进程释放内存时,$STORAGE 的值至少上升到 256KB(或 $ZSTORAGE25%,如果它更低),该进程将返回正常模式。在低内存模式下,$STORAGE 的下限约为 -1MB。否则会导致 $STORAGE 低于该限制的任何操作都会导致 <STORE> 错误。下限的值由进入低内存模式时的 $STORAGE 值定义,减去 1MB

注意:进程可以将 $ZSTORAGE 设置为其允许范围内的任何值。如果 $ZSTORAGE 设置为小于当前使用的值,则 $STORAGE 将具有负值。如果在进程处于正常模式时发生这种情况,分配内存的下一个操作将导致进程获得 <STORE> 错误并进入低内存模式,下限等于该值减去 1MB。如果在进程已经处于低内存模式时发生这种情况,则下限保持不变。

对于因超出 -1MB 低内存模式限制或未能从操作系统分配内存而导致的 <STORE>错误,由于可用内存太少,进程的行为是不可预测的。进<STORE>错误,或者错误处理程序可能无法被调用并且进程可能会停止。

错误处理程序可以使用以下一种或多种方法解决 <STORE> 错误:

  • 中止导致内存请求的计算,可能会释放计算在发生 <STORE>错误之前获得的任何存储空间。
  • 尝试通过删除不需要的数据来生成更多可用内存。
  • 执行任何必要的清理工作(例如关闭打开的文件),然后终止程序。
  • $ZSTORAGE 的值设置为更大的值,以允许进程继续并在将来请求更多内存。

特别注意事项

平台

大多数实例在每个进程的可分配空间少于 2TB 的系统上运行。在此类系统上,当 进程耗尽可用系统内存(实际物理内存加上可用交换空间)时,底层系统可能会以多种方式处理这种情况。一些例子是:

  • 在某些平台上,系统会发送一个信号,导 进程终止。
  • 在某些平台(例如 Linux 和 AIX)上,系统使用启发式算法来杀死它认为最具攻击性的进程。这可能是流程,但也可能是另一个选择的流程。
  • 一些系统通过产生使底层操作系统崩溃的内核“恐慌panic”来处理内存耗尽。
  • 一些系统可以处理内存耗尽的情况,但恢复可能会导致进程中的访问冲突。

良好的编程实践表明进程不应依赖于底层平台使用的错误恢复算法。相反,此类流程应提供充分的设计、测试和日志记录,以允许流程适当地估计和管理其自身的资源需求。

0
0 125
文章 姚 鑫 · 七月 2, 2022 5m read

第十六章 字符串本地化和消息字典(二)

XML 消息文件

XML 消息文件是消息字典的导出。这也是希望导入的任何消息的必需格式。

只要有可能,XML 消息文件应该使用 UTF-8 编码。但是,在某些情况下,开发人员或翻译人员可能会使用本地平台编码,例如 shift-jis,以便于编辑 XML 消息文件。无论 XML 文件使用何种编码,应用程序的语言环境都必须支持它,并且它必须能够表达该语言的消息。

XML 消息文件可能包含一种语言和多个域的消息。

<MsgFile> Element

<MsgFile> 元素是 XML 消息文件的顶级容器,每个文件只有一个 <MsgFile>元素。

<MsgFile>元素有一个必需的属性,Language<MsgFile>Language 属性的值是一个全小写的 RFC1766 代码,用于标识文件的语言。它由一个或多个部分组成:主要语言标签(例如 enja)可选地后跟连字符 (-) 和次要语言标签(en-gbja-jp)。

在以下示例中,此语言为“en”(英语)。

<?xml version="1.0" encoding="utf-8" ?>
<MsgFile Language="en">
   <MsgDomain Domain="sample">
       <Message Id="source">Source</Message>
       <Message Id="menu">Samples Menu</Message>
   </MsgDomain>
</MsgFile>

<MsgFile>必须包含至少一个<MsgFile> 元素。它可能包含多个<MsgFile>

<MsgDomain> Element

<MsgDomain>元素具有一个必需的属性域。 <MsgDomain> Domain 属性的值是用来组织应用程序中消息的域名之一。

任何 <MsgDomain>元素都可以包含零个或多个 <MsgDomain> 元素。

<Message> Element

<Message> 元素有一个必需的属性,Id。 <Message> Id 属性的值是用来组织应用程序中的消息的消息 ID 字符串之一。

任何 <Message> 元素都可以包含一个文本字符串。字符串可以由以下任何一项组成,单独或组合:

  • 文件格式允许的简单文本
  • 替换参数 %1%2%3%4
  • HTML 格式
  • ObjectScript 格式的字符串表达式

以下示例使用 %1%2、用于粗体格式的 HTML 标记以及两个连续双引号字符表示单个双引号的 ObjectScript 字符串约定:

<Message>
  The session $Username="&lt;b&gt;%1&lt;/b&gt;" $Roles="&lt;b>%2&lt;/b&gt;"
</Message>

管理消息字典

本节总结了使用消息字典时最常用的 %Library.MessageDictionary 方法。可以使用这些方法:

  • XML 消息文件导入消息
  • 将消息导出到 XML 消息文件
  • 从消息字典中删除消息
  • 在消息字典中列出消息

导入 XML 消息文件

要导入 XML 消息文件,请打开终端并执行以下操作:

  1. 更改为正在开发应用程序的命名空间:
 set $namespace = "myNamespace"
  1. 运行导入命令。默认情况下,每种语言都在一个单独的 XML 消息文件中,文件名末尾带有语言环境名称。因此:
  • 只能导入特定语言的那些消息:
 SET file="C:\myLocation\Messages_ja-jp.xml"
 DO ##class(%Library.MessageDictionary).Import(file)
  • 或者,为同一个应用程序导入多种语言:
 SET myFiles="C:\myLocation"
 DO ##class(%Library.MessageDictionary).ImportDir(myFiles,"d")
  1. 检查同一命名空间中的 ^IRIS.Msg 全局变量以查看结果。

以下主题总结了这两种导入方法。

导入特定 XML 消息文件

%Library.MessageDictionary 类方法 Import() 具有以下签名:

classmethod Import(filepath As %String, flag As %String = "") returns %Status
  • filepath - 导入由 filepath 指定的 XML 消息文件。确保目录中只有 XML 消息文件,因为其他 XML 文件会生成错误。
  • flag - (可选)如果提供,d 标志(显示)指示终端控制台将在导入文件时显示确认消息。否则,没有确认。

导入目录中的所有 XML 消息文件

%Library.MessageDictionary 类方法 ImportDir() 具有以下签名:

classmethod ImportDir(directory As %String, flag As %String = "") returns %Status
  • directory - 导入指定目录中的所有 XML 消息文件。
  • flag - (可选)如果提供,d 标志(显示)指示终端控制台将在导入文件时显示确认消息。否则,没有确认。

导出 XML 消息文件

要将消息字典的部分导出到 XML 消息文件,请在终端中执行以下操作:

  1. 更改为正在开发应用程序的命名空间:
 set $namespace = "myNamespace"
  1. 识别输出文件及其位置:
 SET file="C:\myLocation\Messages.xml"
  1. 运行导出命令:
  • 仅导出特定域中的那些消息可能是可行的:
 DO ##class(%Library.MessageDictionary).ExportDomainList(file,"myDomain")
  • 或者,导出命名空间中的所有消息:
 DO ##class(%Library.MessageDictionary).Export(file)

以一种语言导出特定域

%Library.MessageDictionary 类方法 ExportDomainList() 具有以下签名:

classmethod ExportDomainList(file As %String, domainList As %String, language As %String) returns %Status
  • file - (必需)此格式的输出文件名模板:filepath.ext 实际输出文件名将语言值附加到扩展名为 ext 的文件路径中。
  • domainList - (可选)要导出的域的逗号分隔列表。
  • language - (可选)仅导出指定的语言。该值必须是全小写的 RFC1766 代码。如果未提供,则该值默认为系统默认语言,该值存储在特殊变量 $$$DefaultLanguage 中。

以特定语言导出所有域

%Library.MessageDictionary 类方法 Export() 具有以下签名:

  • file - (必需)此格式的输出文件名模板:filepath.ext 输出文件的名称是 filepathlanguage-code.ext 例如,如果文件是 c:/temp/mylang_.txt 并且语言包括语言代码 ja- jp,则输出文件之一命名为 c:/temp/mylang_ja-jp.txt
  • languages - (可选)以逗号分隔的语言代码列表。列表中的每个值都必须是全小写的 RFC1766 代码。如果未指定语言或为空,则导出数据库中的所有语言。每种语言都使用为 file 参数描述的约定导出到一个单独的文件中。
  • flag - (可选)如果提供,则 s 标志(系统)指示除了应用程序消息字典之外还要导出系统消息字典。否则,仅导出应用程序消息字典。

删除消息

要删除消息,请使用以下命令:

 Set status = ##class(%MessageDictionary).Delete(languages,flag)

语言是可选的以逗号分隔的语言列表。如果未指定语言,则删除所有语言。默认值是仅删除应用程序消息。 s 标志(系统)是一个可选标志,指示是否也删除系统消息。与包含文件关联的消息名称总是被删除,但包含文件不会。还支持 d 标志(显示)。

列出消息

要获取为指定域加载了消息的所有语言的列表,请使用 GetLanguages() 方法:

 Set list = ##class(%MessageDictionary).GetLanguages(domain,flag)

GetLanguages() 以标准 RFC1766 格式返回语言代码的 %ListofDateTypes 格式列表,全部为小写。如果指定了域,则列表中仅包含指定域存在的语言。否则,所有语言都包含在列表中。 s 标志(系统)是一个可选标志,指示是否要返回系统或应用程序消息支持的语言。默认值是返回应用程序消息的语言。还支持 d 标志(显示)。

0
0 94
文章 姚 鑫 · 七月 1, 2022 4m read

第十五章 字符串本地化和消息字典(一)

本文概述了字符串本地化,并描述了如何导出、导入和管理消息字典。

字符串本地化

当本地化应用程序的文本时,会创建一种语言的文本字符串清单,然后当应用程序区域设置不同时,建立约定以另一种语言替换这些消息的翻译版本。

支持以下本地化字符串的过程:

  1. 开发人员在他们的代码中包含可本地化的字符串(在REST 应用程序或商业智能模型中)。

这种机制各不相同,但最常见的机制是 $$$Text 宏。代替硬编码的文字字符串,开发人员包含 $$$Text 宏的实例,为宏参数提供如下值:

  • 默认字符串
  • 此字符串所属的域(将字符串分组为域时,本地化更易于管理)
  • 默认字符串的语言代码
write "Hello world"

替换为

write $$$TEXT("Hello world","sampledomain","en-us")
  1. 编译代码时,编译器会在消息字典中为 $$$Text 宏的每个唯一实例生成条目。

消息字典是全局的,因此可以在管理门户中轻松查看(例如)。有一些类方法可以帮助完成常见任务。

  1. 开发完成后,发布工程师导出该域或所有域的消息字典。

结果是一个或多个 XML 消息文件,其中包含原始语言的文本字符串。

  1. 发布工程师将这些文件发送给翻译人员,请求翻译版本。

  2. 翻译人员使用他们喜欢的任何 XML 创作工具来处理 XML 消息文件。从本质上讲,它们将文本从原始语言翻译成新语言,而不改变周围的 XML

  3. 转换器返回一个新的 XML 消息文件,该文件具有相同的结构并且:

  • 标识 <MsgFile> 元素的语言属性的新 RFC1766 值。
  • 包含已识别语言的翻译文本。
  1. 发布工程师将翻译后的 XML 消息文件导入到导出原始文件的同一个名称空间中。

译文和原文在消息词典中并存。

  1. 在运行时,应用程序根据浏览器默认语言选择要显示的文本。

消息字典

消息字典是一个 global,包含按域名、语言名称和消息 ID 组织的文本字符串:

  • 每条消息的文本是最多 32K 个字符的字符串。如果数据库启用了长字符串,则字符串可能会更长,但默认最大值为 32K。消息可能只包含文本,也可能包含一个或多个由 %1%2 等指定的参数。当应用程序页面需要时,可以将这些参数替换为文本(例如错误消息中的文件名)显示消息。
  • 域名是任意字符串。它标识一组相关的文本项,例如特定应用程序或页面的所有消息。如果将域分配给一组邮件,可以稍后对具有相同域的所有邮件执行特定操作。

域名区分大小写,可以包含大小写字符。如果域名以 % 开头, 认为该域中的所有消息都是在所有命名空间中可见的系统消息。否则,当创建消息时,它仅在定义它的命名空间中可见。

  • 语言名称是符合 RFC1766 的全小写语言标记。它由一个或多个部分组成:主要语言标签(例如 enja)可选地后跟连字符 (-) 和次要语言标签(en-gbja-jp`)。
  • 消息 ID 是任意字符串;它唯一地标识一条消息。消息 ID 只需要在域内是唯一的。可以分配一个消息 ID 或允许编译器分配一个,这取决于用于创建消息的约定。消息 ID 区分大小写,可以包含大小写字符。

消息字典存储

每个用户定义的命名空间都将其消息字典存储在名为 ^IRIS.Msg 的下标全局中。 ^IRIS.Msg 中的下标顺序是域、语言和消息 ID

要查看命名空间的 ^IRIS.Msg

  1. 启动管理门户。
  2. 切换到感兴趣的命名空间。
  3. 单击System Explorer > Globals。
  4. IRIS.Msg 行中,单击查看。

例如

DHC-APP>zw ^IRIS.Msg
^IRIS.Msg("Ensemble")="zh-cn"
^IRIS.Msg("Ensemble","zh-cn",27956811)="Business Rules"
^IRIS.Msg("Ensemble","zh-cn",46484733)="Create New Message Routing Rule"
^IRIS.Msg("Ensemble","zh-cn",52267811)="Business Rule Log"
^IRIS.Msg("Ensemble","zh-cn",56439219)="Edit the configuration of this production."
^IRIS.Msg("Ensemble","zh-cn",59992850)="Edit, Start or Stop an Ensemble Production."
^IRIS.Msg("Ensemble","zh-cn",60154202)="The following PubSub Domain Names are currently defined. PubSub domain names are used to group PubSub subscribers and their associated subscriptions."
^IRIS.Msg("Ensemble","zh-cn",65030300)="Edit Role definition:"
^IRIS.Msg("Ensemble","zh-cn",74372598)="Create New Subscriber"
^IRIS.Msg("Ensemble","zh-cn",103132939)="Defined Data Transformations:"
^IRIS.Msg("Ensemble","zh-cn",140775628)="Suspended"
...
DHC-APP>zw ^CacheMsg
^CacheMsg("Ensemble")="zh-cn"
^CacheMsg("Ensemble","zh-cn",27956811)="Business Rules"
^CacheMsg("Ensemble","zh-cn",39977858)="Select a Business Process"
^CacheMsg("Ensemble","zh-cn",46484733)="Create New Message Routing Rule"
^CacheMsg("Ensemble","zh-cn",52267811)="Business Rule Log"
^CacheMsg("Ensemble","zh-cn",56439219)="Edit the configuration of this production."
^CacheMsg("Ensemble","zh-cn",59992850)="Edit, Start or Stop an Ensemble Production."

image

0
0 146