|
测试用的数据库表也很简单,25万行数据,4个字段。通过检查span.ToString的结果,发现同样的代码,ADO.NET 2.0比1.1多用了近一倍的时间。dap.Fill方法的执行时间从原来的3秒增加到6秒。{#page#} 1.1.2 悲观和绝望 应该如何着手解决这个问题? 我测试完成,看到这个结果后,我的感觉:悲观。看看我们能够做什么: 1.后台数据库表格的定义非常简单。4个字段都是int,都是在没有index、primary key、foreign key等约束条件下测试的。也就是说,这个问题跟数据表的Schema定义无关,完全是客户端的问题。 2.代码已经非常简单。Console工程里Main函数就做这么一件事情。客户端没有任何可以修改和变动的地方。 3.NET Framework 1.1和.NET Framework 2.0共存在同一个客户端,测试也是在同一个客户端进行的。所以软硬件环境,比如Oracle Client都相同。唯一的区别就是.NET Framework的版本。这也就是客户最关心的地方。 这个工具可以显示出每一个方法(包括子方法)调用所花费的时间,以及占整体运行时间的比例。为了让问题更加明显,这里把数据库的行数增加了10万来方便观察。沿着花费时间比例最多的函数一路走下去,发现DataReader.Read的方法实现分成两部分:自身的托管代码调用和非托管代码调用,分别占用了35%左右的时间和65%左右的时间。有了这个信息后,再通过Reflector分析ADO.NET1.1中的对应函数的实现(VS2005自带的Profiler无法分析.NET Framework 1.1的程序,不然直接分析两者时间比例就可以方便地看出问题),发现非托管代码部分的调用几乎没什么差别,都是调入数据库的客户端非托管DLL。主要差别在托管代码部分。其中引起注意的是ADO.NET 2.0增加了SafeHandle.Dangerous- AddRef/DangerousRelease调用。每一对这样的调用就要花费7%的时间。而每读取一行数据,需要用大约3对这样的调用。经过比较分析,认为问题就是在这里。 由于对数据库的操作和数据填充最终通过调用非托管的数据库引擎来完成,所以需要向非托管的DLL传入托管代码管理的缓存空间,而SafeHandle就是管理这种资源的。在.NET Framework 1.1中,由于缺少SafeHandle类,高负载环境下程序存在表现不稳定的危险,没有完美的解决方法。.NET Framework 2.0增加了SafeHandle来保证程序的可靠性。然而,代价就是25万行数据发生3秒钟的时间损失。(后来经开发人员确认,这3秒钟的损失,在下一版本的Framework也可以想办法优化掉!) 1.1.5 结论和收获 在跟客户作进一步的交流后,这个问题的结论如下: 1. MSDN文章的介绍是正确的。如果用文章里面的例子,的确可以看到性能有数量级上的提升。 . 在客户的真实环境中,损失的3秒时间其实不会对最终用户和整个程序造成影响。 3.3秒钟不是白白损失的,换来的是程序的可靠性。 |