在 WebSphere Message Broker 和 IBM Integration Bus 中使用 ESQL 共享变量实现高效、可扩展的缓存

ESQL 共享变量常常在 WebSphere Message Broker 和 IBM Integration Bus 中用于缓存数据库表的内容。此方法的问题在于,ESQL SELECT 操作执行顺序搜索,这会随着缓存增长而逐渐变慢。本文介绍一种构建缓存的新方式,这种缓存能有效地扩展,甚至在缓存大小超过 10,000 行时仍比访问数据库更快。新的缓存结构对应用程序透明,所以您可安全地转换现有的缓存,并且显著减少响应时间和 CPU 使用量。

Tim Dunn, 高级技术研究员 (STSM),IBM Integration Bus 开发团队 , IBM

Tim Dunn 的照片Tim Dunn 是位于英国 IBM 赫斯利软件实验室的 IBM Integration Bus 开发团队的 IBM 高级技术研究员 (STSM)。以前,他是 WebSphere Message Broker 开发团队的性能架构师。Tim 负责指导领先客户解决 IBM Integration Bus 设计、配置和调优问题。他经常在欧洲和美国的大会上发表 IBM Integration Bus 设计和性能主题方面的演讲,还撰写了大量有关这些主题的文章。可通过 dunnt@uk.ibm.com 联系 Tim。


developerWorks 投稿作者

Gerardo Brenner, 高级顾问 , Prolifics

Gerardo Brenner 是 Prolifics(一家 IT 解决方案提供商和 IBM 业务合作伙伴)的高级顾问,他专门研究使用 WebSphere Message Broker 和 IBM Integration Bus 开发业务集成解决方案。他出生在布宜诺斯艾利斯,于 1978 年作为程序员加入 IBM Argentina。不久之后,他调到英国,在位于赫斯利的 IBM 开发实验室工作,起初从事 CICS 开发,后来从事 WebSphere Message Broker 开发,他在这里专门从事 ESQL 的开发、实现和测试,以及数据库支持工作。他于 2011 年加入 Prolifics,在这里帮助许多客户设计和实现了集成解决方案。可通过 gerardo.brenner@prolifics.com 联系 Gerardo。



Emir Garza, 高级顾问 , Prolifics

Emir Garza 是 Prolifics(一家 IT 解决方案提供商和 IBM 业务合作伙伴)的高级顾问,专门研究业务集成软件。他之前是一位 IBM IT 顾问,1999 年开始从事 WebSphere MQ 研究,随后于 2001 年从事 WebSphere Message Broker(现在为 IBM Integration Bus)研究。可通过 emir.garza@prolifics.com 联系 Emir。



2014 年 7 月 10 日

简介

ESQL 是 WebSphere® Message Broker 和 IBM® Integration Bus 定义的一种 SQL 扩展,用于定义和处理消息流中的数据。ESQL 共享变量常常用于缓存数据库表的内容,共享变量缓存实现为一个包含 SELECT 操作结果的数组,而搜索通过向该数组发出 SELECT 命令来执行。此方法的问题是,ESQL SELECT 操作执行顺序搜索,所以随着缓存增长,搜索会变慢。大多数用户发现,在缓存大小超出几千行时,访问数据库比使用缓存更快。本文介绍一种构建缓存的新方式,这种缓存能有效扩展,而且甚至在大小超过 10,000 行时仍比访问数据库更快。

假如有一个消息流接收包含机场编码(比如 LHR)的消息,并访问一个数据库表来获取相应的城市名称 (London)。每条消息访问一次数据库可能代价很高,所以一种常见的解决方案是在第一次访问时将这个表加载到一个 ESQL ROW 共享变量中,然后搜索该共享变量。 本文首先介绍称为标准缓存 的最常用缓存结构,然后介绍一种称为 ESQL 缓存 的更高效且可扩展的变体,这种缓存利用了 ESQL 访问其变量的方式。

标准缓存

使用 ESQL 共享变量的最常见的缓存实现包含一个共享的 ROW,其中包含对缓存的数据库表执行 SELECT 的结果:

DECLARE CACHE SHARED ROW;

假设您有一个名为 AIRPORTS 的数据库表,它包含 CODECITY 两列。此代码加载该缓存:

SET CACHE.AIRPORT[] = SELECT A.CODE, A.CITY FROM Database.AIRPORTS AS A;

CACHE 变量的内容如下所示:

CACHE.AIRPORT[1].CODE = AAA
CACHE.AIRPORT[1].CITY = Anaa
CACHE.AIRPORT[2].CODE = AAB
CACHE.AIRPORT[2].CITY = Arrabury
...
CACHE.AIRPORT[4225].CODE = LHR
CACHE.AIRPORT[4225].CITY = London
...
CACHE.AIRPORT[9185].CODE = ZZV
CACHE.AIRPORT[9185].CITY = Zanesville

此函数实现该缓存:

DECLARE CACHE SHARED ROW;
CREATE PROCEDURE getCity_v01 (IN airportCode CHARACTER) RETURNS CHARACTER
BEGIN
 -- v01: "standard" cache
 -- PERFORMANCE TEST ONLY! No ATOMIC blocks.
 -- Do not use if Additional Instances > 0.
 IF CACHE.AIRPORT.CODE IS NULL THEN
 -- load the cache
 SET CACHE.AIRPORT[] = SELECT A.CODE, A.CITY FROM
 Database.AIRPORTS AS A;
 END IF;
 RETURN THE(SELECT ITEM A.CITY FROM CACHE.AIRPORT[] AS A
 WHERE A.CODE = airportCode);
END;

为了保证可读性,显示了没有 ATOMIC 块的缓存加载,此方法仅适用于只有一个线程运行该消息流时。还有其他搜索缓存的方法,比如使用 FORWHILE 循环,但使用 SELECT 是最快的。

使用标准缓存时的性能

此缓存结构的问题在于它无法扩展。一次用户跟踪显示,SELECT 按顺序扫描该表,直至找到满足 WHERE 子句的一行。随着该表不断扩大,搜索会变慢。最终,丢弃缓存而每次都访问数据库的速度更快。

在 Windows 7 64 位版本上使用 IBM Integration Server V9 和一个本地 DB2 V10.1 数据库的度量结果,显示了不断增长的缓存的影响:

图 1. 不同缓存大小每条消息需要的毫秒数(无缓存与标准缓存对比)
缓存和无缓存的性能比较图

该测试包括发出 10,000 条消息来随机访问缓存中的前 1000、2000、3000(依此类推)行,进而模拟不同的缓存大小。缓存的总大小为 9185 行。该图表显示了处理一条消息所花的时间(以毫秒为单位)。数据库响应时间(大约 0.8 毫秒)是不变的(标签 NO_CACHE)。

在此配置中,至不超过 3000 行时,缓存比数据库更快。对于更大的缓存,缓存比数据库访问更慢。(其他配置的结果将不同,比如远程访问数据库,或者使用内存型数据库。)

ESQL Cache

ESQL Cache 将每个键和值(在我们的示例中为机场编码和城市名称)存储为名称/值对:

CACHE.AAA = Anaa
CACHE.AAB = Arrabury
...
CACHE.LHR = London
...
CACHE.ZZV = Zanesville

没有数组。要返回给定机场编码对应的城市名称,缓存搜索函数只需引用适当的变量:

RETURN CACHE.{airportCode};

以下是实现新缓存结构的函数:

CREATE PROCEDURE getCity_v02 (IN airportCode CHARACTER) RETURNS CHARACTER
BEGIN
   -- v02: "ESQL" cache   
   -- PERFORMANCE TEST ONLY! No ATOMIC blocks. 
   -- Do not use if Additional Instances > 0.
   IF FIELDNAME(CACHE.*[1]) IS NULL THEN
	-- load the cache
	DECLARE TEMPCACHE ROW;
	SET TEMPCACHE.AIRPORT[] = SELECT A.CODE, A.CITY 
    FROM Database.AIRPORTS AS A;
	    FOR cacheline AS TEMPCACHE.AIRPORT[] DO
		CREATE LASTCHILD OF CACHE 	NAME cacheline.CODE 
VALUE cacheline.CITY;
	    END FOR;
   END IF;	
   RETURN CACHE.{airportCode};
END;

要按新结构组织缓存,该函数会导航数据库 SELECT 返回的每一行。在此示例中,每行包含一个机场编码(比如 JFK)和城市 (New York)。这个名称/值对用于向缓存添加一个变量,其中变量名称为编码,它的值为城市 (CACHE.JFK='New York')。

为了保证可读性,显示的缓存加载不包含 ATOMIC 块。上述代码仅适用于没有额外的线程时。以下是填充一个缓存行的语句:

SET CACHE.{cacheline.CODE} = cacheline.CITY;

当且仅当该键(在本例中为 CODE)只有一个实例时,SETCREATE 才等效。在该键存在多个实例时使用 SET,缓存中将只有一个实例(最后一个)。

ESQL Cache 的性能

因为搜索功能直接访问该变量,所以它快得多且能更好地扩展。下面的图表比较了 ESQL Cache 与标准缓存的响应时间。图 2 显示了最多 9000 行的缓存大小的每条消息的毫秒数:

图 2:不同缓存大小每条消息需要的毫秒数(ESQL Cache 与无缓存对比)
ESQL Cache 和无缓存的性能比较图

对于所有缓存大小,ESQL Cache(每条消息的平均花时为 0.21 毫秒)都比访问数据库快得多。它也比标准缓存快得多,如图 3 中所示:

图 3. 不同缓存大小每条消息需要的毫秒数(ESQL Cache 与标准缓存对比)
ESQL Cache 和标准缓存的性能比较图

对于所有缓存大小,ESQL Cache 都比标准缓存更快。该图表隐藏了每条消息的 ESQL Cache 用时并非真正不变、而在非常缓慢地增长的事实。这一增长在图 4 中的图表中很明显,其中仅显示了 ESQL Cache:

图 4:不同缓存大小每条消息需要的毫秒数(ESQL Cache)
该图显示了缓存大小的性能影响

响应时间会从缓存大小为 1000 个条目时的每条消息 0.2 毫秒,增长到缓存大小为 9000 个条目时的每条消息 0.23 毫秒,增幅约为 15%。ESQL Cache 何时变得比数据库访问更慢?尽管作者没有通过实验验证这一点,但线性投影表明,对于所测试的配置,直到 80,000 个条目,ESQL Cache 都仍然比数据库访问更快。

使用多个键

ESQL Cache 可轻松地支持多个键,以满足 SELECT ...WHERE KEY1=value1 AND KEY2=value2 的等效需求。以下是它的结构:

CACHE.aa.aa = xxx
CACHE.aa.bb = yyy
CACHE.xx.yy = zzz

返回一个值:

RETURN CACHE.{key1}.{key2};

ESQL Cache 与 Global Cache 之对比

WebSphere Message Broker V8 引入了 Global Cache。它使用执行组的 JVM 堆来存储数据,提供 Java API 来向缓存存储和从中获取数据。它很容易实现,而且在众多缓存大小上提供了一致的性能。Global Cache 相较 ESQL 共享变量的一个优势是,缓存可在消息流、集成服务器、执行组和集成总线或消息代理之间共享,而 ESQL 共享变量的范围仅限于消息流。图 5 比较了 Global Cache 和 ESQL Cache:

图 5. 不同缓存大小每条消息需要的毫秒数(ESQL Cache 与 Global Cache 对比)
ESQL Cache 与 Global Cache 对比图

对于所测试的所有缓存大小,ESQL Cache(每条消息 0.21 毫秒)比 Global Cache(平均每条消息 0.43 毫秒)更快。但是,对于更大的大小,Global Cache 将更快。之前使用的同样的线性投影表明,达到大约 40,000 个条目时,ESQL Cache 就会变得比 Global Cache 更慢。

结束语

我们提出的缓存结构会为带 ESQL 共享变量的缓存带来显著的性能改进。实现这种新缓存的逻辑非常简单,所以转换现有的标准缓存结构也很简单。

我们还未度量超过 9000 行的缓存大小,但该趋势表明,包含 80,000 个条目的缓存仍比访问数据库更快。准确的分界点将依赖于您具体的硬件和软件配置,但在任何情况下,ESQL Cache 都在当前几千个条目的分界点基础上带来了显著的改进。

对于更大的缓存大小,可考虑使用 IBM Integration Bus Global Cache,它提供了始终不错的性能且容易实现。如果需要跨消息流、集成服务器、执行组或集成总线/消息代理而共享缓存,那么 Global Cache 是惟一的选择,因为 ESQL 共享变量的范围仅限于消息流。

致谢

感谢来自南非开普敦的 Amanda Erlank 和来自南非 Standard Bank 的 Johannes Wagener 审阅本文的初步草案。

参考资料

学习

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=WebSphere
ArticleID=977378
ArticleTitle=在 WebSphere Message Broker 和 IBM Integration Bus 中使用 ESQL 共享变量实现高效、可扩展的缓存
publish-date=07102014