总览
IBM Sterling Order Management是OLTP应用程序,如果可以通过添加新的处理器,设备和存储进行升级以处理更多事务,则认为它是可伸缩的。 定制设计或代码对整个应用程序的可伸缩性具有重大影响。 本文重点介绍了可用于Sterling Order Management实施以确保应用程序可伸缩性的最佳设计实践。
- 减少来自外部系统的依赖性影响
- 定义最佳交易边界
- 消除垃圾收集(GC)开销
- 减少I / O开销
- 减少网络开销
设计最佳实践1 –减少来自外部系统的依赖性影响
IBM Sterling Selling and FulfillmentSuite®应用程序与各种外部应用程序进行交互,例如AVS(地址验证系统),付款系统,税额计算(作为订单履行周期的一部分)。 这些交互是从应用程序层事务或代理层事务创建的。 本质上是同步的应用程序层事务由请求或响应操作启动,并且该过程的结果在该操作之后立即返回给调用方。 但是,如果创建异步事务的代理层是通过单向操作启动的,则结果是通过启动其他单向操作返回的; 与外部系统的同步交互会建立强烈的依赖关系,从而影响用户体验。 如果外部系统无响应,则COM或SOM之类的客户端应用程序将导致用户无响应的窗口。 由于Web容器线程不会及时释放到公共池以处理其他请求,因此单个事务边界内的多次调用会阻碍可伸缩性。 这导致缺少配置的最佳数量的Web容器线程。 应用程序服务器具有可配置的线程超时设置,以确保线程不会无限期地卡住。 但是,由于代理层没有线程超时设置,因此可能会构成此威胁。 因此,至关重要的是,在Sterling Selling and Fulfillment Suite端或中间件(如果存在)中包括超时实现,并在外部系统无响应的情况下将适当的响应超时消息抛出到用户的屏幕上。
设计最佳实践2 –定义最佳交易边界
事务包含以“全有或全无”模式执行的工作单元,以确保原子性。 定义最佳交易边界要求清楚地了解在交易边界内发生的操作顺序,以确保可伸缩和可恢复的应用程序。 事务边界适用于同步和异步事务。 用户响应时间或吞吐量以及锁定机制是确定最佳事务边界时要考虑的两个关键方面。 对于一个实现, CreateOrder
和ScheduleOrder
API陷入单一事务边界可能无法满足另一实现的吞吐量或响应时间。 所有同步事务应更短,并确保最小的行排他锁定,以实现更好的响应时间。 基本API的模板扩展不正确也可能导致不必要的锁定。
例如,在V8.5中使用默认模板进行的getShipmentDetails
API调用不会设置锁定,而如果扩展模板以包含订单详细信息,则会对订单记录上的行进行排他锁定。 不可避免的锁定场景,例如changeOrder
, manageOrganizationHierarchy
; 设计人员需要定义一个较短的事务边界,因为在事务期间将持有由事务中的语句获取的所有锁。 事务边界越长,在应用程序和代理层中发生行锁争用的机会就越大,这最终会阻碍可伸缩性。
下面的示例在锁定问题上提供了更好的清晰度。 假设getOrderDetails
和getShipmentList
API在单个事务边界中一个接一个地启动。 getOrderDetails
获取对订单记录的锁定,并且直到getShipmentList
完成执行后才释放。 所有列表API均应以严格的条件参数开头,以确保搜索范围狭窄。 如果将列表API作为Web服务组件公开给外部系统,则这一点尤其重要。 如果从数据库中获取了多个记录,并且SQL中存在ORDER BY子句。 然后,由于数据库方面的排序操作,整个事务的执行时间更长。 数据库总是尝试在sort_area_size
内的RAM空间中进行排序,并且仅在RAM内存用完时才进行磁盘排序。 如果记录数不适合内存,则排序将在磁盘中进行,这将导致更高的CPU周期。 可以通过在输入.xml文件中添加属性IgnoreOrdering=Y
来消除此ORDER BY子句。
设计最佳实践3 –消除垃圾收集(GC)的开销
应用程序层和代理层都使用JVM™进行动态内存分配。 通过低效的自定义代码创建更多对象时,堆区域中几乎没有空间容纳新对象,并且JVM™执行额外的工作,从而导致频繁进行垃圾回收(GC)。 GC识别不再引用的数据对象,然后回收这些对象所占用的内存。 就内存利用率和性能影响而言,对象创建是最昂贵的操作之一。 因此,建议仅在代码中需要时才创建或初始化对象。 您必须避免在经常调用的方法中创建临时对象。 最好尽早创建对象并保留这些对象直到需要。 Java™程序会在循环中花费大量时间。 因此,永远不要在循环内部创建临时对象,而要优化循环以获得更好的性能。 以下示例显示了一个优化的for
循环示例:
清单1.初始的for循环示例代码
public loop control 1()
{
String str = "Sudhanshu";
for (int j = 0; j < str.length(); j++)
{
// code doesn’t change the length of the string.
}
}
public loop control 2()
{
String str = "Sudhanshu";
int len = str.length();
for (int j = 0; j < len; j++)
{
// code doesn’t change the length of the string.
}
}
该loop control2
更可以通过修改环路倒数得到改善。 JVM™经过优化,可以与-1和+5之间的整数进行比较。 因此,重写循环以与0比较会产生更快的循环。 清单1中的循环可以更改为使用以下for循环指令:
清单2.更改为for循环示例代码
for (int j = len-1; j >= 0; j-)
与StringBuffer
相比,使用“ +”运算符的字符串连接会创建许多临时对象并增加垃圾回收,并且性能大大降低。 对字符串进行操作的方法将返回新字符串,而不是旧字符串的更新副本。
清单3.字符串连接示例代码
String abc = "<OrderLineList>" +
" <OrderLine DeliveryMethod=" "
ItemGroupCode=" "
OrderLineKey=" "
OrderedQty=" " +
" PrimeLineNo=" "
SubLineNo="/>" +
" >) ";
使用不合适的集合会产生大量的GC开销。 例如,一个ArrayList
会在插入过程中创建空间并复制其所有引用,随着ArrayList
变大,它会变得昂贵。 记住TreeMap
比HashMap
慢也很重要。
设计最佳实践4 –实施适当的数据库扩展
OOB数据库表的扩展不可避免地要保留自定义列,这是自定义的一部分,但是需要尽职调查才能为自定义列选择适当的数据类型。 例如,您必须在CHAR
和VARCHAR2
之间进行选择。
-
CHAR
用于存储定长字符串。 字符串值在存储到磁盘之前,先进行空格/空白填充。 如果此类型用于存储可变长度的字符串,则会浪费磁盘空间。 - 相反,
VARCHAR2
用于存储长度可变的字符串。 字符串值的长度与值本身一起存储在磁盘上,从而可以有效利用磁盘空间。
因此, CHAR
是大量的,因此,在Sterling Selling and Fulfillment Suite V9.1中,所有OOB CHAR
列都由VARCHAR2
代替。 Sterling Selling and Fulfillment Suite OLTP交易需要频繁插入和更新交易数据。 如果将不适当的数据类型添加为扩展列,这些更新将增加行链接和行迁移到表面的问题的可能性。
- 行迁移 -如果行适合新的块,则Oracle®数据库会将整个行移动到新的数据块。 迁移行的原始行包含指向包含迁移行的新块的指针或“转发地址”。
- 行链接 -Oracle®数据库将行的数据存储在为该段保留的一个或多个数据块的链中。 行链接最常见于大行。
如果数据库为未迁移的行执行一个I / O来读取索引,而执行一个I / O来读取未迁移的行的表,则数据库需要一个额外的I / O来获取已迁移或链接的行的实际行数据。 扩展包含许多列(如YFS_ITEM
– 152列, YFS_ORDER_LINE
– 179列, YFS_SHIPMENT
– 156列)的OOB表以保存CLOB
数据类型,如果频繁更新,将导致行迁移。 如果表中的总列数超过255,则将出现行链接。 因此,对这些Sterling Selling和Fulfillment Suite表的任何扩展都需要认真执行。 始终评估选择维护与这些实体具有外键关系的单独实体的选项,以包括CLOB
或VARCHAR
(列大小> 1000)数据类型的自定义列。 同样,模板修剪的实现对于自定义代码至关重要,以控制仅从主表而不是主表和辅助表中获取的数据,以减少IOPS的数量。
设计最佳实践5-减少网络和I / O开销
从应用程序代理层到数据库层的数据请求通过网络,然后数据库执行I / O操作以从磁盘获取数据作为响应。 利用通过网络层传输的最佳数据量,数据库可以在预期的时间范围内执行I / O操作,并且应用程序可以满足预期的吞吐量和响应时间。 如果不适当的设计或代码带来了开销,则应用程序将不再可扩展。 如果以适当的方式实施,异常处理将是其中的一项功能,从而导致大量的网络开销。
例如,如果在用户操作期间发生异常,并且自定义逻辑将整个错误堆栈跟踪抛出给COM或SOM用户,则这将导致网络拥塞,因为不必要的数据必须穿过网络才能显示给用户。 此显示的数据与用户无关。 OOB自定义错误代码功能用于显示适当的错误消息。
清单4.不适当的编码
yfsUserExitException.setErrorDescription(e.getMessage());
yfsUserExitException.setStackTrace(e.getStackTrace());
自定义Java™代码中包含System.out.println
语句会消耗为应用程序服务器实例分配的磁盘空间,并导致磁盘空间争用。 它还会在日志写入中引入延迟,从而导致在同步JVM日志记录时降低吞吐量或响应时间。 Sterling Selling and Fulfillment Suite应用程序的默认日志记录为INFO
和ERROR
。 因此,必须使用Log4j
代替System.out.println
进行日志记录。 由于表中数据类型为CLOB
或VARCHAR2
(大小> 2000)的扩展列数量很多,数据库将遇到I / O开销。
例如,产品在内部运行选择查询,大多数情况下在YFS_ITEM
上使用'*'。
清单5.选择查询
SELECT /*YANTRA*/ YFS_ITEM.* FROM YFS_ITEM YFS_ITEM WHERE (YFS_ITEM.ITEM_KEY = 'XYZ')
清单5中的查询从数据库中获取一条记录,因为WHERE
子句中使用了作为主键的Item_KEY
。 但是,由于包含在YFS_ITEM
实体中的扩展的CLOB
或VARCHAR2
列的列数量很大,因此I / O操作引入了性能瓶颈。 因此,您需要确保所获取的数据受到模板的严格控制。
结论
可伸缩性和性能密切相关。 在由于不适当的设计和开发而引入的关键性能瓶颈的情况下,应用程序无法扩展。 这五个设计实践必须在实现的设计和开发阶段都实现,以确保可伸缩性。
翻译自: https://www.ibm.com/developerworks/java/library/co-sterling-perf-om-ssfs/index.html