如何确定*USRQ中的最大消息数

How to determine the maximum number of messages in *USRQ

需要创建尽可能大的*USRQ。 根据文档,*USRQ 的最大大小为 2Gb。 创建队列需要指定最大消息大小、队列中的初始消息数、队列扩展的大小(以消息为单位)以及最大扩展操作数。 假设消息大小为 1024 字节。初始数量为 128 条消息。队列将扩展 128 条消息。 估计消息的最大可能数量 - 2Gb / 128 字节。然后我们减去初始消息数 (128) 并除以扩展的大小 - 128。结果,我们得到最大扩展数 - 16,383。 我们将这些参数传递给 QUSCRTUQ,之后我们看看我们得到了什么(调用 matqat)。 我们看到最大分机数 (mat_template.Max_Extend) 设置为小于请求的分机数 - 15 306,因此队列中的最大消息数为 1 959 296 然后开始填充队列,在某个时候得到错误“试图大于存储限制”同时,队列中的消息数为 1,957,504,使用的扩展数为 15,282。 为什么会出现这种情况以及创建队列时如何正确估计最大增量数?

考虑到队列存在(并且必须存在)一些内部“开销”,以将所有排队的消息以正确的 LIFO 或 FIFO 顺序等链接在一起。这些内部“链表”或“指针” " 不是 "免费"。

创建一个小测试 *USRQ,然后对其执行 DMPOBJ。然后将几条消息添加到队列中,然后再次使用 DMPOBJ。然后“de-queue”几个消息,再做一个DMPOBJ。然后比较这些转储的假脱机文件,看看发生了什么。

这并没有真正直接回答你的问题,但我认为我的评论没有用,所以我会尝试回答。

documentation

If the number of queue extensions is specified and non-zero, the queue will be extended by the number of additional messages specified until either the number of queue extensions is reached or the storage limit of 2GB is reached.

也就是说,您在QUSCRTUQ中指定的分机数量只是一个请求;如果超出限制,系统会在您达到该数量之前阻止您。

正如 中所指出的,将会有一些开销占用 2GB,因此 MATQAT 报告的扩展数量低于您的要求也就不足为奇了。

令人惊讶(无论如何对我来说)的是,当您实际向队列添加数据时,您甚至可能无法获得 MATQAT 报告的扩展数。因此,获得准确数字的唯一方法似乎是添加数据,直到收到错误消息。

鉴于此,接下来我要看的是可能的用户输入是什么。他们真的可以输入任何东西吗?还是他们必须在一组有限的值中进行选择?如果用户输入的可能组合数量是“可管理的”,那么您可以编写一个脚本来尝试所有这些组合。为扩展数使用巨大的值(故意请求超过 2GB)并监视错误消息以捕获您真正获得的扩展数。如果您可以为每个可能的用户输入执行此操作,那么您可以查找 table 扩展的数量,并实际使用它而不是进行算术运算。

如果可能的用户输入太多,那么您只需要尝试一个有代表性的样本。而不是查找 table,而是进行算术运算,但基于样本中遇到的最小有效大小(或者甚至更小,以提供安全边际)。

首先,我想感谢所有评论和回答的人 - 找到解决方案帮助很大。 特别感谢@MarkSWaterbury - 你的回答对找到解决方案的正确方法很有帮助。 我们设法理解和看到的东西。

为实验选择了以下参数:

  • 一条消息的大小是64字节
  • 初始队列容量 - 1 条消息
  • 队列增量 - 1 条消息

那个。每次添加新消息时,队列的大小都应该增加。

创建队列后,其大小为16Kb。这与“关联space”的大小相同(可以通过MATMDATA获取)。

不管消息的大小和队列增量的大小(增量中的消息数),队列的物理大小并不是每次都增加,而是由于容量不足和倍数1 页(MATMDATA 表示页面大小为 4Kb)。

在我们的例子中,前 26 条消息不会导致队列的物理大小发生变化。第 27 个导致其增加 4Kb。此外,直到第 55 条消息,队列的物理大小不会改变。第 56 位又增加了 4Kb。然后一直到第86个没有变化,第87个又增加了物理大小4Kb...

所有这些使我们能够评估所谓的 Mark S Waterbury“内部开销”。 在这里,还应该注意的是,队列中消息的物理大小由 user-specified 最大消息大小和消息头的大小组成 - 结构中的消息属性填充有 MATQMSG 并包含:

  • 消息排队时间 Char(8)
  • 消息长度 Bin(4)
  • 保留字符 (4)

对于 KEYED 队列,密钥大小添加到消息的物理大小。

使用提出的方法,我们可以粗略估算出上面提到的“内部开销”,也就是64字节(可能少一点,因为队列物理大小的页增量,准确的值为每次都不一样,但对齐 16 让它像这样)。

总的来说,我们得到了用户指定的最大消息大小和键大小之和的额外 80 个字节(对于 KEYED 队列,对于 FIFO 和 LIFO 队列,键大小为 0)。

现在,如果我们根据指定的最大队列大小(2GB,如文档中所示)和消息的物理大小(如上所述定义)计算最大增量数,我们将得到正确的值,我们将其传递给 QUSCRTUQ。如果我们随后调用 MATQAT 并计算队列中消息的最大大小,使用返回给它的值初始消息数、附加消息数和队列扩展数,我们得到最大消息数的实际值队列,它不会抛出异常 1C04“超出对象存储限制”(MCH2804)。 那。现在你可以通过队列中的消息数与计算出的最大消息数的比例来控制队列的填充程度,而不用担心出现异常。

这一切是为了什么?如果我们有*DTAQ,一切都会简单得多...... 问题是,如果我们不需要 *DTAQ 的所有功能(日志记录、将内容保存到磁盘、使用远程队列...)并且我们只使用本地队列,*USRQ 会快 4-5 倍并且消耗大约同样的时间少 CPU 资源。

我认为通过更多实验,您会发现 IBM i 总是以页为单位分配内存。它可能正在经历每个消息的分配过程,并从上一个内存分配开始在 *USRQ 上分配 space 直到分配的内存已满,然后它分配另一个足够大的块来包含下一个分配。

举个简单的例子。一个数据区最多可达 2000 字节,但无论你将其设为多大,它始终显示 8K 字节的物理大小。在内部,它知道可以使用多少 8K space。

那么发生了什么?

  • 您创建了一个最小大小的 *USRQ:1 条消息 64 bytes/message。
  • 系统创建了一个最小物理大小为 16K 但逻辑大小更小的对象 64bytes + 开销
  • 您添加一条消息
  • 系统添加留言
  • 您再添加一条消息
  • 系统检查是否为下一条消息分配了足够的内存,如果有,则扩展 *USRQ 的逻辑大小。
  • 系统随后添加消息
  • 这会随着消息的添加而继续,直到分配的内存不再足够大以容纳下一个扩展。此时 OS 以页面增量分配更多物理内存,但至少足以容纳请求的扩展。

所以这里有几个分配方案。 *USRQ 例程根据 *USRQ 参数扩展对象的逻辑大小,OS 根据 *USRQ 例程的请求在页面大小块中分配物理内存。因此 *USRQ 扩展不一定需要物理内存分配。它还指出了一种可能的优化,该优化基于设置队列的初始大小使其至少填满 16K,并设置扩展的大小使其至少填满 4K。这样 API 就不会 运行 通过扩展逻辑而不是必要的。