搜索​​​​

清除过滤器
文章
姚 鑫 · 十月 15, 2021

第四十六章 SQL命令 FROM(二)

[toc] # 第四十六章 SQL命令 FROM(二) ## %PARALLEL 这个可选关键字在查询的FROM子句中指定。 它建议 IRIS使用多个处理器(如果适用)并行处理查询。 这可以显著提高使用一个或多个`COUNT`、`SUM`、`AVG`、`MAX`或`MIN`聚合函数和/或`GROUP BY`子句的某些查询的性能,以及许多其他类型的查询。 这些通常是处理大量数据并返回小结果集的查询。 例如,`SELECT AVG(SaleAmt) FROM %PARALLEL User.AllSales GROUP BY Region`使用并行处理。 既指定单个字段又指定聚合函数且不包含`GROUP BY`子句的查询不能执行并行处理。 例如,`SELECT Name,AVG(Age) FROM %PARALLEL Sample`。 `Person`不执行并行处理,而是从`SELECT Name,AVG(Age) FROM %PARALLEL Sample.Person GROUP BY Home_State`执行并行处理。 `%PARALLEL`用于`SELECT`查询及其子查询。 `INSERT`命令子查询不能使用`%PARALLEL`。 指定`%PARALLEL`可能会降低某些查询的性能。 在具有多个并发用户的系统上使用`%PARALLEL`运行查询可能会导致整体性能下降。 注意:指定`%PARALLEL`的查询必须在读/写而不是只读的数据库中运行。 否则,可能发生``错误。 不管在`FROM`子句中是否存在`%PARALLEL`关键字,有些查询可能使用线性处理,而不是并行处理:有些查询不支持并行处理; 一些优化后的查询可能无法从并行处理中获益。 可以使用`Show Plan`确定 IRIS是否以及如何对查询进行了并行处理分区。 要确定当前系统上的处理器数量,使用 `%SYSTEM.Util.NumberOfCPUs()`方法。 ## %STARTTABLE 这个可选关键字指定查询优化器应该开始对`FROM`子句中列出的第一个表执行联接。 其余表的连接顺序留给查询优化器。 将此关键字与`%INORDER`进行比较,后者指定了完整的连接顺序。 `%STARTTABLE`不能与交叉连接或右外连接一起使用。 不能使用`%STARTTABLE`(或`%FIRSTTABLE`)从左`OUTER join`(或右`OUTER join`)的左边开始连接顺序。 如果指定的开始表与外部连接的要求不一致,则会生成一个`SQLCODE -34`错误:“优化器未能找到可用的连接顺序。” 为了避免这种情况,当与外部连接一起使用时,建议`%STARTTABLE`只与`ansi`风格的左外部连接或完整外部连接一起使用。 下表显示了在使用`%INORDER`和`%STARTTABLE`优化组合超查询父视图和内联视图时的合并行为: "" | 没有连接优化器的超查询 | 具有%STARTTABLE的超级查询 | 有`%INORDER`的超级查询 ---|---|---|--- 不带连接优化器的视图 | 如果可能,合并视图 |如果视图是超查询`start: don't merge`。否则,如果可能,合并视图。 |合并如果可能的话;视图的底层表是无序的。 使用`%STARTTABLE`查看 | 不合并 | 如果视图是超级查询`start: merge`,如果可能的话。视图的开始表变成了超级查询的开始表。否则,不合并。 | 不合并 使用`%INORDER`查看 | 不合并 | 不合并 | 如果视图不是由`%INORDER`控制的,则不要合并。否则,如果可能,合并视图;视图的顺序被替换为超级查询连接顺序。 `%FIRSTTABLE`提示在功能上与`%STARTTABLE`相同,但是提供了以任意顺序指定连接表序列的灵活性。 # FROM子句中的表值函数 表值函数是一个类查询,它被投影为一个存储过程,并返回单个结果集。 表值函数是任何具有`SqlProc TRUE`的类查询。 用作表值函数的类查询必须在`LOGICAL`或`RUNTIME`模式下编译。 当作为表值函数使用并在`RUNTIME`模式下编译时,表值函数查询将在`LOGICAL`模式下调用。 表值函数遵循与类查询的存储过程名称相同的命名约定。 参数括号是必须的; 括号可以是空的,可以包含一个字面值或一个主机变量,也可以包含一个用逗号分隔的字面值和主机变量列表。 如果不指定参数(空括号或空字符串),表值函数将返回所有数据行。 要使用表值函数发出查询,用户必须对定义表值函数的存储过程拥有`EXECUTE`权限。 用户还必须对表值函数查询访问的表或视图具有`SELECT`权限。 在下面的示例中,类查询`Sample.Person.ByName`被投影为一个存储过程,因此可以用作表值函数: ```sql SELECT Name,DOB FROM Sample.SP_Sample_By_Name('A') ``` 下面的动态SQL示例指定相同的表值函数。它使用`%Execute()`方法将参数值提供给`?`入参: ```java ClassMethod From() { s myquery="SELECT Name,DOB FROM Sample.SP_Sample_By_Name(?)" s tStatement = ##class(%SQL.Statement).%New() s qStatus = tStatement.%Prepare(myquery) if qStatus'=1 {w "%Prepare failed:" d $System.Status.DisplayError(qStatus) q} s rset = tStatement.%Execute("A") d rset.%Display() w !,"End of A data",!! s rset = tStatement.%Execute("B") d rset.%Display() w !,"End of B data" } ``` 表值函数只能在`SELECT`语句或`DECLARE`语句的`FROM`子句中使用。表值函数名可以用模式名限定,也可以用非限定名(没有模式名)限定;非限定名使用默认模式。在`SELECT`语句`FROM`子句中,只要可以使用表名,就可以使用表值函数。它可以在视图或子查询中使用,并且可以使用逗号分隔的列表或显式联接语法与其他表引用项联接。 表值函数不能直接用于`INSERT`、`UPDATE`或`DELETE`语句。但是,可以为这些命令指定子查询,以指定表值函数。 SQL没有为表值函数定义`EXTENTSIZE`,也没有为表值函数列定义`SELECTIVITY`。 # FROM子句中的子查询 可以在`FROM`子句中指定子查询。 这称为流子查询。 子查询被视为与表相同的处理方式,包括它在`JOIN`语法中的使用以及使用`as`关键字可选地分配别名。 `FROM`子句可以以任何组合包含多个表、视图和子查询,但要受`JOIN`语法的限制,如`JOIN`中所述。 ```sql SELECT name,region FROM (SELECT t1.name,t1.state,t2.region FROM Employees AS t1 LEFT OUTER JOIN Regions AS t2 ON t1.state=t2.state) GROUP BY region ``` 子查询可以指定`TOP`子句。 当与`TOP`子句配对时,子查询可以包含`ORDER BY`子句。 子查询可以使用`SELECT *`语法,但有以下限制:因为`FROM`子句的结果是值表达式,所以包含`SELECT *`的子查询只能生成一列。 子查询中的连接不能是`NATURAL`连接或接受`USING`子句。 ## 从子查询和%VID 当调用`FROM`子查询时,它为返回的每个子查询行返回一个`%VID`。 `%VID`是一个整数计数器字段; 它的值是系统分配的、唯一的、非空的、非零的、不可修改的。 `%VID`仅在显式指定时返回。 它以数据类型`INTEGER`返回。 因为`%VID`值是顺序整数,所以如果子查询返回的是顺序数据,则它们更有意义; 子查询只能在与`TOP`子句配对时使用`ORDER BY`子句。 因为`%VID`是一个顺序整数,所以可以用它来确定带有`ORDER BY`子句的子查询中项目的排名。 在下面的示例中,`10`条最新的记录按名称顺序列出,但是使用`%VID`值可以很容易地看到它们的时间戳排名: ``` SELECT Name,%VID,TimeStamp FROM (SELECT TOP 10 * FROM MyTable ORDER BY TimeStamp DESC) ORDER BY Name ``` `%VID`的一个常见用途是`“window”`结果集,将执行划分为符合显示窗口中可用行数的顺序子集。 例如,显示`20`条记录,然后等待用户按`Enter`键,然后显示下`20`条记录。 ```java ClassMethod From1() { s myq=4 s myq(1)="SELECT %VID,* " s myq(2)="FROM (SELECT TOP 60 Name,Age FROM Sample.Person " s myq(3)="WHERE Age > 55 ORDER BY Name) " s myq(4)="WHERE %VID BETWEEN ? AND ?" s tStatement = ##class(%SQL.Statement).%New() s qStatus = tStatement.%Prepare(.myq) if qStatus'=1 {w "%Prepare failed:" d $System.Status.DisplayError(qStatus) q} for i=1:10:60 { s rset = tStatement.%Execute(i, i+9) while rset.%Next() { d rset.%Print() } w !! } w "End of data" } ``` ```java DHC-APP>d ##class(PHA.TEST.SQLCommand).From1() 1 "Ahmed,Elmo X." 78 2 "Alton,Phil T." 68 3 "Anderson,Mario L." 78 4 "Bachman,Susan O." 88 5 "Basile,Filomena X." 87 6 "Browne,Robert X." 83 7 "Bukowski,Mario V." 86 8 "Burroughs,Barbara H." 86 9 "Cerri,Stavros Q." 96 10 "Chadbourne,Barb B." 94 ``` # 可选FROM子句 如果`SELECT`项列表(直接或间接)没有引用表数据,则`FROM`子句是可选的。 这种`SELECT`可以用于从函数、运算符表达式、常量或宿主变量返回数据。 对于不引用表数据的查询: - 如果省略`FROM`子句,则不管`TOP`关键字值如何,最多返回一行数据; `TOP 0`不返回任何数据。 `DISTINCT`子句被忽略。 不需要特权。 - 如果指定了`FROM`子句,则必须指定当前命名空间中的现有表。 必须对该表具有`SELECT`权限,即使该表没有被引用。 除非指定了`TOP`或`DISTINCT`子句,或者用`WHERE`或`HAVING`子句限制它,否则返回的相同数据行数等于指定表中的行数。 指定DISTINCT子句将输出限制为单行数据。 `TOP`关键字将输出限制为`TOP`值指定的行数; `TOP 0`不返回任何数据。 无论是否有`FROM`子句,都可以指定后续子句(如`GROUP BY`、`HAVING`或`ORDER BY`)。 `WHERE`或`HAVING`子句可用于确定是否返回结果,或返回多少相同的结果行。 即使没有指定`FROM`子句,这些子句也可以引用表。 可以指定`GROUP BY`或`ORDER BY`子句,但这些子句没有意义。 下面是不引用表数据的`SELECT`语句示例。 两个示例都返回一行信息。 下面的例子省略了`FROM`子句。 `DISTINCT`关键字不是必需的,但是可以指定。 不允许使用`SELECT`子句。 ```sql SELECT 3+4 AS Arith, {fn NOW} AS NowDateTime, {fn DAYNAME({fn NOW})} AS NowDayName, UPPER('MixEd cASe EXPreSSioN') AS UpCase, {fn PI} AS PiConstant ``` 下面的示例包含一个`FROM`子句。 `DISTINCT`关键字用于返回单行数据。 `FROM`子句表引用必须是一个有效的表。 这里允许使用`ORDER BY`子句,但没有意义。 注意,`ORDER BY`子句必须指定一个有效的选择项别名: ```sql SELECT DISTINCT 3+4 AS Arith, {fn NOW} AS NowDateTime, {fn DAYNAME({fn NOW})} AS NowDayName, UPPER('MixEd cASe EXPreSSioN') AS UpCase, {fn PI} AS PiConstant FROM Sample.Person ORDER BY NowDateTime ``` 下面的例子都使用了`WHERE`子句来决定是否返回结果。 第一个包含`FROM`子句,并使用`DISTINCT`关键字返回单行数据。 第二个省略了`FROM`子句,因此最多返回一行数据。 在这两种情况下,`WHERE`子句表引用必须是具有`SELECT`权限的有效表: ```sql SELECT DISTINCT {fn NOW} AS DataOKDate FROM Sample.Person WHERE FOR SOME (Sample.Person)(Name %STARTSWITH 'A') ``` ```sql SELECT {fn NOW} AS DataOKDate WHERE FOR SOME (Sample.Person)(Name %STARTSWITH 'A') ```

#Global Summit 2024

0 帖子0 关注者

#Global Summit 2023

3 帖子0 关注者
文章
姚 鑫 · 二月 15, 2021

第三十五章 Caché 变量大全 $ZNSPACE 变量

# 第三十五章 Caché 变量大全 $ZNSPACE 变量 包含当前命名空间名称。 # 大纲 ```java $ZNSPACE ``` # 描述 `$ZNSPACE`包含当前命名空间的名称。通过设置`$ZNSPACE`,可以更改当前名称空间。 要获取当前命名空间名称,请执行以下操作: ```java DHC-APP>SET ns=$ZNSPACE DHC-APP>WRITE ns DHC-APP ``` 还可以通过调用`%SYSTEM.SYS`类的`Namespace()`方法来获取当前命名空间的名称,如下所示: ```java DHC-APP>SET ns=$SYSTEM.SYS.NameSpace() DHC-APP>WRITE ns DHC-APP ``` 可以使用`%SYS.Namespace`类的`Existes()`方法测试命名空间是否已定义,如下所示: ```java DHC-APP>WRITE ##class(%SYS.Namespace).Exists("USER") 1 DHC-APP>WRITE ##class(%SYS.Namespace).Exists("LOSER") 0 ``` 对于UNIX®系统,默认命名空间建立为系统配置选项。对于Windows系统,它是使用命令行启动选项设置的。 命名空间名称不区分大小写。InterSystems IRIS始终以全大写字母显示显式名称空间名称,以全小写字母显示隐含的名称空间名称。 要获取指定进程的命名空间名称,请使用`%SYS.ProcessQuery`类的方法,如下例所示: ```java DHC-APP>WRITE ##CLASS(%SYS.ProcessQuery).%OpenId($JOB).NameSpaceGet() DHC-APP ``` # 设置当前命名空间 可以使用`ZNSPACE`命令、`SET $NAMESPACE`、`SET $ZNSPACE`或`%cd`实用程序更改当前名称空间。 - 在终端命令提示符下,`ZNSPACE`命令是更改名称空间的首选方式。`SET $ZNSPACE`在功能上与`ZNSPACE`命令相同。 - 在代码例程中,新建`$NAMESPACE`,然后设置`$NAMESPACE = NAMESPACE`是更改当前名称空间的首选方式。通过使用`new $NAMESPACE`和`SET $NAMESPACE`,可以建立一个名称空间上下文,该上下文在方法结束或发生意外错误时自动恢复到前一个名称空间。 可以使用`SET $ZNSPACE`更改进程的当前命名空间。将新命名空间指定为字符串文字或计算结果为带引号的字符串的变量或表达式。可以指定显式名称空间(`“NAMESPACE”`)或隐式名称空间(`“^SYSTEM^DIR”或“^^DIR”`)。 如果指定当前命名空间,则`SET $ZNSPACE`不执行任何操作,也不返回任何错误。如果指定了一个未定义的名称空间,则`SET $ZNSPACE`会生成一个``错误。 不能`new $ZNSPACE`特殊变量。 # 示例 在以下示例中,如果当前命名空间不是`USER`,则`SET $ZNSPACE`命令会将当前命名空间更改为`USER`。请注意,由于`if`测试,命名空间必须全部用大写字母指定。 ```java /// d ##class(PHA.TEST.SpecialVariables).ZNSPACE() ClassMethod ZNSPACE() { SET ns="USER" IF $ZNSPACE=ns { WRITE !,"命名空间已经 ",$ZNSPACE } ELSEIF 1=##class(%SYS.Namespace).Exists(ns) { WRITE !,"命名空间是 ",$ZNSPACE SET $ZNSPACE=ns WRITE !,"将命名空间设置为 ",$ZNSPACE } ELSE { WRITE !,ns," 不是定义的命名空间" } QUIT } ``` ```java DHC-APP>d ##class(PHA.TEST.SpecialVariables).ZNSPACE() 命名空间是 DHC-APP 将命名空间设置为 USER ``` 此示例要求`UnnownUser`已分配`%DB_IRISSYS`和`%DB_USER`角色。
文章
Michael Lei · 一月 17, 2023

ZPM 简单实现实战宝典

ZPM 设计用于与 InterSystems IRIS 数据平台的应用程序和模块一起使用。 它由两个组件组成:ZPN 客户端(用于管理模块的 CLI)和注册表(模块和元信息的数据库)。 我们可以使用 ZPM 来搜索、安装、升级、移除和发布模块。 使用 ZPM,可以安装 ObjectScript 类、前端应用程序、互操作性生产环境、IRIS BI 解决方案、IRIS 数据集或任何文件,例如嵌入式 Python wheel。  今天的这份实战宝典将分为 3 个部分: 1. 安装 ZPM 2. 生成模块 3. 在注册表中查找、安装、发布模块   1. 安装 ZPM * 下载最新版本的 ZPM(它应该是一个 XML 文件)[下载链接](https://pm.community.intersystems.com/packages/zpm/latest/installer) * 将下载的 XML 导入到 IRIS(它只能部署到已打开 IRIS 的 IRIS 终端)并按 Enter 键 _write $SYSTEM.OBJ.Load("C:\zpm.xml", "c")_ 请注意,“C:\zpm.xml”是下载的 XML 文件的路径,这一步可能需要一些时间。 * 安装完成后,只需输入 _zpm_,按 Enter 键,您会看到您在 zpm shell 中 ![](/sites/default/files/inline/images/images/screenshot_2022-10-07_111845.png) 2. 生成模块 在开始生成模块之前,我们需要准备一个文件夹,里面有一个或多个可以加载的文件,因此我在 C 盘下创建了一个名为 zpm 的文件夹。 执行命令 _generate C:/zpm_ 在指定所有必要的内容后,您的第一个模块已成功生成,您还会看到  ![](/sites/default/files/inline/images/images/screenshot_2022-10-07_151452.png) 注意:  1. 模块版本正在使用语义化版本控制  2. 模块源文件夹是包含所有类文件的文件夹 3. zpm 还提供了一个选项,可以添加 web 应用程序和依赖项,本例中我将其留空 现在,打开文件资源管理器,您会看到一个名为“module.xml”的文件,如下面的屏幕截图所示 ![](/sites/default/files/inline/images/images/screenshot_2022-10-07_161718.png) 输入命令 _load C:\ZPM\ _,您会看到您的模块已重新加载、验证、编译和激活  ![](/sites/default/files/inline/images/images/screenshot_2022-10-12_110126.png)   3. 在注册表中查找、安装、发布模块 在当前注册表中查找可用的软件包:_zpm:USER>search_ 举例来说,从当前注册表安装软件包,让我们在公共注册表中安装一个名为 zpmshow 的模块:_zpm:USER>install zpmshow _(命令为 install "moduleName") 加载后发布模块:_zpm:USER>publish myFirstZPMDemo_ 可以使用 _zpm:USER>search_ 验证发布,本例中,您可以看到“myfirstzpmdemo 0.1.0”位于当前注册表中。 ![](/sites/default/files/inline/images/images/screenshot_2022-10-12_110359.png) 注意:如果发布模块时遇到以下错误:_“ERROR! Publishing module, something went wrong”(错误!发布模块时出错了)_,确保当前注册表的状态为“已启用”且可用。 可以使用_ zpm:USER>repo -list, _验证当前注册表的状态。   ![](/sites/default/files/inline/images/images/screenshot_2022-10-10_152312.png)   可用视频:[点击此处](https://www.loom.com/share/0ca097f0dea4476ea294841295f972b2%C2%A0%C2%A0)
文章
姚 鑫 · 二月 25, 2022

第六十五章 SQL函数 %INTERNAL

# 第六十五章 SQL函数 %INTERNAL 返回逻辑格式表达式的格式转换函数。 # 大纲 ``` %INTERNAL(expression) %INTERNAL expression ``` # 参数 - `expression` - 要转换的表达式。 字段名、包含字段名的表达式,或返回可转换数据类型(如`DATE`或`%List`)中的值的函数。 # 描述 `%INTERNAL`将表达式转换为逻辑格式,与当前选择模式(显示模式)无关。逻辑格式是数据的内存格式(对其执行操作的格式)。`%INTERNAL`通常用于选择列表`SELECT-ITEM`。 **可以在`WHERE`子句中使用`%INTERNAL`,但强烈建议不要使用`%INTERNAL`,因为使用`%INTERNAL`会阻止在指定字段上使用索引,并且`%INTERNAL`会强制所有比较区分大小写,即使该字段有默认排序规则也是如此。** 应用`%INTERNAL`会将列标题名称更改为诸如“`Expression_1`”之类的值;因此,通常需要指定列名别名,如下面的示例所示。 `%INTERNAL`将数据类型%DATE的值转换为整数数据类型值。`%INTERNAL`将数据类型`%TIME`的值转换为数字`(15,9)`数据类型值。之所以提供此转换,是因为ODBC或JDBC客户端不识别逻辑`%DATE`和`%TIME`值。 `%INTERNAL`是否转换日期取决于日期字段或函数返回的数据类型。`%INTERNAL`转换`CURDATE`、`CURRENT_DATE`、`CURTIME`和`CURRENT_TIME`值。它不转换`CURRENT_TIMESTAMP`、`GETDATE`、`GETUTCDATE`、`NOW`和`$HOROLOG`值。 不能将流字段指定为ObjectScript一元函数(包括所有格式转换函数,`%Internal`除外)的参数。`%INTERNAL`函数允许将流字段作为表达式值,但不对该流字段执行任何操作。 `%INTERNAL`是InterSystems SQL扩展。 要将表达式转换为显示格式,而不考虑当前的选择模式,请使用`%EXTERNAL`函数。要将表达式转换为`ODBC`格式,而不考虑当前的`SELECT`模式,请使用`%ODBCOUT`函数。 # 示例 下面的动态SQL示例以当前选择模式格式返回出生日期(道布)数据值,并使用`%INTERNAL`函数返回相同的数据。出于演示目的,在此程序中,为每次调用随机确定`%SelectMode`值: ```java ClassMethod Internal() { s tStatement = ##class(%SQL.Statement).%New() s tStatement.%SelectMode=$RANDOM(3) if tStatement.%SelectMode=0 {WRITE "Select mode LOGICAL",! } elseif tStatement.%SelectMode=1 {WRITE "Select mode ODBC",! } elseif tStatement.%SelectMode=2 {WRITE "Select mode DISPLAY",! } s myquery = 2 s myquery(1) = "SELECT TOP 5 DOB,%INTERNAL(DOB) AS IntDOB " s myquery(2) = "FROM Sample.Person" s qStatus = tStatement.%Prepare(.myquery) s rset = tStatement.%Execute() d rset.%Display() w !,"End of data" } ``` ```java DHC-APP>d ##class(PHA.TEST.SQLCommand).Internal() Select mode DISPLAY DOB IntDOB 04/25/1990 54536 01/02/2014 63189 01/02/2014 63189 01/28/1978 50066 5 Rows(s) Affected End of data ``` 下面的示例显示了此函数的两种语法形式;在其他方面它们是相同的。它们指定`%LIST`字段的`%EXTERNAL`(显示格式)、`%INTERNAL`(逻辑格式)和`%ODBCOUT`(ODBC格式): ```sql SELECT TOP 10 %EXTERNAL(FavoriteColors) AS ExtColors, %INTERNAL(FavoriteColors) AS IntColors, %ODBCOUT(FavoriteColors) AS ODBCColors FROM Sample.Person ``` ```sql SELECT TOP 10 %EXTERNAL FavoriteColors AS ExtColors, %INTERNAL FavoriteColors AS IntColors, %ODBCOUT FavoriteColors AS ODBCColors FROM Sample.Person ```
文章
shaosheng shengshao · 九月 14, 2022

windows下处理IIS在未安装但Healthshare已安装的时候,部署IIS服务并代理Healthshare

研究Healthshare2018在已经安装完成使用的情况下,部署IIS,并代理平台。之前看到可以通过单独的CSP Gateway安装包进行处理这种问题,该文主要是获取不到该安装包的时候可以如何实现IIS的处理。 第一步:首先按照网上教程部署IIS服务。安装完成之后会在C盘创建一个名叫intepub的包。 第二步:在inetpub包下面直接插入CSPGateway 。这个包是在其他先安装IIS下,后安装Healthshare的CSP网关服务的时候在inetpub下面自动插入的包,直接找一个复制过来。 第三步:打开IIS,创建一个叫csp的网站,按照下图配置。 第四步:在点击模块进行CSPms模块的添加。 接下来是配置好的CSPms的模块。 注意:我们从图中看到的CSPms模块是本机继承的模块,我们在右边添加本机模块的时候并不能找到C:\inetpub\CSPGateway\CSPms.dll这个模块,导致加不进去。那我们就直接在配置文件上面改。找到C:\Windows\System32\inetsrv\config该文件路径下的applicationHost.config文件,为了稳妥起见,我们先备份一下该文件。找到<globalModules>节点,在下面加入<add name="CSPms" image="C:\Inetpub\CSPGateway\CSPms.dll" />找到<modules>节点,在下面加入<add name="CSPms" />找到hiddenSegments节点,找到下面的bin参数,删除<add segment="bin " />,如下图所示。 保存文件,如果保存过程中出现不允许修改的问题,可以把文件贴到桌面,在这个文件上面改造,在copy回去直接替换。再回到iis页面,网站csp的模块里面就会出现对应的CSPms的本机模块。后面进行服务映射,双击点击处理程序映射。 第六步:接下来测试我们打开平台管理页面能不能打开。 如果出现了这个页面,那我们就快成功了,这个错误可能我们大家都有见过。我们打开C:\inetpub\CSPGateway下面的CSP.ini的文件。里面有个 有这个Password字段,因为我们的CSPGateway这个包是从其他服务器上copy过来的,这个密码只能用于那台服务,不能用于这个服务器,那我们可以拿57772端口的apache配置下的这个密码拿过来用。在安装路径下D:\InterSystems\HealthShare\CSP\bin的CSP.ini,也有一个Password字段,把这个密码copy过来放到C:\inetpub\CSPGateway下面的CSP.ini上。重启IIS服务,如果出现权限问题,也把CSP.ini拷贝到桌面,修改好后再贴回去进行替换。再次进行访问,就可以进去桌面了。第七步:点入到CSP网关管理,如果提示出现warning字眼,那是C:\inetpub\CSPGateway下面的用户权限问题。 对CSPGateway右键按照下图示例进行IIS_IUSRS用户权限的添加。对文件夹下的CSP.ini和CSP.log做一样的操作。 好文,欢迎参赛!
文章
姚 鑫 · 一月 25

第七章 C - D 开头的术语

# 第七章 C - D 开头的术语 #### 当前设备 (current device) **系统** 处理 `I/O` 命令的设备。当你登录时,当前设备是你的主设备,通常是你登录时使用的终端或个人计算机。 #### 当前目录 (current directory) **通用** 你当前工作的目录。 #### 基于游标的 SQL (cursor-based SQL) **SQL** 一种嵌入式 `SQL` 查询类型,打开一个游标来处理查询。当你的应用程序需要访问多行数据时,必须使用游标。游标像一个指针——它专注于访问和处理一行数据,然后移动到序列中的下一行。 #### 游标 (cursor) **SQL** 在多行数据中向前移动的迭代器。 #### 自定义存储 (custom storage) **对象(Objects)** 自定义存储允许你通过编写自己的存储接口方法实现来决定对象的存储结构。通常,使用自定义存储的类不会被投射到 `SQL`。 # 以 D 开头的术语 #### 数据库 (database) **通用** `IRIS.DAT` 文件。它可以包含代码和数据。通过全局、包和例程映射,任何给定的数据库都可以被多个命名空间使用。 #### 数据库缓存 (database cache) **系统** 用于缓存从数据库中检索的数据的系统内存(`RAM`),这样相同查询的重复实例可以从内存中而不是存储中检索结果,从而提供显著的性能提升。为了获得最佳性能,数据库缓存应至少与应用程序工作负载的工作集一样大。 #### 数据库加密 (database encryption) **系统** 将 `IRIS` 数据库以加密状态存储在磁盘上的过程。当 `IRIS` 从磁盘读取数据时,数据会在运行时自动解密,以便其合法用户可以访问数据。当数据写入磁盘时,会进行加密。磁盘上的数据也称为静态数据(`at rest`)。 #### 数据库加密密钥 (database-encryption key) **系统** 用于加密 `IRIS` 数据库的 `AES` 密钥。 #### 数据库完整性 (database integrity) **通用** 数据库在其内容或结构上保持未损坏的状态。数据库中的数据有时由于物理完整性退化而变得不可读。内部指针可能由于内部完整性退化而损坏。 #### 数据定义语言 (Data Definition Language) **InterSystems SQL** 数据定义语言(`DDL`)是一种基于命令的语言,用于创建、定义表的结构和删除表。 #### 数据位置 (data location) **系统** 全局所在的数据集。复制全局的全局集合和删除操作的来源是映射到全局数据位置的目录(或目录和系统)。
文章
YuHao Wan · 十一月 5, 2022

Caché实现SM4分组密码算法

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

第二十四章 Caché 变量大全 $ZA 变量

# <center> 第二十四章 Caché 变量大全 $ZA 变量 包含当前设备上最后一次读取的状态。 # 大纲 ```java$ZA``` # 描述 `$ZA`包含当前设备上最后一次读取的状态。 不能使用`SET`命令修改此特殊变量。尝试这样做会导致`<SYNTAX>`错误。 # 注意 ## `$ZA`,带终端I / O `$ZA`被实现为一系列位标志,每个位表示一条特定的信息。下表显示了可能的值、它们的含义,以及如何使用模(`#`)和整数除(`\`)运算符测试它们: 位 | 测试| 含义---|---|---0 |`$ZA#2`| `<CTRL-C>`已到达,无论是否启用中断。1 |`$ZA\2#2`| 读取超时。2 |`$ZA\4#2`| I/O错误。8 |`$ZA\256#2`| Caché检测到无效的转义序列。9 |`$ZA\512#2`| 硬件检测到奇偶校验或成帧错误。11 |`$ZA\2048#2`| 进程已与其主设备断开连接。12 |`$ZA\4096#2`|对于COM端口:CTS(清除发送)。从调制解调器发送到其计算机的信号,表示传输可以进行。对于TCP设备:设备在服务器模式下运行。13 |`$ZA\8192#2`|对于COM端口:DSR(数据集就绪)。从调制解调器发送到其计算机的信号,表示调制解调器已准备好运行。对于TCP设备:设备当前处于与远程主机通话的已连接状态。14 |`$ZA\16384#2`| 如果为真,则设置振铃。15 |`$ZA\32768#2`| 载波检测设置为真。16 |`$ZA\65536#2`|CE_BREAK COM端口错误状态。17 |`$ZA\131072#2`| CE_FRAME COM端口错误状态。18 |`$ZA\262144#2`| CE_IOE COM端口错误状态。19 |`$ZA\524288#2`| CE_OVERRUN COM端口错误状态。20 |`$ZA\1048576#2`| CE_RXPARITY COM端口错误状态。21 |`$ZA\2097152#2`| CE_TXFULL COM端口错误状态。22 |`$ZA\4194304#2`| TXHOLD通讯端口错误状态。在`ClearCommError()`返回的错误掩码中,如果下列任一字段为真,则设置为:fCtsHold、fDsrHold、fRlsdHold、fXoffHold、fXoffSent。`24 & 25 |$ZA\16777216#4|`Caché请求DTR(数据终端就绪)设置:0 = DTR关闭。 1 = DTR =开。 2 = DTR握手。设置为(1)时,表示已准备好发送和接收数据。 尽管`$ZA`显示的许多条件都是错误,但它们不会通过捕获`$ZTRAP`来中断程序的流程。 (具有中断功能的`<CTRL-C>`会陷阱到`$ZTRAP`。)与这些错误有关的程序在每次读取后必须检查`$ZA`。 COM端口使用位12至15、24和25报告调制解调器控制引脚的状态。无论端口的Caché调制解调器控制检查是打开还是关闭,都可以执行此操作。用户可以通过设置`OPEN`或`USE`命令`portstate`参数(具体为字节8)来启用或禁用COM端口的`$ZA`错误报告。如果启用了错误报告,则端口错误状态将在位16到22中报告。 可以使用`%SYSTEM.Process`类的`DisconnectErr()`方法来检测当前进程的调制解调器断开连接。可以通过设置`Config.Miscellaneous`类的`DisconnectErr`属性来建立系统范围的默认行为。 ## 带磁带I / O的$ZA 对于磁带I / O,`$ZA`中的位字段指示错误和特殊情况。在每个引用磁带设备的命令之后,Caché更新`$ZA`。 下表显示了磁带I/O的`$ZA`位的含义。请注意Trap列。字母Y表示`<MAGTAPE>`错误。如果设置了`$ZTRAP`变量,则Caché会发出相关的`$ZTRAP`错误代码。 位 | 值| 陷阱 |含义注意---|---|---|---0 |1| Y| 逻辑错误(读写混合),用于在读取和写入之间切换,或者关闭然后打开设备,或者发出前进空格、退格键或倒带命令。2 |4| N| 写保护始终反映`OPEN`或`USE`只读参数的状态。此位不反映磁带的物理写保护状态(写环或写锁定),因为许多版本的UNIX®在尝试实际写入磁带之前不会通知磁带写保护。如果尝试打开不带只读参数的写保护9磁道磁带,则Caché会设置此位并以只读方式打开磁带。未出现错误。3 |8| Y| 错误摘要错误摘要是导致Caché错误的所有条件(在Trap下标记为Y的所有条件)的逻辑或。5 |32| N| 磁带的开始[BOT]在UNIX®系统上,该位在倒带时设置,并在打开磁带时清除。6 |64| N| On Line 7 |128| Y|控制器或驱动器错误。10 |1024| N|磁带结尾[EOT]在大多数UNIX®平台上不支持。14 |16384| Y| Tape MarkCaché在遇到Read,Read Block,Forward Space或Backspace上的磁带标记时将Tape Mark位置1。这会将“错误摘要”位置1,并在“读取”,“读取标签”和“读取块”上将陷阱陷阱为`$ZTRAP`。15 |32768| Y| 磁带未准备好 一些位指示错误条件,而另一些位指示不一定产生错误的条件。为了监视这些非错误情况,程序必须在每次磁带操作后测试`$ZA`的相应位。例如,如果程序可能写在磁带末尾之外,则它必须检查位10(磁带末尾)。 要测试某个位,请将`$ZA`除以表中该位列出的值,然后执行模2运算。例如,以下命令检查是否设置了位14(磁带标记): ```javaUSE 47 IF $ZA\16384#2 {DO Endfile}``` 其中16384等于2等于14的幂,而#2表示模2运算。由于任何等于0的幂等于1,因此无需除数即可检查位0(逻辑错误)。例如: ```javaUSE 47 GOTO Logerr:$ZA#2```
文章
Nicky Zhu · 一月 8, 2021

Web 服务业务操作客户端 – 响应超时行为

调用 Web 服务的过程中,在期望的时间内未返回响应时,后续发生的情况由业务操作的几个设置来控制。(请注意,这也与非 SOAP 简单 HTTP 调用等有关)   3 个主要设置为: 响应超时 指定从远程 Web 服务器获取响应的超时时间。 重试间隔 尝试与 Ensemble 之外的目标进行连接之间等待的秒数。 故障超时 尝试与 Ensemble 之外的目标保持连接的总秒数。 经过此秒数时间后,业务操作将丢弃消息数据并返回错误代码。   归纳一下就是这样: 我们将等待 Web 服务器的响应,时间为“响应超时”所用的时间。 如果此时间过后未收到响应,我们将在“重试间隔”时间过后再次调用 Web 服务器。 如此继续尝试,直到时间(从第一次尝试算起)达到“故障超时”设置的时间秒数。   以下举例进行说明:   假定以下设置:   就是说: 响应超时 - 等待 7 秒以响应 重试间隔 - 每 10 秒重试一次 故障超时 – 30 秒后“放弃”重试   假定在 8 秒后返回响应,则情况如下: 在 00:00,我们进行第一次调用 在 00:07,由于未返回任何响应,我们在内部确认发生了“响应超时”错误(并在“事件日志”中记录“错误事件”),并根据重试策略和设置再次尝试 – “故障超时”尚未到达,因此引发了“需要重试标志”。 [在 00:08,Web 服务器返回一个响应,但由于我们已经将其记录为超时错误,因此我们不会收到此响应] 在 00:10,重试间隔时间已到,并且由于引发了“需要重试标志”,我们将再次调用 Web 服务。 在 00:17,如果仍未返回任何响应,我们将再次达到“响应超时”时间(请注意,在上面的“00:08/第 3 段中,返回的响应已被“忽略/丢弃”),因此我们再次在内部将此认为是错误(但这次不会再添加一个“错误事件日志”条目,只会在第一次尝试时记录此错误,而不是每次重试失败时都记录),由于我们尚未达到“故障超时”时间,因此需要再次引发“需要重试标志”。 [在 00:18,Web 服务器返回一个响应,这次我们还是接收不到] 在 00:20,又达到重试间隔时间,我们将尝试第 3 次调用。 在 00:27,仍无响应,再次发生“响应超时”错误,需要重试(仍未达到“故障超时”)。 [在 00:28,服务器未发回响应] 在 00:30,再次达到重试间隔时间,我们进行第 4 次尝试(也是最后一次)。 在 00:37,“响应超时”再次发生 – 这次,已达到“故障超时”时间,因此我们不会引发“需要重试标志”,而是放弃 – 我们在事件日志中记录一个错误事件,指出已达到故障超时时间,并且返回业务操作调用错误。   以下是上述示例的调用“证据”。   首先是服务器端 [来自 SOAP 日志] – 可以看到,其收到了 4 次调用/请求,均相隔 10 秒,每次在请求发出 8 秒后返回一个响应: * * * 05/31/2016 14:18:45 ********************* Input to Web service with SOAP action = http://tempuri.org/Test.WSTimeouts.WebService.GetResponse ...   05/31/2016 14:18:53 ********************* Output from Web service with SOAP action = http://tempuri.org/Test.WSTimeouts.WebService.GetResponse ...   05/31/2016 14:18:55 ********************* Input to Web service with SOAP action = http://tempuri.org/Test.WSTimeouts.WebService.GetResponse ...   05/31/2016 14:19:03 ********************* Output from Web service with SOAP action = http://tempuri.org/Test.WSTimeouts.WebService.GetResponse ...   05/31/2016 14:19:05 ********************* Input to Web service with SOAP action = http://tempuri.org/Test.WSTimeouts.WebService.GetResponse ...   05/31/2016 14:19:13 ********************* Output from Web service with SOAP action = http://tempuri.org/Test.WSTimeouts.WebService.GetResponse ...   05/31/2016 14:19:15 ********************* Input to Web service with SOAP action = http://tempuri.org/Test.WSTimeouts.WebService.GetResponse ...   05/31/2016 14:19:23 ********************* Output from Web service with SOAP action = http://tempuri.org/Test.WSTimeouts.WebService.GetResponse ...   现在,从 Ensemble BO/客户端,可以看到,有 4 次尝试,均相隔 10 秒,每次在 7 秒后记录响应超时错误。   客户端   05/31/2016 14:18:45 ********************* Output from Web client with SOAP action = http://tempuri.org/Test.WSTimeouts.WebService.GetResponse ...   05/31/2016 14:18:52 ********************* Input to Web client with SOAP action = http://tempuri.org/Test.WSTimeouts.WebService.GetResponse ERROR #5922: Timed out waiting for response string**** SOAP client return error. method=GetResponse, action=http://tempuri.org/Test.WSTimeouts.WebService.GetResponse      ERROR #5922: Timed out waiting for response   05/31/2016 14:18:55 ********************* Output from Web client with SOAP action = http://tempuri.org/Test.WSTimeouts.WebService.GetResponse ...   05/31/2016 14:19:02 ********************* Input to Web client with SOAP action = http://tempuri.org/Test.WSTimeouts.WebService.GetResponse ERROR #5922: Timed out waiting for response string**** SOAP client return error. method=GetResponse, action=http://tempuri.org/Test.WSTimeouts.WebService.GetResponse      ERROR #5922: Timed out waiting for response   05/31/2016 14:19:05 ********************* Output from Web client with SOAP action = http://tempuri.org/Test.WSTimeouts.WebService.GetResponse ...   05/31/2016 14:19:12 ********************* Input to Web client with SOAP action = http://tempuri.org/Test.WSTimeouts.WebService.GetResponse ERROR #5922: Timed out waiting for response string**** SOAP client return error. method=GetResponse, action=http://tempuri.org/Test.WSTimeouts.WebService.GetResponse      ERROR #5922: Timed out waiting for response   05/31/2016 14:19:15 ********************* Output from Web client with SOAP action = http://tempuri.org/Test.WSTimeouts.WebService.GetResponse ...   05/31/2016 14:19:22 ********************* Input to Web client with SOAP action = http://tempuri.org/Test.WSTimeouts.WebService.GetResponse ERROR #5922: Timed out waiting for response string**** SOAP client return error. method=GetResponse, action=http://tempuri.org/Test.WSTimeouts.WebService.GetResponse      ERROR #5922: Timed out waiting for response   以下是对 Ensemble 的可视化跟踪: 以下是事件日志条目:   以下为跟踪事件的日志示例[您可能需要放大以更好地读取文字]: 由此,可以看到上述示例的一些“内部运作”: 在日志行 ID #684,执行了首次调用 – 时间:17:09:16。 然后,在 7 秒之后 (09:23),我们收到响应超时错误 (#685)。 然后,操作记录了此错误 (#687),并确定等待 3 秒钟,直到达到重试间隔时间;10 秒重试间隔时间减去 7 秒响应超时时间 (#688-#690)。 经过 3 秒等待( 09:26;#691)之后,进行第 2 次尝试 (#692),结果相同,继续重复操作,直到第 4 次尝试 (#704) 。 在第 4 次尝试失败(09:53;#705)之后,由于达到超时时间(30 秒),因此未进行另一次尝试。  
文章
姚 鑫 · 一月 31, 2023

第六十二章 使用 SNMP 监控 IRIS

# 第六十二章 使用 SNMP 监控 IRIS 本附录描述了 `IRIS` 数据平台和 `SNMP`(简单网络管理协议)之间的接口。 `SNMP` 是一种通信协议,作为一种管理 `TCP/IP` 网络(包括单个网络设备和一般计算机设备)的方法已获得广泛接受。它的流行扩大了它作为许多企业管理工具的底层结构和协议的用途。这是它对 `IRIS` 的主要重要性:一种向各种管理工具提供管理和监控信息的标准方法。 `SNMP` 既是一种标准消息格式,也是管理对象的一组标准定义。它还提供用于添加自定义管理对象的标准结构,这是 `IRIS` 用来定义其管理信息以供其他应用程序使用的功能。 # 将 `SNMP` 与 `IRIS` 结合使用 `SNMP` 定义了客户端-服务器关系,其中客户端(网络管理应用程序)连接到在远程网络设备或计算机系统上执行的服务器程序(称为 `SNMP` 代理)。客户端请求并从该代理接收信息。有四种基本类型的 `SNMP` 消息: - `GET` – 获取特定托管对象的数据 - `GETNEXT` – 获取分层树中下一个管理对象的数据,允许系统管理员遍历设备的所有数据 - `SET`——为特定的管理对象设置值 - `TRAP`——被管理设备或系统发送的异步警报 `SNMP MIB`(管理信息库)包含管理对象的定义。每个设备都会发布一个文件,也称为其 `MIB`,该文件定义了它支持的标准 `MIB` 的哪一部分,以及托管对象的任何自定义定义。对于 `IRIS`,这是位于 `install-dir\SNMP` 目录中的 ISC-IRIS.mib 文件。 # `IRIS` 作为子代理 `SNMP` 客户端连接到 `SNMP` 代理,该代理正在侦听众所周知的地址端口 `161`。由于客户端希望连接到该特定端口,因此计算机系统上只能有一个 `SNMP` 代理。为了允许访问系统上的多个应用程序,开发人员可以实现主代理,它可以扩展或连接到多个子代理。 已将 `IRIS SNMP` 接口实施为子代理,旨在通过 `SNMP` 主代理进行通信。 `IRIS` 支持的大多数操作系统都提供一个 `SNMP` 主代理,它可以通过某种方式扩展以支持多个子代理。然而,这些代理中的许多都以专有且不兼容的方式实现了它们的可扩展性。 `IRIS` 使用代理扩展性 (`AgentX`) 协议实现其子代理,该协议是 `IETF` 提议的标准,如 `RFC 2741`中所述。 一些标准的 `SNMP` 主代理支持 `AgentX`。如果操作系统提供的 `SNMP` 主代理与 `AgentX` 不兼容,可以将其替换为公共域 `Net-SNMP` 代理。 注意:`Windows` 标准代理例外,它不支持 `AgentX`,`Net-SNMP` 版本可能不适合。对于这个例外,提供了一个 `Windows` 扩展代理 `DLL`,`iscsnmp.dll`,它处理标准 `Windows SNMP` 服务扩展 `API` 和 `IRIS AgentX` 服务器之间的连接。 # 在 `IRIS` 中管理 `SNMP`。 由于 `SNMP` 是标准协议, `IRIS` 子代理的管理是最少的。最重要的任务是验证系统上的 `SNMP` 主代理是否与代理扩展性 (`AgentX`) 协议兼容(请参阅 `IRIS` 作为子代理)并且它处于活动状态并正在侦听标准 `AgentX TCP` 端口 `705` 上的连接。 `Windows`系统,系统自动安装一个`DLL`来连接标准的`Windows SNMP`服务。验证 `Windows SNMP` 服务是否已自动或手动安装和启动。 重要提示:一些 `SNMP` 主代理,特别是 `Linux` 上的 `Net-SNMP`,默认情况下不启用 `AgentX`,并且一旦启用,默认情况下不使用 `TCP` 端口 `705`。对于 `Net-SNMP`,必须修改 `snmpd.conf` 文件以启用与 `IRIS` 子代理的通信。最新版本的 `Net-SNMP` 还实现了 `VACM`(基于视图的访问控制模型)安全性,默认情况下,只允许访问 `mib-2.system` 子树; `IRIS` 子代理启动并正常运行,但没有 `SNMP` 请求转发到 `IRIS`。必须扩展 `snmpd.conf` 中定义的“视图”以包括 `IRIS MIB` 子树。 接下来,使用以下步骤启用监控服务: 1. 导航到管理门户中的服务页面(系统管理 > 安全 > 服务)。 2. 单击 `%Service_Monitor` 服务。 3. 选中启用服务复选框并单击保存。 4. 返回到服务列表页面并确保启用了 `%Service_Monitor` 服务。 最后,使用以下步骤将 `IRIS SNMP` 子代理配置为在 `IRIS` 启动时自动启动: 1. 导航到管理门户中的监控设置页面(系统管理 > 配置 > 其他设置 > 监控)。 2. 为系统启动时启动 `SNMP` 代理设置选择是,然后单击保存。 3. 当编辑此设置时,`SNMP` 接口的 `IRIS` 端会立即停止和启动。 还可以使用 `^SNMP` 例程手动或以编程方式启动和停止 `IRIS SNMP` 子代理: ```java Do start^SNMP(,) Do stop^SNMP ``` 其中 `` 是连接的 `TCP` 端口(默认为 `705`),`` 是 `TCP` 端口读取超时值(默认为 `20` 秒)。在达到 `` 值之前, `IRIS` 会在 `install-dir\mgr` 目录下的 `SNMP.LOG` 文件中记录在建立连接或应答请求时遇到的任何问题。 注意:当 `SNMP` 主代理重新启动时,可能需要使用 `^SNMP` 例程手动重新启动 `IRIS SNMP` 子代理,如前所述。
公告
Claire Zheng · 一月 16, 2023

来看看你的“开发者社区2022战报” 吧!

亲爱的开发者社区成员们, 我们非常高兴地与您分享最新的开发者社区功能! 🔥 您的 2022 年回顾 🔥 现在,您可以生成个性化“2022 年活动概要”——体现了您在2022年在开发者社区的主要活动,包括发帖数量、评论、视图、热门和最喜欢的帖子/标签等等! 如何查看?有两种方式。 方式一:在您主页顶部会发现一个新横幅: 单击“深入了解”按钮查看您当年的个人亮点!比如: 方式二:点击右上角头像,进入个人主页后即可查看 快来回顾你的2022吧!欢迎在评论分享自己的“2022战报”!
问题
jiang yucong · 七月 26, 2023

如何下载cache-2016的安装包?

从哪里可以下载到cache-2016的安装包?包括windows、macOS以及Linux 您好,目前Cache 2016的安装包仅面向我们的现有客户提供。如果您是我们的客户,请联系我们的售后团队support@intersystems.com;如果您还不是,请联系我们的销售团队GCDPSales@intersystems.com; 谢谢!
文章
Michael Lei · 七月 6, 2021

精华文章--虚拟化大型数据库 - VMware CPU 容量规划

供应商或内部团队要求说明如何为 VMware vSphere 上运行的_大型生产数据库_进行 CPU 容量规划。 总的来说,在调整大型生产数据库的 CPU 规模时,有几个简单的最佳做法可以遵循: - 为每个物理 CPU 核心规划一个 vCPU。 - 考虑 NUMA 并按理想情况调整虚拟机规模,以使 CPU 和内存对于 NUMA 节点是本地的。 - 合理调整虚拟机规模。 仅在需要时才添加 vCPU。 通常,这会引出几个常见问题: - 由于使用超线程技术,VMware 创建的虚拟机的 CPU 数量可以是物理 CPU 数量的两倍。 那不就是双倍容量吗? 创建的虚拟机不应该有尽可能多的 CPU 吗? - 什么是 NUMA 节点? 我应该在意 NUMA 吗? - 虚拟机应该合理调整规模,但我如何知道什么时候合理? 我以下面的示例回答这些问题。 但也要记住,最佳做法并不是一成不变的。 有时需要做出妥协。 例如,大型生产数据库虚拟机很可能不适合 NUMA 节点,但我们会看到,其实是没问题的。 最佳做法是指必须针对应用程序和环境进行评估和验证的准则。 虽然本文中的示例是在 InterSystems 数据平台上运行的数据库,但概念和规则通常适用于任何大型(怪兽)虚拟机的容量和性能规划。 有关虚拟化最佳做法以及有关性能和容量规划的更多帖子,请参见 [InterSystems 数据平台和性能系列的其他帖子列表](https://cn.community.intersystems.com/post/intersystems-数据平台的容量规划和性能系列文章)。 # 怪兽虚拟机 本帖主要是关于部署_怪兽虚拟机_,有时也称为 _Wide 虚拟机_。 高事务数据库的 CPU 资源要求意味着它们通常部署在怪兽虚拟机上。 > 怪兽虚拟机是指虚拟 CPU 或内存多于物理 NUMA 节点的虚拟机。 # CPU 架构和 NUMA 当前的英特尔处理器架构采用非统一内存架构 (NUMA)。 例如,本帖中用来运行测试的服务器有: - 两个 CPU 插槽,每个插槽一个 12 核处理器(英特尔 E5-2680 v3)。 - 256 GB 内存(16 条 16GB RDIMM) 每个 12 核处理器都有自己的本地内存(128GB RDIMM 及本地高速缓存),还可以访问同一主机中其他处理器上的内存。 每个由 CPU、CPU 高速缓存和 128GB RDIMM 内存组成的 12 核套装都是一个 NUMA 节点。 为了访问其他处理器上的内存,NUMA 节点通过快速互连来连接。 处理器上运行的进程访问本地 RDIMM 和缓存内存的延迟比跨互连访问其他处理器上的远程内存的延迟要低。 跨互连访问会增加延迟,因此性能不一致。 同样的设计也适用于具有两个以上插槽的服务器。 一台四插槽英特尔服务器有四个 NUMA 节点。 ESXi 了解物理 NUMA,ESXi CPU 调度器设计为优化 NUMA 系统的性能。 ESXi 使性能最大化的方法之一是在物理 NUMA 节点上创建数据本地性。 在我们的示例中,如果虚拟机有 12 个 vCPU,并且内存不到 128GB,ESXi 将分配该虚拟机在一个物理 NUMA 节点上运行。 这就形成了规则: > 如果可能,将虚拟机规模调整为使 CPU 和内存对于 NUMA 节点是本地的。 如果需要比 NUMA 节点规模大的怪兽虚拟机也没有问题,ESXi 可以很好地自动计算和管理要求。 例如,ESXi 将创建能够智能调度到物理 NUMA 节点上的虚拟 NUMA 节点 (vNUMA),以获得最佳性能。 vNUMA 结构对操作系统公开。 例如,如果您有一台具有两个 12 核处理器的主机服务器和一个具有 16 个 vCPU 的虚拟机,ESXi 可能会使用每个处理器上的 8 个物理核心来调度虚拟机 vCPU,操作系统(Linux 或 Windows)将看到两个 NUMA 节点。 同样重要的是,应合理调整虚拟机的规模,并且分配的资源不要超过所需的资源,否则会导致资源浪费和性能损失。 除了有助于调整 NUMA 的规模,具有高(但安全的)CPU 利用率的 12 vCPU 虚拟机比具有中低 CPU 利用率的 24 vCPU 虚拟机更高效、性能更好,特别是该主机上还有其他虚拟机需要调度并且争用资源时。 这也再次强化了该规则: > 合理调整虚拟机规模。 __注意:__英特尔和 AMD 的 NUMA 实现有区别。 AMD 每个处理器有多个 NUMA 节点。 我已经有一段时间没有在客户服务器中看到 AMD 处理器了,但是如果你有这些处理器,请检查 NUMA 布局,作为规划的一部分。 ## Wide 虚拟机和授权 为实现最佳 NUMA 调度,请配置 Wide 虚拟机; 2017 年 6 月更正:按每个插槽 1 个 vCPU 配置虚拟机。 例如,默认情况下,一个具有 24 个 vCPU 的虚拟机应配置为 24 个 CPU 插槽,每个插槽一个核心。 > 遵守 VMware 最佳做法规则。 请参见 [VMware 博客上的这篇文章以查看示例。 ](https://blogs.vmware.com/performance/2017/03/virtual-machine-vcpu-and-vnuma-rightsizing-rules-of-thumb.html) 该 VMware 博客文章进行了详细介绍,但是作者 Mark Achtemichuk 建议遵循以下经验法则: - 虽然有许多高级 vNUMA 设置,但只有极少数情况下需要更改其默认值。 - 总是将虚拟机 vCPU 数配置为反映每插槽核心数,直到超过单个物理 NUMA 节点的物理核心数。 - 当需要配置的 vCPU 数量超过 NUMA 节点中的物理核心数量时,将 vCPU 均匀分配到最少数量的 NUMA 节点上。 - 当虚拟机规模超过物理 NUMA 节点时,不要分配奇数数量的 vCPU。 - 不要启用 vCPU 热添加,除非您不介意禁用 vNUMA。 - 不要创建规模大于主机物理核心总数的虚拟机。 Caché 授权以核心数为准,因此这不是问题,但是对于除 Caché 以外的软件或数据库,指定虚拟机有 24 个插槽可能会对软件授权产生影响,因此必须与供应商核实。 # 超线程和 CPU 调度器 超线程 (HT) 经常在讨论中出现,我听过“超线程使 CPU 核心数量翻倍”。 这在物理层面上显然是不可能的,物理核心有多少就是多少。 超线程应该被启用,并会提高系统性能。 预计应用程序性能可能会提高 20% 或更多,但实际数字取决于应用程序和工作负载。 但肯定不会翻倍。 正如我在 [VMware 最佳实践](https://cn.community.intersystems.com/post/intersystems-数据平台和性能-–-第-9-篇-intersystems-iris-vmware-最佳实践指南)中所述,_调整大型生产数据库虚拟机规模_的一个很好的起点是假定 vCPU 拥有服务器上完整的物理核心专用资源 — 在进行容量规划时基本忽略超线程。 例如: > 对于一台 24 核主机服务器,可规划总共多达 24 个 vCPU 的生产数据库虚拟机,且可能还有余量。 在您花时间监测应用程序、操作系统和 VMware 在峰值处理期间的性能后,您可以决定是否进行更高度的虚拟机整合。 在最佳做法帖子中,我将规则表述为: > 一个物理 CPU(包括超线程)= 一个 vCPU(包括超线程)。 ## 为什么超线程不会使 CPU 翻倍 英特尔至强处理器上的超线程是在一个物理核心上创建两个_逻辑_ CPU 的方法。 操作系统可以有效地针对两个逻辑处理器进行调度 — 如果一个逻辑处理器上的进程或线程正在等待,例如等待 IO,则物理 CPU 资源可以被另一个逻辑处理器使用。 在任何时间点都只能有一个逻辑处理器运行,因此虽然物理核心得到了更有效的利用,但_性能并没有翻倍_。 在主机 BIOS 中启用超线程后,当创建虚拟机时,可以为每个超线程逻辑处理器配置一个 vCPU。 例如,在一台启用了超线程的物理 24 核服务器上,可以创建具有多达 48 个 vCPU 的虚拟机。 ESXi CPU 调度器将通过首先在独立的物理核心上运行虚拟机进程来优化处理(同时仍然考虑 NUMA)。 在以后的帖子中,我将探讨在怪兽数据库虚拟机上分配比物理核心数更多的 vCPU 是否有助于扩展。 ### 协同停止和 CPU 调度 在监测主机和应用程序性能后,您可以决定是否让主机 CPU 资源过载。 这是否是一个好主意在很大程度上取决于应用程序和工作负载。 了解调度器和要监测的关键指标有助于确保没有使主机资源过载。 我有时听说,要让虚拟机正常运行,空闲逻辑 CPU 的数量必须与虚拟机中的 vCPU 数量相同。 例如,一个 12 vCPU 虚拟机必须“等待”12 个逻辑 CPU“可用”,才能继续执行。 不过应该注意,ESXi 在版本 3 之后就不是这样了。 ESXi 对 CPU 使用宽松的协同调度,以提高应用程序性能。 由于多个协作线程或进程经常相互同步,不一起调度它们可能会增加操作的延迟。 例如,在自旋循环中,一个线程等待被另一个线程调度。 为了获得最佳性能,ESXi 尝试将尽可能多的同级 vCPU 一起调度。 但是,当有多个虚拟机在整合环境中争用 CPU 资源时,CPU 调度器可以灵活地调度 vCPU。 如果一些 vCPU 的进展比同级 vCPU 领先太多(这个时间差称为偏移),领先的 vCPU 将决定是否停止自身(协同停止)。 请注意,协同停止(或协同启动)的是 vCPU,不是整个虚拟机。 这种机制即使在资源有些过载的情况下也非常有效,但正如您所预期,CPU 资源过载太多将不可避免地影响性能。 我在后面的示例 2 中展示了一个过载和协同停止的例子。 记住,这不是虚拟机之间全力争夺 CPU 资源的竞赛;ESXi CPU 调度器的工作是确保 CPU 共享、保留和限制等策略被遵守,同时最大限度地提高 CPU 利用率,并确保公平性、吞吐量、响应速度和可伸缩性。 关于使用保留和共享来确定生产工作负载优先级的讨论不在本帖范围之内,而且取决于应用程序和工作负载组合。 如果我以后发现任何特定于 Caché 的建议,我可能会重新讨论这个话题。 有许多因素会影响到 CPU 调度器,本节只是简单提一下。 要深入了解,请参见帖子末尾的参考资料中的 VMware 白皮书及其他链接。 # 示例 为了说明不同的 vCPU 配置,我使用一个基于浏览器的高事务速率医院信息系统应用程序运行了一系列基准测试。 与 VMware 开发的 DVD 商店数据库基准测试的概念类似。 基准测试的脚本是根据现场医院实施的观测值和指标创建的,包括高使用率的工作流程、事务和使用最多系统资源的组件。 其他主机上的驱动虚拟机以设置的工作流程事务速率执行具有随机输入数据的脚本,来模拟 Web 会话(用户)。 1 倍速率的基准为基线。 速率可以按比例递增和递减。 除了数据库和操作系统指标外,一个很好的用来衡量基准数据库虚拟机性能的指标是在服务器上测量的组件(也可以是事务)响应时间。 一个组件示例是一部分最终用户屏幕。 组件响应时间增加意味着用户将开始看到应用程序响应时间变差。 性能良好的数据库系统必须为最终用户提供_一致的_高性能。 在下面的图表中,我针对一致的测试性能进行测量,并通过对 10 个最慢的高使用率组件的响应时间取平均值来表示最终用户体验。 预计平均组件响应时间为亚秒级,用户屏幕可能由一个组件组成,或者复杂的屏幕可能有多个组件。 > 请记住,您始终针对峰值工作负载进行规模调整,并且为意外的活动峰值留出缓冲区。 我通常以平均 80% 的峰值 CPU 利用率为目标。 基准测试硬件和软件的完整列表在帖子末尾。 ## 示例 1. 合理调整规模 - 每个主机一个怪兽虚拟机 可以创建一个可以使用主机服务器所有物理核心的数据库虚拟机,例如 24 物理核心主机上的 24 vCPU 虚拟机。 数据库虚拟机不会在 Caché 数据库镜像中“裸机”运行服务器以实现 HA,也不会引入操作系统故障转移集群的复杂性,而是包含在 vSphere 集群中实现管理和 HA,例如 DRS 和 VMware HA。 我见过有客户遵循老派的思维,根据五年硬件寿命结束时的预期容量来确定主数据库虚拟机的规模,但从上文可知,最好合理调整规模;如果虚拟机没有过度调整,性能和整合度会更好,并且管理 HA 将更容易;如果需要维护或主机出现故障,并且数据库怪兽虚拟机必须迁移或在其他主机上重启,想想俄罗斯方块的玩法就知道了。 如果预计事务速率显著增加,可以在计划维护期间提前增加 vCPU。 > 注意,“热添加”CPU 选项会禁用 vNUMA,因此不要将其用于怪兽虚拟机。 考虑下图显示的在 24 核主机上进行的一系列测试。 对于这个 24 核系统,3 倍事务速率是甜蜜点和容量规划目标。 - 主机上运行一个虚拟机。 - 使用了四种虚拟机规模来展示 12、24、36 和 48 vCPU 的性能。 - 尽可能对每种虚拟机规模都运行一系列事务速率(1 倍、2倍、3 倍、4 倍、5 倍)。 - 性能/用户体验以组件响应时间(条形图)的形式显示。 - 客户机虚拟机的 CPU 利用率百分比为平均值(线条)。 - 所有虚拟机规模中,主机 CPU 利用率都在 4 倍速率时达到 100%(红色虚线)。 ![24 物理核心主机 单个客户机虚拟机平均 CPU 百分比和组件响应时间 ](https://community.intersystems.com/sites/default/files/inline/images/single_guest_vm.png "单个客户机虚拟机") 这个图表中有许多信息,但我们可以关注几个有趣的事情。 - 24 vCPU 虚拟机(橙色)平稳地增加到目标 3 倍事务速率。 在 3 倍速率时,客户机内虚拟机的平均 CPU 利用率为 76%(峰值为 91% 左右)。 主机 CPU 利用率并不比客户机虚拟机高多少。 在 3 倍速率之前,组件响应时间非常稳定,因此用户很满意。 就我们的目标事务速率而言 — _这个虚拟机已合理调整规模_。 关于合理规模调整先说这么多,那么增加 vCPU 也就是使用超线程又会如何。 性能和可伸缩性有可能翻倍吗? 简短回答是_不可能!_ 在这种情况下,可以通过查看 4 倍以上速率的组件响应时间来了解答案。 虽然在分配了更多逻辑核心 (vCPU) 后性能“更好”,但仍然不平稳,不像 3 倍速率之前那样一致。 4 倍速率时,用户将报告响应时间变慢,无论分配多少个 vCPU。 请记住,在 4 倍速率时,_主机_曲线已经持平于 100% CPU 利用率,如 vSphere 所报告。 在 vCPU 数量较多的情况下,即使客户机内 CPU 指标 (vmstat) 报告低于 100% 利用率,对于物理资源来说情况也并非如此。 请记住,客户机操作系统不知道它是虚拟化的,它只是报告它所看到的资源。 另外,客户机操作系统也看不到超线程,所有 vCPU 都表现为物理核心。 关键是,数据库进程(在 3 倍事务速率时有 200 多个 Caché 进程)非常繁忙,并且非常高效地使用处理器,逻辑处理器没有很多空闲资源来调度更多工作,或将更多虚拟机整合到该主机。 例如,很大一部分 Caché 处理是在内存中进行的,因此没有很多 IO 等待。 所以,虽然可以分配比物理核心更多的 vCPU,但由于主机已经被 100% 利用,并不会获益许多。 Caché 非常擅长处理高工作负载。 即使主机和虚拟机的 CPU 利用率达到 100%,应用程序仍在运行,并且事务速率仍在提高 — 扩展不是线性的,如我们所见,响应时间越来越长,用户体验将受到影响 — 但应用程序不会“一落千丈”,尽管情况不是很好,但用户仍可以工作。 如果您的应用程序对响应时间不是那么敏感,那么很高兴地告诉您,您可以将其推向边缘甚至更远,并且 Caché 仍然可以安全地工作。 > 请记住,您不会想要以 100% CPU 运行数据库虚拟机或主机。 您需要容量来应对虚拟机的意外峰值和增长,而 ESXi 虚拟机监控程序需要资源来进行所有网络、存储和其他活动。 我总是针对 80% CPU 利用率的峰值进行规划。 即便如此,vCPU 的规模最多也只调整到物理核心数,这样即使在极端情况下,仍然有余量让 ESXi 虚拟机监控程序处理逻辑线程。 > 如果您运行超融合 (HCI) 解决方案,还必须考虑主机级别的 HCI CPU 要求。 有关详细信息,请参见我[先前关于 HCI](https://community.intersystems.com/post/intersystems-data-platforms-and-performance-%E2%80%93-part-8-hyper-converged-infrastructure-capacity "previous post on HCI") 的帖子。 部署在 HCI 上的虚拟机的基本 CPU 规模调整与其他虚拟机相同。 请记住,您必须在您自己的环境中使用您的应用程序验证和测试所有内容。 ## 示例 2. 资源过载 我看到过客户站点报告应用程序性能“慢”,而客户机操作系统却报告有空闲的 CPU 资源。 记住,客户机操作系统并不知道它是虚拟化的。 不幸的是,客户机内指标(例如 vmstat 在 pButtons 中报告的指标)可能具有欺骗性,您还必须获得主机级指标和 ESXi 指标(例如 `esxtop`)才能真正了解系统运行状况和容量。 如上面的图表所示,当主机报告 100% 利用率时,客户机虚拟机可能报告较低的利用率。 36 vCPU 虚拟机(红色)在 4 倍速率时报告 80% 平均 CPU 利用率,而主机报告 100%。 即使规模调整合理的虚拟机也可能出现资源短缺的情况,例如,如果在启动后有其他虚拟机迁移到主机上,或者由于 DRS 规则配置不当而导致资源过载。 为了显示关键指标,在下面的一系列测试中,我进行了以下配置: - 主机上运行两个数据库虚拟机。 - - 一个 24 vCPU 虚拟机以恒定的 2 倍事务速率运行(图表上未显示)。 - - 一个 24 vCPU 虚拟机以 1 倍、2 倍、3 倍事务速率运行(图表上显示这些指标)。 在另一个数据库使用资源的情况下;在 3 倍速率时,客户机操作系统 (RHEL 7) vmstat 只报告 86% 平均 CPU 利用率,运行队列大小平均只有 25。 然而,该系统的用户将大声抱怨,因为组件响应时间随着进程变慢而迅速增加。 如下图所示,协同停止和就绪时间说明了为什么用户性能如此糟糕。 就绪时间 (`%RDY`) 和协同停止 (`%CoStop`) 指标显示 CPU 资源在目标 3 倍速率下大幅过载。 这实际并不奇怪,因为_主机_以 2 倍速率运行(其他虚拟机),_而_该数据库虚拟机以 3 倍速率运行。 ![](https://community.intersystems.com/sites/default/files/inline/images/overcommit_3.png "过载的主机") 该图表明,当主机上的 总 CPU 负载增加时,就绪时间也会增加。 > 就绪时间是指虚拟机已准备好运行,但由于 CPU 资源不可用而无法运行的时间。 协同停止也会增加。 没有足够的空闲逻辑 CPU 来允许数据库虚拟机运行(正如我在上面的超线程部分详细说明的那样)。 最终结果是由于对物理 CPU 资源的争用而导致处理延迟。 我曾在一个客户站点看到过这种情况,当时通过 pButtons 和 vmstat 获取的支持视图只显示了虚拟化的操作系统。 虽然 vmstat 报告还有 CPU 余量,但用户的性能体验非常糟糕。 这里的教训是,直到 ESXi 指标和主机级视图可用,才能诊断出真正的问题;一般的集群 CPU 资源短缺导致的 CPU 资源过载,以及使情况变得更糟的不良 DRS 规则,会使高事务数据库虚拟机一起迁移并使主机资源不堪重负。 ## 示例 3. 资源过载 在此示例中,我使用了一个以 3 倍事务速率运行的基准 24 vCPU 数据库虚拟机,然后使用两个以恒定 3 倍事务速率运行的 24 vCPU 数据库虚拟机。 虚拟机的平均基准 CPU 利用率(见上面的示例 1)为 76%,主机则为 85%。 单个 24 vCPU 数据库虚拟机会使用全部 24 个物理处理器。 运行两个 24 vCPU 虚拟机意味着这两个虚拟机将争用资源,并使用服务器上的全部 48 个逻辑执行线程。 ![](https://community.intersystems.com/sites/default/files/inline/images/overcommit_2vm.png "过载的主机") 请记住,在运行单个虚拟机时,主机并没有被 100% 利用,我们仍然可以看到,当两个非常繁忙的 24 vCPU 虚拟机试图使用主机上的 24 个物理核心(即使开启了超线程)时,吞吐量和性能显著下降。 尽管 Caché 非常有效地使用了可用的 CPU 资源,但每个虚拟机的数据库吞吐量仍然下降了 16%,更重要的是,组件(用户)响应时间增加了 50% 以上。 ## 总结 本帖的目的是回答几个常见问题。 要深入了解 CPU 主机资源和 VMware CPU 调度器,请参见下面的参考部分。 虽然有许多专业级的调整,并且要深入研究 ESXi 才能榨干系统的最后一点性能,但基本规则非常简单。 对于_大型生产数据库_: - 为每个物理 CPU 核心规划一个 vCPU。 - 考虑 NUMA 并按理想情况调整虚拟机规模,以使 CPU 和内存对于 NUMA 节点是本地的。 - 合理调整虚拟机规模。 仅在需要时才添加 vCPU。 如果您想要整合虚拟机,请记住,大型数据库非常繁忙,在高峰期会大量使用 CPU(物理和逻辑)。 在您的监视系统告诉您安全之前,不要超额预定 CPU。 ## 参考 - [VMware 博客 - 怪兽虚拟机何时过载 vCPU:pCPU](https://blogs.vmware.com/vsphere/2014/02/overcommit-vcpupcpu-monster-vms.html) - [2016 NUMA 深入研究系列介绍](http://frankdenneman.nl/2016/07/06/introduction-2016-numa-deep-dive-series) - [VMware vSphere 5.1 中的 CPU 调度器](http://www.vmware.com/content/dam/digitalmarketing/vmware/en/pdf/techpaper/vmware-vsphere-cpu-sched-performance-white-paper.pdf) ## 测试 我在一个 vSphere 集群上运行了本帖中的示例,该集群包括连接到一个全闪存阵列的双处理器 Dell R730。 在示例运行期间,网络或存储没有出现瓶颈。 - Caché 2016.2.1.803.0 PowerEdge R730 - 2 个 Intel(R) Xeon(R) CPU E5-2680 v3 @ 2.50GHz - 16 条 16GB RDIMM,2133 MT/s,双列,x4 数据宽度 - SAS 12Gbps HBA 外部控制器 - 超线程 (HT) 开启 PowerVault MD3420,12G SAS,2U-24 驱动器 - 24 个 960GB 固态硬盘 SAS 读取密集型 MLC 12Gbps 2.5 英寸热拔插驱动器,PX04SR - 2 个控制器,12G SAS,2U MD34xx,8G 缓存 VMware ESXi 6.0.0 build-2494585 - 按照最佳实践配置虚拟机;VMXNET3、PVSCSI 等 RHEL 7 - 大页面 基准 1 倍速率下平均每秒 700,000 gloref(每秒数据库访问次数)。 24 vCPU 在 5 倍速率下平均每秒超过 3,000,000 gloref。 测试以老化方式进行,直到达到稳定的性能,然后进行 15 分钟采样并取平均值。 > 这些示例只是为了说明理论,您必须使用自己的应用程序进行验证!