WebObjects/EOF/使用 EOF/EOF 性能调优
EOF 为你做了很多很棒的事情。但很多人在一些事情上会遇到困难;比如,一个关系型到对象的映射库可能会有严重的开销。在大型复杂对象模型中,一个常见的经验是,人们会对他们的对象建模,然后进行大量获取,并发现获取大量 EO 的速度非常慢(相对于他们使用 JDBC 或直接 SQL 的预期速度)。将东西带入 EO 的开销会有所不同,但看到比原始 SQL 慢 100 倍的速度下降是完全有可能的;对于许多问题来说,这实在是一个太大的代价。那么该怎么办呢?
以下是一些可以采取的方法来改进;
在 R/O 系统中,最大的代价是将新对象(项目)与其他缓存版本(快照)进行比较,并整合和解决任何更改。因此,添加对象意味着你会产生这种代价。尽管如此,如果你减轻对象的负担,还是有一些微不足道的益处。
你的 EO 不需要对表中的所有内容进行建模。你也可以有多个映射到同一表的 EO。EO 的宝贵之处在于你可以对关系进行建模,并且可以进行关系遍历;但这些关系也会产生成本。如果你减少了不需要的关系的对象,它们就会加载得更快;对你不需要的属性也是如此。你甚至可以更进一步;根据你当前的操作,可以有多种 EO 版本。因此,可以使用一个 EO 来获取特定获取所需的所有属性和关系,另一个变体用于不同类型的获取;最后,你需要完整版本的 EO,只有在你需要查看或编辑所有信息的情况下才加载。因此,你的大部分 EO 集合都比用于大型编辑的 EO 更轻量级,更具针对性。
还有其他技术,例如在 EOModeler 中使用 little lock 属性,使更多项目对 EOF 不透明(或不可变)。EOF 会将这些带有锁的属性与快照中的对象进行比较,以确定对象是否陈旧;因此减少需要比较的属性可以提高速度。有些人将这种方法发挥到极致,将所有/大多数属性设置为解锁,并使用一个时间戳作为唯一关键项目;然后,如果任何属性发生变化,该时间戳也会发生变化,EOF 仍然可以管理快照,但只需要管理更少的属性。但这似乎更像是为了节省 EOF 在每个对象上的运行时维护而进行的编码。
但关键是,EO 越轻量级、越简单,速度就越快。然而,主要的代价是在序列化/同步方面;因此,在大多数情况下,回报可能不足以证明为创建这些“优化”而采取的措施的合理性。
当你打算使用一个关系型到对象的映射库(如 EOF)时,你应该期望这会改变你的需求;足以让你能够调整你的模型以适应这个工具。
如果获取 EO 很重/很慢,那么通常你获取的对象越少,你的系统性能就越好。因此,如果你将许多小的表合并和反规范化成几个更大的表,你将减少 EO 的加载次数,并且可能减少对所有这些小片段及其关系的所有错误和关系管理的处理;这会导致性能提升。
你可以通过扁平化关系来做到这一点,或者在数据库中使用视图使事物看起来比实际更扁平;或者你可以直接对模型进行扁平化。根据你的需求,可以对每种方法进行论证。
你甚至可以更进一步,开始将复杂的数据结构和关系移入你自己管理的 blob 中。这将 EOF 从管理这些数据结构和关系中解放出来,并且通常可以加快速度;但代价是你需要承担更多的代码维护工作,而且反规范化可能会对设计造成负面影响,所以你应该谨慎地选择走这条路。
加快访问速度的最重要技术之一是使用原始行;或者进行延迟提升。基本上,原始行获取是一种方法,允许你进行数据库获取,返回一个数据结构(键值对数组),而不是获取并将数据结构提升为一个完整的对象(EO)。这种轻量级的获取所需时间更少,对于大型集合来说要好得多。
然后,在使用这些集合的页面上可以使用原始行,并且你只在需要处理整个对象(及其所有复杂性)的页面上将对象从集合中提升为重量级 EO。
由 EOF、EOModeler 或其他工具生成的获取通常没有由人类生成的获取那么优化;如果它们是优化的,那么机器就不需要我们了,它们可以自己编程。通过自己创建原始 SQL,你通常可以自己调整和优化获取,以只获取你感兴趣的表的部分内容(列和/或行);再次提高性能,并对最需要调整的区域进行调整。
另一个重大性能提升是,如果你将关系中需要选择的字段作为原始行的一部分进行选择,就可以遍历关系。这也会减少事务负载(一些数据库在更便宜的许可证下只允许 100 TPM),并且通常会提高获取速度,因为所有数据都在一次 SQL 查询中通过多表 SELECT 语句(JOIN)进行获取。你还可以指挥你的 EOModel,以确定关系执行内部联接还是外部联接,这对于检查数据完整性来说是一个很大的好处。(查看一些 SQL 网站以了解 INNER、LEFT OUTER、OUTER 和 RIGHT OUTER 联接,看看一些优秀的数据收集方法)
虽然使用数据库(使用存储过程等)似乎违背了数据库抽象层的目的之一;但这可能是必要的或明智的步骤。
存储过程可以在数据库级别完成一些事情,这些事情要快得多,并且可以极大地减轻应用程序服务器的负载。代价是缺乏数据库独立性;并且你必须学习你正在使用的数据库的详细信息。但是,一些精心放置的存储过程可以带来数量级的差异。
缓存是一个需要单独讨论的问题。虽然 EO 速度较慢,但它们确实会进行缓存——因此后续获取(重新获取)的速度比第一次(主要获取)快得多。这对不可变(静态)数据非常有效。对动态数据效果较差。尽管如此,您仍然可以利用除默认缓存行为之外的其他行为。您可以将不可变数据的原始行获取到您自己的数组中,并管理您自己的这些数据的缓存。您也可以将复杂或复合属性放入缓存属性中,并获取该项目,而不是实时获取该项目或不断地从所有源部分重新计算该项目(预先构建一些计算或集合)。
因此,缓存不仅意味着 WO+EOF 应该缓存,而且您可能还需要考虑可以缓存哪些内容,以减轻各种获取的负担。
现在,各种预取和提示并没有真正为您节省时间,因为您仍然需要加载所有对象并为其支付相同的加载时间;但它们可以改变您支付这些时间的位置。有时,即使它不改变实际性能,这也能显著提高感知性能。用户可能会容忍在一个页面上 5 或 10 秒的延迟,如果他们与之交互的接下来的 10 件事都更快;反之亦然。因此,了解用户想要做什么,以及他们如何尝试去做,并相应地进行调整非常重要。
这可能听起来很明显,但尽可能减少您的数据集。例如,没有一个页面应该真正显示 5,000 个元素——这需要花费太长时间来加载和显示。最好使用获取限制或类似的行为,尽可能向用户显示一组较小的数据;然后使用各种技术尽可能少地获取数据。数据集越小,通常可以越快地处理它。
这些只是您可以想到的加快 EO 获取速度的一些技术。
有些人可能会说,如果您必须做所有这些事情,那么您就是在为 EOF 做所有工作;或者您在优化代码上花费的时间和它最初为您节省的时间一样多。虽然在极少数情况下可能是这样;但一般来说,会发生这种情况,EOF 使您能够非常快速和轻松地创建解决方案;然后,您回头去学习瓶颈在哪里(分析),并调整这些区域,直到它们在您的要求下表现得足够好。这允许 RAD(快速应用程序开发)工具的优势,以及更低级别工具的功能和速度;如果您愿意花时间调整需要调整的区域。而且,许多类别的应用程序可能只需要很少的调整。
有关供应商特定的性能调整,请参阅供应商特定部分。
看看 Project Wonder 中的 ERXAdaptorChannelDelegate。
EOModeler 为多对多联接表生成的 SQL 不允许联接的完全优化。多对多联接表仅生成一个索引,该索引是两个列的复合键。
例如,
CREATE TABLE "GROUP_FILE" ( "FILE_PKEY" NUMERIC NOT NULL, "GROUP_PKEY" NUMERIC NOT NULL ); ALTER TABLE "GROUP_FILE" ADD PRIMARY KEY ("GROUP_PKEY","FILE_PKEY") NOT DEFERRABLE INITIALLY IMMEDIATE;
此索引只能用于优化索引中第一列上的联接(出于某种原因,这是表中的第二列)。由于缺少另一列的索引,因此在另一列上联接时会导致表扫描。这种联接发生在遵循多对多关系时。例如,group.files() 将为 files() 中的每个元素生成类似于此的 SQL
SELECT f.* FROM FILE f, GROUP g, GROUP_FILE gf WHERE f.PKEY = gf.FILE_PKEY AND g.PKEY = gf.GROUP_PKEY AND g.PKEY = 10;
只有 GROUP_PKEY 有索引,可用于优化此查询。FILE_PKEY 上的联接 (f.PKEY = gf.FILE_PKEY) 未优化,会导致表扫描。您需要为此手动添加索引
CREATE INDEX GROUP_FILE_FILE_PKEY ON GROUP_FILE (FILE_PKEY);
如果联接表中有许多行,这将产生真正巨大的差异。