我们最近修改了一个大型Delphi应用程序来使用ADO连接和查询而不是BDE连接和查询.由于这种变化,表现已经变得可怕了.
我已经分析了应用程序,并且瓶颈似乎在实际调用TADOQuery.Open.换句话说,从代码角度来看,除了重组应用程序以减少实际使用的数据之外,还没有太多的工作来改进这一点.
有没有人有关于如何提高ADO连接的Delphi应用程序的性能的建议?我已经尝试了suggestions given here,几乎没有任何影响.
为了给出性能差异的想法,我对同样大的操作进行了基准测试:
>在BDE下:11秒
>在ADO下:73秒
>在该文章引用的更改后的ADO下:72秒
我们正在客户端 – 服务器环境中使用Oracle后端.本地计算机每个维护与数据库的单独连接.
为了记录,连接字符串如下所示:
const c_ADOConnString = 'Provider=OraOLEDB.Oracle.1;Persist Security Info=True;' + 'Extended Properties="plsqlrset=1";' + 'Data Source=DATABASE.DOMAIN.COM;OPTION=35;' + 'User ID=******;Password=*******';
回答zendar提出的问题:
我在Windows Vista和XP上使用Delphi 2007.
后端是Oracle 10g数据库.
如连接字符串所示,我们使用OraOLEDB驱动程序.
我的基准机器上的MDAC版本是6.0.
编辑:
在BDE下,我们有很多代码看起来像这样:
procedure MyBDEProc; var qry: TQuery; begin //fast under BDE,but slow under ADO!! qry := TQuery.Create(Self); try with qry do begin Database := g_Database; sql.Clear; sql.Add('SELECT'); sql.Add(' FIELD1'); sql.Add(',FIELD2'); sql.Add(',FIELD3'); sql.Add('FROM'); sql.Add(' TABLE1'); sql.Add('WHERE SOME_FIELD = SOME_CONDITION'); Open; //do something Close; end; //with finally FreeAndNil(qry); end; //try-finally end; //proc
但是我们发现在ADO下调用sql.Add实际上是非常昂贵的,因为每次更改CommandText时都会触发QueryChanged事件.所以用这个代替以上更快:
procedure MyADOProc; var qry: TADOQuery; begin //fast(er) under ADO qry := TADOQuery.Create(Self); try with qry do begin Connection := g_Connection; sql.Text := ' SELECT '; + ' FIELD1 ' + ',FIELD2 ' + ',FIELD3 ' + ' FROM ' + ' TABLE1 ' + ' WHERE SOME_FIELD = SOME_CONDITION '; Open; //do something Close; end; //with finally FreeAndNil(qry); end; //try-finally end; //proc
更好的是,您可以从ADODB.pas中复制TADOQuery,将其重命名为新名称,然后将QueryChanged事件(根据我所知道的)完全没有任何用处.然后使用您的新版本的TADOQuery,而不是本机版本.
type TADOQueryTurbo = class(TCustomADODataSet) private // protected procedure QueryChanged(Sender: TObject); public Fsql: TWideStrings; FRowsAffected: Integer; function Getsql: TWideStrings; procedure Setsql(const Value: TWideStrings); procedure Open; constructor Create(AOwner: TComponent); override; destructor Destroy; override; function Execsql: Integer; {for TQuery compatibility} property RowsAffected: Integer read FRowsAffected; published property CommandTimeout; property DataSource; property EnableBCD; property ParamCheck; property Parameters; property Prepared; property sql: TWideStrings read Fsql write Setsql; end; //////////////////////////////////////////////////////// //////////////////////////////////////////////////////// //////////////////////////////////////////////////////// constructor TADOQueryTurbo.Create(AOwner: TComponent); begin inherited Create(AOwner); Fsql := TWideStringList.Create; TWideStringList(Fsql).OnChange := QueryChanged; Command.CommandText := 'sql'; { Do not localize } end; destructor TADOQueryTurbo.Destroy; begin inherited; inherited Destroy; FreeAndNil(Fsql); end; function TADOQueryTurbo.Execsql: Integer; begin CommandText := Fsql.Text; inherited; end; function TADOQueryTurbo.Getsql: TWideStrings; begin Result := Fsql; end; procedure TADOQueryTurbo.Open; begin CommandText := Fsql.Text; inherited Open; end; procedure TADOQueryTurbo.QueryChanged(Sender: TObject); begin // if not (csLoading in ComponentState) then // Close; // CommandText := Fsql.Text; end; procedure TADOQueryTurbo.Setsql(const Value: TWideStrings); begin Fsql.Assign(Value); CommandText := Fsql.Text; end;
解决方法
这是我做的事情
>根据查询设置TAdoDataSet.CursorLocation:
> clUseClient如果查询获取GUI和查询的记录是相对“简单”的 – 没有分组或总和
> clUseServer如果查询有某种聚合(sum,grouping,counting)
>根据查询设置TAdoDataSet.CursorType:
> ctForward仅适用于不需要滚动浏览数据集的报表 – 仅适用于clUseServer
> ctStatic for GUI.这只是使用clUseClient的模式
>根据查询设置TAdoDataSet.LockType:
> ltReadOnly只针对不用于编辑的每个数据集(网格,报告)
>当更改后立即将数据发布到数据库时(例如用户编辑表单上的数据)
>当您更改大量记录时,ltBatchOptimistic.这是为了获取记录数量的情况,然后对它们进行一些处理,然后批量发送更新到数据库.这最好结合clUseClient和ctStatic.
>根据我的经验,Oracle的OLEDB提供商比Oracle OleDb提供者更好.你应该测试一下.编辑:检查Fabricio关于可能的blob问题的评论.>用TAdoDataSet替换TAdoQUery. TAdoQuery是为将应用从BDE转换为ADO而创建的,但是Borland / Codegear的推荐是使用TAdoDataSet>重新检查Oracle连接字符串以确保您没有网络延迟.连接到Oracle需要多长时间? TnsPing多长时间?