简介
性能总是Microsoft® FoxPro®数据库管理系统的主要特点之一,尤其是在数据库引擎方面。在介绍了Microsoft Visual FoxPro™有关的数据库开发系统以及它相关的对象模型,加强引擎,还有客户端/服务器特性之后,你就会发现整个产品的功能变得越来越强大了。然而强大的另一面就是产品也变得越来越复杂了。因此,当开发越来越强劲时,面向对象的应用程序使用远程数据存储器中的数据变得越来越容易了,同时,应用程序也变得更加慢了。
本文要为你介绍一些实践技巧和技术,帮助你改善你的应用程序的性能。只要你在应用程序中正确的使用了这些技术,你就能够明显改善你的用户界面和数据访问性能。
尽管应用起来只有部分技巧结果比较明显,
我们还是希望
我们所介绍的能够给你提供有用的信息,帮助你全面改善应用程序的性能。同时,
我们希望你能够把你的意见或经验反馈给
我们,这样今后使用Visual FoxPro的开发者要提高应用程序的性能时就能获得更多的帮助,这正如你的前人所做的一样。
硬件配置
在这一部分
我们将介绍调整操作系统的技巧,经过调整之后操作系统将会与Visual FoxPro一起工作得更加和谐。在某些情况下你需要改变硬盘缓存器的值以达到优化性能,从而满足应用程序需要的目的。
Microsoft Windows 95 和Windows NT
在大多数情况下Microsoft Windows® 95和 Windows NT®操作系统与Visual FoxPro一起工作得非常好。唯一的例外就是在Windows NT Server上运行Visual FoxPro。因为Windows NT Server 需要的内存比Visual FoxPro想象的还多,所以Visual FoxPro只能够为自己超支分配内存使用。
为了解决这种情况,你可以用SYS(3050)系统函数来调整Visual FoxPro的内存缓冲区,把它近似调整为它的缺省值的三分之一(详情见内存管理)。例如,你可以把下面这一行代码添加到启动编码或者是CONFIG.FPW文件中去:
=SYS (3050, VAL (SYS (3050, 1, 0)) / 3)
本地数据性能
Rushmore 和索引
在MSDN Library的Visual FoxPro Developers Guide中,第十五章“优化应用程序”一文详细地讨论了Rushmore™技术。下面再补充说明一些技巧。
使用索引
使用索引能够提高数据查询的性能,这是很明显的。在Visual FoxPro中,索引是树形的数据结构,所以在索引中查询非常快。
Visual FoxPro最独特的方面之一就是支持在索引中使用公式。大多数数据库管理系统(DBMSs)允许使用一个或更多个字段进行查询,而Visual FoxPro允许在索引中使用任何公式,甚至是用户自定义的函数。
不过你要注意的是,当你往表格中添加了很多的索引时,更新和插入速度会比较慢,因为此时Visual FoxPro要更新每一个索引。通常,你只需要为那些在过滤器和汇合指令中使用的数据建立索引。
你要避免使用FOR <条件 > 或者NOT <条件 >形式的索引公式,因为这些公式对于索引来说是不可优化的。例如:
INDEX ON DELETED() TAG DEL
是Rushmore可优化的,但是
INDEX ON NOT DELETED()TAG NOTDEL
不是。作为一个查询的特殊情况,当你不想删掉记录时,如果你已经SET DELETED ON,那么使用第一个例子中的索引公式会帮助你加快操作速度。
如果你在公式中使用了一个FOR的句子,Visual FoxPro不会使用索引来达到最优化。例如
INDEX ON ORDNUM FOR DISCOUNT > 10 TAG ORDDISC
最后,注意不要使用那些只能用离散值的索引(如逻辑字段等)。
何时进行查询Rushmore优化?
什么时候对Visual FoxPro表格进行的查询或者过滤要用Rushmore优化,以及什么时候只是需要部分地进行优化,这些问题都很重要。
Visual FoxPro用Rushmore技术来优化过滤条件,它会找寻符合过滤器左边表达式的索引关键表达式。通常,开发者们会错误地试图把一个索引的标记名与一个过滤器的表达式的相匹配,这样只会发现Visual FoxPro不能够用这种方法进行优化。以下是一个错误地使用了索引的例子:
USE CUSTOMER
INDEX ON UPPER(contact) TAG name
SELECT * FROM customer ;
WHERE contact="BILL" && not optimized. No index on "contact"
以下是在过滤查询中正确利用索引的方法:
SELECT * FROM customer WHERE UPPER(contact)="BILL" && Optimized!
你还可以用SYS(3054)函数来决定Rushmore查询优化的等级。SYS(3054)可以显示也可以不显示查询的Rushmore优化等级。SYS(3054,1)表示显示Rushmore 优化的等级,而SYS(3054,0) 则表示不显示Rushmore优化的等级。
数据库引擎性能特性
主索引和侯选的索引
在FoxPro 2.x中,用唯一的ID号来增加记录的典型技术就是在你的关键字段中建立一个索引,要增加记录时就运行一个搜索程序来查看这个ID号是否已经被使用了。虽然这个方法已经比较快了,但是它还不是最理想的。
用Primary 和Candidate关键字插入(或者更新)Visual FoxPro表格中,这种方法是由数据库引擎验证的唯一方法,它用于比较低的级别中,速度非常快。
用可区别的标记来提高性能
在Visual FoxPro中,
我们改进了查找具有通用特征(指那些有可区别标记的特征)的数据的方法。如果你的索引表达式的结果不支持可区别的标记(如B或M),查找会快一些。
Non-Machine比较顺序
non-Machine比较顺序(如General)比Machine比较顺序慢的原因有两点:
Non-Machine的索引关键字比Machine的索引关键字长一倍,因为它们要包含可区别的信息。
Non-Machine比较用了许多特别的规则来为特征值建立索引,这样它才能返回正确的结果。
因为Machine比较顺序相对来说要快一些,所以它通常用于连接和查找,而其他的比较顺序通常用来为记录排序。
注意:Visual FoxPro利用的索引仅仅是用SET COLLATE当前的设置来建立的。因此,典型的解决方法是在主要的搜索和分类字段中有两个索引:
SET COLLATE TO "GENERAL"
INDEX ON lastname TAG lastname && sort index
SET COLLATE TO "MACHINE"
INDEX ON lastname TAG _lastname && seek/select index
如果你想要用相对来说比较快的Machine索引来对字段进行查找,选择或者连接,请在进行之前使用SET COLLATE TO "MACHINE"。Rushmore会使用在Machine比较顺序中建立的索引,查找和选择/连接都非常快。
用SQL SELECT
如果在Machine比较顺序中完成了一个SQL SELECT,那么任何ORDER BY 或者GROUP BY都可以用Machine比较顺序。如果你要用non-Machine比较顺序为结果记录排序,你可以在这个比较顺序中完成SELECT,当然这样做比较慢,也可以分两步把它完成:
第一步:用"Machine"比较顺序来选择记录。
SET COLLATE TO "MACHINE" && use only machine indexes
SELECT * FROM table INTO CURSOR temp1 WHERE lastname = "Müller"
第二步:用"General"比较顺序为记录排序。
SET COLLATE TO "GENERAL" && use only general indexes
SELECT * FROM temp1 INTO TABLE output ORDER BY lastname
新数据类型
我们在Visual FoxPro中引进了几个新的数据类型:DateTime, Integer, Double, 和Currency。所有这些类型的数据在硬盘上将存为二进制数据(其中Integer是一个四个字节的二进制值,其他的是八个字节的二进制值)。
这样做有两个优点。第一个优点是,因为存储在硬盘上的数据量比较小(一个八位的数作为Numeric来存储要占八个字节,而作为Integer来存储则只要四个字节),所以从硬盘往内存里调数据和索引时,一次能够调更多的数据到内存页中去,这样速度就会比较快一些。第二个优点是,无须进行数据的传送。在内部,如果Visual FoxPro把整数看作四个字节的二进制值,那么需要把它转换为数字才能以ASCII码数据的形式存储在硬盘中。因而,每一次读取数据都必须先从ASCII码转换成为二进制,然后再转换回去。有了Integers, Doubles, DateTimes, 和Currency几种数据类型,这种转换就不必要了,对数据的访问自然就快了很多。
在新的数据类型中,Integer对速度的影响最大。因此,只要有可能,尽量地把主要的和外来的关键值设置为Integer。好处就是:.DBF文件更小了,索引更小了,而连接就更快了(这也许才是最重要的)。
缓冲和事务处理
事务处理必须简短。看看下面这个例子:
BEGIN TRANSACTION
DO FORM BIGFORM TO nResult
IF nResult = IDCOMMIT
END TRANSACTION
ELSE
ROLLBACK
ENDIF
上面这个例子说明了什么?如果你认为这是一个操作的恰当的事务处理的封装,的确如此。但是,事务处理仅仅用于限制数据的更新。如果你做的不仅仅是这些(如涉及用户界面甚至编程的概念像CASE, WHILE,或 IF语句等),那么在实际的数据更新过程中效率就会变得很低。
很明显,这是因为在Visual FoxPro中如果要使用事务处理就要求锁住记录。在事务处理和记录更新的过程中,记录被锁住,一直要等到事务处理被委托或者是反转才能够解开。即使你在UNLOCK之后说明了要RLOCK()或者FLOCK(),记录也一直被锁死着,直到END TRANSACTION或者 ROLLBACK出现才会改变。进一步说,把记录悬挂起来也要求Visual FoxPro要把头文件锁住,直到事务处理完成之后才能够解锁。(被委托或者是反转)。因此,在一个大的,多用户的系统中,减少记录被锁死的持续时间是很关键的。所以在上面这个例子中一个更加适当的应用程序是:
* Save method from the cmdSave command button.
BEGIN TRANSACTION
UPDATE PRODUCTS SET reorder_amt = 0 ;
WHERE discontinued = .T.
END TRANSACTION
使用SYS(3051)
这个SYS函数控制时间的数量,精度达到毫秒级,经过一次不成功的锁定尝试之后,Visual FoxPro在再次试图锁定一个记录,表格,索引,或备忘录之前会一直等待。缺省值是333毫秒。
如果在你的数据库文件中有许多锁定竞争,你只要把这个值调大(最大是1000),就能够改善你的应用程序的性能。这样你的应用程序就不会把时间浪费在快速通过网络进行锁定上了。
不过,如果不希望锁定竞争太高,你就要把这个值调低(最小值是100),这样锁定操作就会更快一些。
远程数据性能
从任何后端数据库中检索获得数据都是很耗费时间的。为了从一个服务器数据库中检索数据,以下几个步骤是必须的:
客户端向后端服务器数据库提出查询。
服务器分析并且编辑查询条件。
生成结果集。
服务器通报客户端结果已经完成。
客户端通过网络从服务器上获得数据。
为了加快数据的检索(或更新)过程要用到许多技术。
只检索你需要的数据
一般来说,应用程序中的一个函数单元(在大部分情况下是一个表单或者报表)要访问表格中所有的数据只是特殊情况。建立一个远程视图,仅仅获取(或者更新)你所需要的字段和行,这样通过线路所传送大量数据的情况就会大大改善。例如,如果你已经建立了一个远程视图,它与一个表单的四个控件绑定在一起(如客户表格中的客户标识号,公司名,联系方式,地址等),用这样一个SELECT语句来执行视图,如:
SELECT * FROM customers
效率会很低,因为你检索了大量不做要求的字段。如果用下面这个SELECT语句来执行视图,效果会好一些。
SELECT customer_id, company, contact, address FROM customers
使用WHERE子句
为了限制检索(或者更新)的数据量,适当地使用WHERE子句是有必要的。
我们用同样的例子来进行说明,如果你想知道西北地区的客户情况,使用下面这个SELECT语句:
SELECT customer_id, company, contact, address FROM customers
WHERE region = NORTHWEST
WHERE子句中的参数
Visual FoxPro中灵活的视图和SQL Pass Through技术能够让你在SQL SELECT, SQL UPDATE,以及 SQL DELETE语句的WHERE子句中使用运行时参数。例如,你能够从任何区中检索信息。
SELECT customer_id, company, contact, address FROM customers
WHERE region = ?pcRegion
这里pcRegion是参数的名字。如果视图只是用于检索或者再查询必需的信息,这个参数应该有(但不是必须有)。
使用正确的更新准则
View Designer中的Update Criteria标签使你能够明确你想要如何把视图中的数据进行更新,插入和删除。标签中标有“SQL WHERE clause includes”的部分能够让你控制UPDATE 和DELETE操作中WHERE子句的内容。这对于控制后端的数据冲突以及性能都十分重要。考虑到上面所提到的视图并且假设关键字段是customer_id。假设你想要更新除了关键字段之外的所有字段(这种情况不会经常出现,因为改变关键字段就意味着做一次删除再加上一次插入)—也就是“公司名”,“联系方式”,以及“地址”。基于你在"SQL WHERE clause includes"选项组中的选择,你的WHERE子句可以是表1中的任何形式。
Table 1. Possible WHERE Clauses for Updates and Deletions
表1。更新和删除的WHERE子句的可能情况
设置
WHERE 子句
仅仅是关键字段
WHERE OLDVAL (customer_id) = CURVAL (customer_id)
关键字段和经过修改的字段(缺省设置)
WHERE OLDVAL (customer_id) = CURVAL (customer_id) AND
OLDVAL (<first_modified_field>) = CURVAL (<first_modified_field>) AND
OLDVAL (<next_modified_field>) = CURVAL (<next_modified_field>) AND
[and so on.]
关键和可更新的字段
WHERE OLDVAL (customer_id) = CURVAL (customer_id) AND
OLDVAL (company) = CURVAL (company) AND
OLDVAL (contact) = CURVAL (contact) AND
OLDVAL (address) = CURVAL (address)
关键字和时间标记
WHERE OLDVAL (customer_id) = CURVAL (customer_id) AND
OLDVAL (timestamp) = CURVAL (timestamp)
从上表中
我们能够很清楚地看出,WHERE子句在很大程度上会影响性能,尤其是有许多字段要更新时(关键字段和经过修改的字段),或者是有许多字段能够被更新时(关键字段和可更新字段)。这是因为Visual FoxPro要把它的"oldval"数据缓冲区中的值(这只是一个系统指针)与硬盘中的值(服务器数据库)相比较,然后及时给出充分的报告说明何时会发生更新冲突(如当你首先获得某些数据时,其他的用户或者程序却改变了这些数据)。如果有很多更新操作要进行,上述比较过程会耗费很多时间。
因此,
我们推荐你在大多数情况下使用Key and Modified字段。一般来说,你要更新的后端字段总是你要更新的所有字段的一个子集(同时也是视图中所有字段的一个子集)。
对于支持时间标记的服务器数据库而言,
我们推荐你使用Key and Timestamp设置,而不是上面提到的Key and Modified Fields设置。Timestamp是一个行版本,每次行有所改变时,它就要更新一次(顺便提一句,timestamp数据类型并不是指数据和时间,而是指一个数值。你用不着担心不同机器上的系统时钟会导致更新问题。)使用这个设置,在WHERE子句中只比较了其中一列,因此处理速度就快了很多。
注意:这些设置也可以在视图的WhereType属性中通过DBSETPROP()或者CURSORSETPROP()函数进行。
使用BatchUpdateCount 属性
一些服务器数据库(如Microsoft SQL Server)允许一批批的语句作为一个单独的数据包送往服务器。例如,如果你把BatchUpdateCount属性设置为4,然后更新前10个记录,Visual FoxPro送往服务器的SQL语句就是这样的:
UPDATE customer SET contact = John Jones WHERE customer_id = 1;
UPDATE customer SET contact = Sally Park WHERE customer_id = 2;
UPDATE customer SET company = John Jones WHERE customer_id = 3;
UPDATE customer SET contact = John Jones WHERE customer_id = 4;
这一技术加快了更新,删除,和插入的速度,因为通过线路传送的网络数据包的绝对数量减少了,并且语句被服务器数据库压缩为一批而不是零散的。
你应该试一试用这一属性的不同的值和PacketSize属性来优化你的更新过程。
这个属性也能够在Query菜单的Advanced Options对话框中进行设置,或者是通过编码用DBSETPROP()或CURSORSETPROP()来设置。要注意的是,要利用这种方法的优点则需要用DBSETPROP()或者SQLSETPROP()把Transactions属性中的手动事务处理设为允许。
使用PacketSize 属性
PacketSize属性控制送往数据库服务器的和从数据库服务器中检索获得的网络数据包的大小(以字节计算)。它实际上是由开放式数据库互接(ODBC)来设置的,并且是一个非负值。不同的网络供应商对于这个属性的处理各不相同,所以你需要查看你的网络服务文档。例如,Novell NetWare把数据包大小的最大值设置为512字节,因此在PacketSize属性中,即使把它的值设置得大于512也没有任何其他作用。