第二十二章:优化客户/服务器性能

在客户/服务器应用程序初步完成以后,可能还会发现一些有待改进的地方,这时,可以对应用程序做一些精细的调整工作,比如,加快表单和查询的运行速度、提高数据吞吐量等,以获得最佳性能。

本章从客户端、网络和服务器三个方面讨论应用程序性能的优化策略。有关如何实现客户/服务器应用程序,请参阅本书前面各章的内容。

本章内容要点:

优化连接的使用

连接的建立工作无论是在客户端还是在服务器上都需要消耗时间和内存。在优化连接时,需要采取折衷方案,在高性能需求和应用程序的资源需求两者之间做出平衡。

Visual FoxPro 使用的连接的数目取决于是否需要强制关闭不再使用的连接,以及如何对连接空闲超时进行设置。

使用共享连接

可以以独占方式使用连接,也可以以共享方式使用连接,两种方法各有优点。在独占使用一个连接时,一旦连接建立,应用程序便无需对连接资源进行争用。如果每个结果集合都独占使用一个连接,您就可以在多个结果集合上进行混合异步处理。

在以共享方式使用一个连接时,对于多个结果集合只有一个连接。在共享连接的多个结果集合上,必须串行地进行数据操作,并且在设计应用程序时要考虑数据访问可能发生冲突,因此需要测试连接是否可用。有关共享一个连接的详细内容,请参阅第八章“创建视图”

控制连接超时

如果应用程序很长一段时间没有任何动作,那么可以通过设置连接的 IdleTimeout 属性来减少连接的使用。IdleTimeout 属性控制连接的空闲时间间隔,超过该时间间隔之后,连接将被 Visual FoxPro 关闭。默认情况下,连接处于无限等待状态,直至由用户关闭才释放该连接。

DBSETPROP( ) 函数的 IdleTimeout 属性为连接定义设置空闲时间;可用 SQLSETPROP( ) 函数为活动的连接设置 IdleTimeout 属性。

即使在显示远程数据的浏览窗口和表单打开时,Visual FoxPro 也可以关闭连接,并在再次需要的时候,又自动进行连接。但是,在下列情况下,Visual FoxPro 不能关闭连接:

可以用 DBSETPROP( ) 函数设置 Transactions 属性,为一个连接定义设置事务处理的方式;而用 SQLSETPROP( ) 函数可以为一个活动连接设置事务处理的方式。

释放连接

在应用程序不再使用一个连接时,应该关闭该连接,这样可以提高应用程序的性能。在关闭一个视图时,连接会自动关闭。如果该连接被多个视图共享,当使用该连接的最后一个视图关闭时,Visual FoxPro 会关闭该连接。

如果不想更新临时表中的数据,就可以人工控制一个查询的连接。可以用 SQL 传递查询先把所需数据放入一个本地临时表,然后关闭连接。

加速数据检索

可以通过以下方法加速数据检索:控制逐步获取过程中所获取的行的数目、控制获取数据的大小、使用延迟获取备注的技术。

也可用 UseMemoSize 视图属性将字符字段返回成为备注字段。然后关闭 FetchMemo 属性,使应用程序可以有选择地获取那些已转换为备注字段的字符字段。

逐步获取

在查询一个远程数据源时,Visual FoxPro 检索所有的数据行,并且建立一个 Visual FoxPro 临时表。为了加速远程数据的检索,可对视图临时表和由 SQL 传递以异步方式创建的临时表使用逐步获取技术。如果一次检索整个数据集合,往往会造成应用程序等待时间过长,而逐步获取技术与此不同。如果使用逐步获取技术,Visual FoxPro 执行查询时只把整个结果集合的一个子集放到本地临时表中。在默认情况下,子集的大小为 100 行。

注释 同步 SQL pass-through 语句并不使用逐步获取技术。在控制权返回应用程序之前,由 SQLEXEC( ) 语句请求的整个结果集合都将被检索。

随着 Visual FoxPro 不断检索数据,本地临时表中得到的查询数据也逐渐增多。由于这些数据行是在不同时刻从数据源中检索得到的,因此行中的信息可能不是当前信息。如果连接处于异步方式下,只要获取了数据的第一个子集,Visual FoxPro 便把控制权返回给您或程序。在空闲时间里,Visual FoxPro 在后台完成抓获查询数据剩余行的任务,每次取一个子集到本地临时表中。这种方法使您可以随时使用临时表中已获取的数据,而不必等待剩余的数据。

注释 增加获取的行数将改善性能,但会降低用户界面的响应速度。减少获取的行数则与之相反。

获取需要的数据

通过使用 FetchAsNeeded 数据库和视图临时表属性,可以只获取所需行,而不连续获取。这对于远程视图或那些需要检索大量结果集的视图,可使数据检索更为有效。

FetchAsNeeded 属性默认设置为假(.F.),即默认为连续获取。当将 FetchAsNeeded 属性设置为真(.T.),则只获取所需的数据行。当 FetchAsNeeded 属性设置为真时,在以下两种情况下可以执行更新:调用当前连接句柄中的 SQLCANCEL( ) 函数完成获取,或关闭视图。

若要查看使用 FetchAsNeeded 属性的效果,可在检索大结果集的视图中,将 FetchAsNeeded 属性设置为.T.,并在视图中打开一个浏览窗口,往下滚动窗口中内容。随着浏览窗口不断向下滚动,状态栏不断更新显示检索过的行数。

控制临时表的获取

若要获取整个临时表,可以发出 GOTO BOTTOM 命令,或者任何其他要求访问整个数据集的命令。

提示 尽管可用 GO BOTTOM 命令获取整个临时表,但更有效率的做法是建立一个参数化视图,每次仅取单独一行,在用户更改记录时再重新查询。有关建立高性能视图的详细内容,请参阅第八章“创建视图”

在程序中不要提供空闲循环处理。若要以编程方式获取视图临时表,可用 GO nRecordNumber 或 GO BOTTOM 命令;若要在异步方式下获取 SQL pass-through 创建的临时表,为每个行子集调用一次 SQL pass-through 异步函数。

取消 SQLEXEC( ) 语句

在任何时候,都可用 SQLCANCEL( ) 函数来取消一条 SQLEXEC( ) 语句或一个视图。但是,如果服务器已经生成远程结果集合,并且 Visual FoxPro 已开始把远程结果集合取入本地临时表,SQLCANCEL( ) 就会取消 SQLEXEC( ) 语句,并留下该本地临时表。若要删除该本地临时表,可以发出 USE 命令,关闭临时表并取消获取操作。

如果 SQLEXEC( ) 语句还没有创建一个本地临时表,则 USE 命令不能取消该语句。为了确定 Visual FoxPro 是否已创建一个本地临时表,可以调用 USED( ) 函数。

控制获取信息的大小

若要控制应用程序从远程服务器上一次获取多少行数据,可在视图中设置 FetchSize 属性。此属性指定了通过逐步获取或者异步 SQL pass-through 调用方式,一次能从远程服务器上获取多少条记录到本地临时表。FetchSize 属性的默认值为 100 行。

若要控制一次获取到视图中的记录数目

延迟获取备注

一个设计合理的应用程序经常使用延迟获取备注技术,来加快包含备注或通用字段的结果集合的下载。延迟备注的获取意味着在下载一行数据时,备注字段和通用字段的内容并不自动下载。相比之下,行中的其他字段能够很快下载。备注字段和通用字段的内容必须直到打开该备注字段或通用字段时才进行获取。由于备注字段和通用字段往往包含的内容很多,因此延迟获取备注技术提供了下载数据行的最快方式,这种下载数据的方式只在用户需要时,才下载备注字段或通用字段。

例如,表单中有可能包含一个用来显示图片的通用字段,为了加速执行,可以使用延迟获取备注的技术,阻止对该图片的下载,直到用户选择了表单中的“预览”按钮。由“预览”按钮的相关代码获取该通用字段,并把它在表单上显示出来。

若要延迟备注的获取,可在视图或临时表中使用 FetchMemo 属性。FetchMemo 属性指出在行下载时是否获取备注或通用字段的内容。此属性的默认值为“真”(.T.),意味着备注和通用字段自动下载。若您的数据中包含大量备注或通用字段的数据,将 FetchMemo 属性设置为“假”(.F.) 将会提高程序执行速度。

注释 视图必须是可更新的,才能使用延迟获取备注这一技术,因为 Visual FoxPro 在检索备注或通用字段时使用由更新属性建立的关键字段值来定位服务器上的源数据行。有关使一个视图可更新的详细内容,请参阅第八章“创建视图”

可用 DBSETPROP( ) 函数设置视图的 FetchMemo 属性,用 CURSORSETPROP( ) 函数设置临时表的 FetchMemo 属性。

优化数据获取性能

可以用下面推荐的连接设置和视图属性来优化数据的获取。其中,连接的 PacketSize 属性对性能影响最大。当然,也可用同步连接来优化获取特性。

对象 属性 设置
连接 PacketSize 4K 到 12K1
连接 Asynchronous2 .F.
视图 FetchSize3 最大

1 为包含较多数据的行设置较大值,需要多试几次不同值才能找到最佳值。
2 如果不想在服务器上执行时取消 SQL 语句,使用同步连接可使性能提高 50%。
3.FetchSize 属性的作用很大程度上依赖于已获取的结果集的记录大小。在同步模式下,它对性能的影响不大,所以根据异步处理视图的积累获取的需要,为 SQL pass-through 设置 FetchSize 属性。如果减小 FetchSize 的值,则积累获取视图时响应特性显著提高,但却降低了获取的速度。若增大 FetchSize 的值,则视图的获取性能增强。

实际性能很大程度上依赖于系统设置和应用程序的需求。这些推荐项基于以下运行环境:客户机已安装 Windows NT 3.51 版,并安装了 ODBC2.10 和 SQL Server ODBC 驱动程序 2.05 版;以及服务器已安装 Windows NT 3.51 版,并安装了 Microsoft SQL Server 4.21 版和 6.0 版。

加速查询和视图的运行

可以通过增加索引、优化本地和远程处理、优化参数表达式来提高查询和视图的性能。

在远程表中增加索引

远程索引可使查询速度显著提高,对于多表查询,如果在各表中以联接字段作为索引,速度会更快。在查询的 WHERE 子句中包含的字段上建立索引,也可提高性能。

聚集索引 (clustered index) 可提供最佳性能。在 SQL Server 上,每个表可有一个聚集索引。“SQL Server 升迁向导”对于那些在 Visual FoxPro 中具有主关键字的表,会自动创建聚集索引。

提示 尽管在被查询的表的字段上建立索引可加速处理过程,但在结果集合上建立索引却会降低执行速度。须小心使用结果集合上的索引。

优化本地和远程处理

如果既要处理本地数据又要处理远程数据,那么可先创建一远程视图,把所有远程数据组合到单独一个视图中。然后再把远程视图和本地数据一起加入到一个本地视图中。由于 Visual FoxPro 在联接和筛选组合视图前,全部获取两个视图,所以限制视图结果集的大小是很重要的。

将远程视图的结果集限制在应用程序所需的最小数据量范围内,可提高远程进程的速度。在检索少量数据进入远程结果集合的同时,也就减少了下载远程数据到本地查询或视图临时表的时间。

优化带有参数的视图

在对一个打开的、带有参数的视图,进行 REQUERY( ) 操作时,在执行前编辑视图,可提高数据检索速度。若要预编辑或“准备”视图,可将视图中 Prepared 属性设为“真”(.T.)。

优化参数表达式

视图参数和 SQL pass-through参数属于 Visual FoxPro 表达式,在发送给远程服务器前,首先要在 Visual FoxPro 中进行求值。表达式的求值时间很重要,因为它可能会延长查询的执行时间。

加速表单的运行

在设计一个主要基于服务器数据的表单时,稍作处理便可获得最佳性能。确定所需的数据和功能,延迟向服务器请求这些数据和功能,除非用户做出了请求。向服务器请求数据需要处理时间,并且造成网络拥挤。采用下列方法可以减少请求的数据量。

本地存储查阅表

很多情况下,一个应用程序中包含有几个表单,都使用相同的远程表。若表中数据并不频繁更改,可选用下列技术中的一种来加速表单的加载并减少服务器的负担。

仅显示要求的字段

只有在用户请求的时候,才显示那些需要花很长时间从服务器检索数据的字段,比如备注或通用字段。可用下列技术:

提高更新和删除的性能

通过以下方式加速更新语句和删除语句的运行:

添加时间戳

在一个包含许多字段的远程表中更新、插入或删除数据时,可以在该远程表中添加一个时间戳字段,这样可以提高性能,条件是您的服务器必须提供时间戳字段类型。

如果远程表具有时间戳字段,就可以用 Visual FoxPro SQL WhereType 更新选项 DB_KEYANDTIMESTAMP。使用这个选项可以节省处理时间,因为这时 Visual FoxPro 只使用视图中的关键字段和时间戳字段与远程表的相应字段进行比较,检测更新冲突。由于只比较这两部分字段,而不是所有可更新的字段(DB_KEYANDUPDATABLE 选项),或者所有已修改的字段(DB_KEYANDMODIFIED 选项),因而减少了更新远程数据的时间。有关 WhereType 选项的详细内容,请参阅第八章“创建视图”

注释 只有在远程表中包含时间戳字段时,DB_KEYANDTIMESTAMP 选项才比较关键字和时间戳。如果对不包含时间戳字段的远程表使用 DB_KEYANDTIMESTAMP 选项,Visual FoxPro 将只比较关键字字段。

“升迁向导”在合适的时候,能自动地在导出的表中添加时间戳字段。有关详细内容,请参阅第二十章“升迁 Visual FoxPro 数据库”中的“时间戳和标识列”部分。

提示 若改变了一个视图的基表的结构,比如添加了一个时间戳字段,就必须重新创建视图。一个视图定义中的字段存储在数据库中,在视图使用以后,任何对该视图基表的更改不能在视图定义中反映出来,除非重新创建该视图。

在 Update WHERE 子句中排除备注字段

在任何时候,都可通过禁止视图的备注字段(备注型、通用型或图片类字段)与它们相应的基表比较,来提高更新速度。默认情况下,CompareMemo 属性设置为真(.T.),SQL WHERE 子句自动包含备注字段,该子句是在创建更新视图时产生的。可将 CompareMemo 属性设置为假(.F.),这样在 SQL WHERE 子句中就不包含备注字段了。

使用事务处理

为了达到最佳性能,可用人工事务处理方式,自己管理事务处理。人工事务处理方式能够控制何时提交一组事务处理,从而使得服务器可以快速地处理更多语句。

自动事务处理方式消耗的时间要多一些,因为在默认情况下,每一条更新语句都包装在一个独立的事务处理当中。这种方法为单条更新语句提供了最大程度的控制手段,但同时也增加了开销。

在自动事务处理方式下,通过增加视图或临时表的 BatchUpdateCount 属性的设置,可以提高性能。当使用一个大的 BatchUpdateCount 设置时,多条更新语句可以成批处理为一条更新语句,然后包装在一个单独的事务处理中。但是,如果批处理中有任何语句失败,整个批处理将被回滚。

提示 有些服务器不支持 BatchUpdateCount 属性;因此在应用程序使用它之前,需要对每个远程服务器做测试。

使用服务器存储过程

可以在服务器上创建存储过程,存储过程由于进行过预编译,因而可快速运行。可以执行存储过程、用 SQL pass-through 发送参数,并把应用程序中的某些处理工作适当地转移到服务器上。

例如,您也许希望在本地收集用户的输入信息,然后执行一个 SQL pass-through 查询来发送数据给服务器,调用合适的存储过程。为了做到这一点,您可以在一个本地临时表或数组上创建一个表单来收集数据,然后用服务器存储过程的名称和提供的参数来构造一条 SQLEXEC( ) 语句。接下来就可把这段代码添加到“确定”或“提交”命令按钮的 Click 事件代码中,在用户选中该按钮时运行 SQLEXEC( ) 语句。使用服务器存储过程来更新远程数据具有更高的效率,因为存储过程在服务器上已进行过编译。

批处理更新

如果应用程序更新的记录比较多,那么可采用批处理方式进行更新,这样可以提高网络和服务器的处理效率。根据视图的 BatchUpdateCount 属性,在更新或插入语句发送给服务器之前,要进行成批处理。BatchUpdateCount 的默认值为 1,意味着每个记录由一条更新语句发送给服务器。增加这个值后,可以在一条语句中容纳多个更新要求,从而减少网络通信量。

提示 有些服务器不支持 BatchUpdateCount 属性;因此在应用程序使用它之前,需要对每个远程服务器做测试。

为了有效地使用这个特性,视图连接必须设置为缓冲方式 5,即开放式表缓冲,理想的更改应限制在临时表每一行的同一字段上。可以用 DBSETPROP( ) 设置视图定义的 BatchUpdateCount 属性;用 CURSORSETPROP( ) 更改活动视图临时表的 BatchUpdateCount 属性。

优化更新和删除的性能

可以采纳下面的建议来设置视图和连接的属性,以优化更新和删除的性能。视图的 BatchSize 属性对性能的影响最大。

对象 属性 设置 注释
视图 BatchUpdateCount 10 – 30 行 对于小的更新可以设置一个大一些的值。1 最多可提高 50% 的性能。默认值为1。
连接 Asynchronous (.F.) 除非希望可以取消正在服务器上执行的 SQL 语句,否则,使用同步连接可提高 50% 的性能。默认值为同步。
连接 WaitTime N/A 在异步方式下要提高性能,可使用稍短一些的等待时间;要减少网络通信量,可增加等待时间。
连接 PacketSize 4K 到 12K 对性能影响不大。

1 最佳值还取决于服务器的速度。

实际性能很大程度上还取决于您的系统配置和应用程序需求。试验列表中的数据,以确定系统的最佳设置。以上推荐项基于以下运行环境:客户机已安装 Windows NT 3.51 版,并安装了 ODBC 2.10 和 SQL Server 驱动程序 2.05 版;以及服务器已安装 Windows NT 3.51 版,并安装了 Microsoft SQL Server 4.21 版和 6.0 版。