博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

ADO.net性能

Posted on 2011-01-01 22:46  itcfj  阅读(337)  评论(0编辑  收藏  举报

总的考虑方向

1) 根据数据使用的方式来设计数据访问层

http://www.mscto.com

2) 缓存数据,避免不必要的操作

3) 使用服务帐户进行连接 软件开发网

4) 必要时连接,尽早释放

5) 关闭可关闭的资源 http://www.mscto.com

6) 减少往返

7) 仅返回需要的数据

8) 选择适当的事务类型

9) 使用存储过程 http://www.mscto.com

根据性能、可维护性、及实现难度来决定跨层数据传递的方式

2 具体实现 软件开发网

1)选用合适的Data Provider
应尽量使用专用的Data Provider,下面是一个性能比较表

由上图中可以看出SqlClient的速度是最快的,其主要原因是其他的数据提供者都经过的几个层次的转换,如下图

软件开发网

从图中可以看出,SqlClient直接访问DB Netlib而其他的数据提供者都经过了两层转换,因此,在设计多层应用的时候,并不是层次越多越好,而是应该在可扩展性与性能间取折中,增加层次是会降低性能的。

1) 数据库连接

http://www.mscto.com

i. 在方法中打开和关闭连接,即不要在类的构造函数中打开连接,在类的析构函数中关闭连接。

软件开发网

ii. 使用完连接明确地关闭。因为有连接池的支持,关闭连接只是将连接放回连接池,并不是真正销毁,不会带来性能开销,而会增加连接池中可用连接,提升性能。

iii. 当使用DataReaders时,指定CommandBehavior.CloseConnection

iv. 当使用Fill()与Updata()时,不要手动打开连接。因为DataAdapter会自动开启连接,但是如果是Command则需要手动开启。

v. 避免检查OleDbConnection的State属性,其性能开销相当大。

vi. 使用连接池。这种方法可以大幅度提高性能。默认的情况下,通过SqlClient连接数据库时,会使用连接池,另可以通过连接字符串来控制连接池的最大值,最小值,以及是否开启连接池。

2) SQL指令

软件开发网

i. 检查SQL的输入,并使用参数,直接使用字符连接容易遭受注入式攻击。http://www.mscto.com

ii. 仅返回需要的行和例

http://www.mscto.com

iii. 对大的数据集使用分页功能

iv. 批次执行SQL,避免多次往返。

v. 如果没有数据返回则使用ExecuteNonQuery方法

vi. 当返回一个标量时,使用ExecuteScalar方法

vii. 不要在运行时间使用CommandBuilder,尽管很省事,但是开销很大。

3) 存储过程

i. 尽量使用存储过程

ii. 对于OleDbCommand,指令类型为CommandType.Text

iii. 使用SqlCommand,指令类型为CommandType.StoredProcedure

iv. 尽可能使用输出参数

v. 考虑在SQL Server中SET NOCOUNT ON,即关闭SQL Server的记数功能

) 事务

5) 使用参数

i. 在存储过程上使用参数

ii. 创建参数并指定类型 软件开发网

iii. 可将参数对象进行缓存

6) DataReader和DataSet

http://www.mscto.com

i. DataReader对象需要关闭

ii. 用DataReader时应,使用CommandBehavior.CloseConnection关闭连接

iii. DataReader应用在只读、只向前翻滚的数据访问场景

iv. 只想快速访问数据,不需要缓存功能应使用DataReader

DataSet在需要数据缓存,并在不同层间传递时使用。它可以存放多个结果集,可以在离线的情况下自由定位,查找数据。

http://www.mscto.com

总的来说,提高性能会降低可扩展性,以及维护难度,应在满足功能与非功能需求的情况下提高性能,另应在设计时就考虑性能,好的设计在性能上的提升比差的设计后期再修改要好得多。


使用ADO.NET2.0提升数据交互性能(1)

这篇文章起源于在公司写的一个PPT,但是由于PPT本身的限制很多内容无法表达或是详细的解释,于是变下定了决心。写篇文档!

  在这篇文章里我将尽量简单的描述下ADO.NET 2.0的新特性,尤其是配合SQL Server 2005所展现出来的强大实力。如果想进一步了解ADO.NET2.0编程方面的话,可以去阅读Glenn Johnson的--"ADO.NET2.0高级编程[微软推荐丛书] ".定价:46元,网络购书的话打了折只要30块就可以了。


  一:功能强大的ADO2.0

  2005年底(2005年10月)与 SQL Server 2005一起出现的是 .NET Framework 2.0 版本,其中用来访问数据库的 ADO.NET类也升级到 ADO.NET 2.0 版。


  ADO.NET 2.0 除了增强旧功能外,也提供了相当多的新功能,包含了以基础类为本(base-class-based)的数据源提供程序(provider)模型、异步访 问架构、批处理更新与大量数据复制(bulk copy)、SQL Server 2005 的回调通知、单一连接同时多执行结果集(MARS)、执行统计、强化的 DataSet 类等等。换句话说,若要有效发挥 SQL Server 2005 的功能,前端应用程序最好用 ADO.NET 2.0 来开发。


  ADO.NET 2.0 提供了相当多的新增功能,一些与数据源提供程序无关,也就是访问各种数据库都可以用到的功能,但有很大的一部分是专属于 SQL Server 2005,针对 SQL Server 2005 的新功能提供给前端应用程序开发使用。


  二: 使用多数据结果集(仅限2005)

  在之前版本的 SQL Server 同一时间一条连接只能传递一个 SELECT 语法执行后返回的结果集。如果想在一次连接后返回多个查询内容只能使用类似如下的方法来实现:

SqlDataAdapter   myDataAdapter   =   new  SqlDataAdapter("StoredProcedureName",myConnection);

myDataAdapter.SelectCommand.CommandType  =   CommandType.StoredProcedure;

myDataAdapter.SelectCommand.Parameters.Add("@sqlstr",sqlstr);

DataSet   ds   =   new   DataSet();

myDataAdapter.Fill(ds);

return   ds;

ds.Tables[0],ds.Tables[1],ds.Tables[2],分别对应三个结果集

 

  SQL Server 2005提供了在同一条连接上可以同时传递多个没有游标结构(cursorless)的结果集(也称为默认结果集),此功能称为 MultipleActive Resultsets(MARS)。如此可以节省需要同时打开的连接数,但要注意的是连接字符串设置要加上 MultipleAct-iveResultSets=true 属性,否则默认不启动多数据结果集的功能。

string connstr ="server=(local);database=northwind;integrated security=true; ";

SqlConnection conn = new SqlConnection(connstr);

conn.Open();

SqlCommand cmd1 = new SqlCommand("select * fromcustomers", conn);

SqlCommand cmd2 = new SqlCommand("select * from orders",conn);

SqlDataReader rdr1 = cmd1.ExecuteReader();

// next statement causes an error prior to SQL Server 2005

            SqlDataReader rdr2 =cmd2.ExecuteReader();

// now you can reader from rdr1 and rdr2 at the same time.

 

 

 

 

5.轻量级对象和快速遍历

  ADO.NET 2.0中的DataTable提供了CreateDataReader方法(在之前的版本名为GetDataReader),该方法将创建一个 DataTableReader对象。DataTableReader与DataTable不同,它是一个轻量级的对象,其支持 Disconnected,这一点与DataReader(SqlDataReader)不同。这些特点决定遍历DataTableReader对象将更 加快速,占用的数据资源更少(Disconnected)。下面的代码创建了一个DataTableReader对象,并将其绑定到 DataGridView控件上:

using (SqlConnection cn = newSqlConnection(cnStr))

{

SqlCommand cmd = new SqlCommand(sqlAllCustomers, cn);

SqlDataAdapter adpt = new SqlDataAdapter(cmd);

DataTable dtCustomers = new DataTable("Customers");

adpt.Fill(dtCustomers);

DataTableReader dtRdr = ds.CreateDataReader();

dgvCustomers.DataSource = dtRdr;

}

 

 

三:异步执行Command命令

  在 ADO.NET 2.0 以前,通过 Command 类(如 SqlCommand、OleDbCommand等)执行 SQL

  命令的线程一定要停下来等待执行结果。ADO.NET 2.0 新增了异步程序访问接口(asynchronous API),让线程发出命令后可以继续执行接下去的程序代码。

  而在 ADO.NET 2.0当前的版本只有SqlClient 支持异步程序访问接口。

  以往编写程序时,我们可以直接通过.NET Framework 所提供的多线程机制,或是以 Delegate 类包装多线程的方式,在 .NET Framework 所提供的异步架构下,设计调用执行 Command 对象实例。这些方法都是让一条工作线程(Worker Thread)停止在后台中等待执行结果,一旦有结果后,工作线程再通过标准的机制告知结果。


  原本 ADO.NET 的 Command 对象执行 SQL 语法的方法有

  ExecuteReader、ExecuteNonQuery、ExecuteXmlReader 以及 ExecuteScalar 等,搭配 .NET

  Framework 原来就提供的异步模型惯例,除了 ExecuteScalar 方法外,其余的方法都新增了以 Begin 和 End 关键字开始的一对方法。也就是说 ExecuteReader 方法是同步执行,若要以异步的方式执行相同的功能,则调用 BeginExecuteReader 和 EndExecuteReader 这一组方法。在 .NET Framework 中,以 Begin 为字首的方法负责传入同名方法所需的参数,而以 End

  为字首的方法用来取回执行结果,

  例如某个方法的定义如下:

  public override int ExecuteNonQuery()

  则以异步调用的起始方法定义如下:

  public IAsyncResult BeginExecuteNonQuery(AsyncCallback callback, objectstateObject)

  Begin~ 系列的方法会多加存放回调方法(Delegation)的指针参数,也就是上述语法中的 callback 参数。并提供语法中的 stateObject参数,让你设置想要带到 End~ 对应方法的信息。而Begin~ 系列方法最后返回的是代表异步执行状态的 IAsyncResult 对象实例,而不是原本同步执行方法的返回结果,你可以藉此查询异步执行的状况。


  而获得执行结果的方法定义如下:

  public int EndExecuteNonQuery(IAsyncResult asyncResult)

  在调用与 Begin~ 对应的 End~ 方法时,需要带入 Begin~ 方法所返回的 IAsyncResult

  对象实例。异步执行完毕后,取回与原先同步执行方法相同的执行结果。


  由于我们在执行完Command 对象访问数据库的方法后,都会返回对象,如 ExecuteReader 取回 DataReader实例;ExecuteNonQuery 取回受影响的记录条数;ExecuteXmlReader 取回 XmlReader 实例。因此大概都需要通过End系列方法来获得执行结果,否则这些结果就遗失在系统中。

  若要异步执行 Command命令,另一个必需设置的是:数据库连接字符串内要加上 async=true 属性。若连接字符串没有加上该属性,而通过 Command对象实例调用异步执行的方法,则会产生异常(exception)。若 Command 通过连接执行时,重头到尾都是以同步的方式执行,则依照默认 async=false 的方式设置比较节省资源。若某些命令需要同步执行,另一些需要异步执行,则可以考虑使用不同的连接。

  在介绍范例应用程序前,我们先稍微谈一下 .NET Framework 所提供的公共的异步运行应用程序设计模式,不只是 ADO.NET2.0,在其他访问耗时的程序编写上,也都可以套用这个模式。

  .NET Framework内置了让应用程序异步运行的功能,让你在编写应用程序时,不会因为某些耗时等待的操作让程序停止响应,操作界面停滞让用户感觉起来好像死机一样。一般会以多线程的方式处理这种需求,但若你不熟悉线程的运行,或是想利用线程池(Thread Pool)的好处,都可以在较为耗时的操作上,采用 .NET Framework 所提供的异步功能。

  一般来说文件 I/O、网络访问乃至于 Web Services 访问,以及本节所讨论的 DB 访问等都较为耗时,.NET Framework为这一类的类都提供了上述以 Begin~/End~开头的非同步执行方法,而这些方法皆成对出现。当然,也有可能是自己编写的方法其商业逻辑非常复杂,导致调用该方法后,需要等待一段时间来完成,这时还可以通过 .NETFramework 所提供的委托(Delegate)类来创建异步运行。


  但是实际在我们的应用中,
 
  但我们不需要获知DB服务器的返回信息时,我们推荐使 用委托,尤其是在Web开发中。

  因为在页面线程启动异步数据库访问时,当页面业务执行完毕后仍然无法放开访问数据库的异步线程。这是我们不希望看到的,但是使用委托却可以避免这个麻烦(webservice异步应用中一

 

样如此)欢迎进入.NET社区论坛,与200万技术人员互动交流 >>进入

四:使用SqlBulkCopy批量装载数据(仅限SqlClient)

  以往访问 SQL Server 2000 时,若有大量的数据记录需要添加到数据库内,例如从主机系统或是 NCR Teradata、Oracle等数据库系统下载大量数据记录,我们想要将它们快速添加到 SQL Server 2000中,可以有的选择是调用 T-SQL 的 Bulk Insert 语法、通过Linked Server 执行 SELECT INTO 语法或是执行 bcp.exe 工具程序,以及通过 DTS 的 Bulk Insert Task 或启动Transform Data Task 的快速装载(Use Fast Load)设置。


  但若要通过自行编写的程序完成批次装载,只能以 C/C++ 调用 OLEDB 或 ODBC 的 BulkAPI,无法通过 ADO.NET 或 ADO 等对象来执行。

  ADO.NET 2.0 的 SqlClient 提供了一个新的类称为 SqlBulkCopy,它让 DataSet 内大量的数据或是 DataReader通过数据流(Stream)直接读取大量的记录,可以快速将这些记录添加到目的数据库的数据表中。但要注意的是它并非如我们一般用的bcp.exe工具程序,可以从某个符号分隔文件读取大量数据,选择性地搭配格式文件(Format File)将记录装载到数据库中,或是将数据库内的数据导出成为一个文件。但由于DataSet 能集成 XML 数据,因此依然可以采用 SqlBulkCopy 类型,轻松地通过 DataSet 将 XML 文件数据大量转入到数据库。

 

  可以利用SqlBulkCopy类快速写入大批量数据,针对SQL Server的优化,可以写入DataRow数据,DataTable,DataReader,并且可以映射不同的数据列名

   WriteToServer(DataTable)写入数据表

  WriteToServer(DataRow[])批次写入数据行

   WriteToServer(DataTable ,DataRowState)按行状态写入数据库表

   WriteToServer(IDataReader)写入DataReader对象


  下面是个示例:

using (SqlConnection sqlcon =

new SqlConnection("Data Source=192.168.80.242;userid=oa;password=oapassword;initial catalog=test"))

{

sqlcon.Open();

using (SqlBulkCopy bcp = newSqlBulkCopy(sqlcon))

{

bcp.BulkCopyTimeout = 3000;

bcp.DestinationTableName = "dbo.Test01";

bcp.ColumnMappings.Add("id", "id");

bcp.ColumnMappings.Add("name1", "name1");

bcp.ColumnMappings.Add("name2", "name2");

bcp.ColumnMappings.Add("name3", "name3");

//映射到不同名列

 

bcp.ColumnMappings.Add("changedname4", "name4");

bcp.WriteToServer(dt);

sqlcon.Close();

}

}

 

  但是SqlBulkCopy使用时要注意以下几点:

  1   确认确实需要大容量更新在执行此操作,(几十行的数据请尽量使用别的渠道把). 

  2    确认数据一致性,与检查机制,以免遇到主键冲突,数据不符格式等意外。 

  3   SqlBulkCopy操作可能会导致对目标表元数据的更改(例如,禁用约束检查时)。如果出现这种情况

 

五:DataSet的性能提升

  对于开发人员来说,ADO.NET2.0最激动人心的变化莫过于.net开发组终于实现了他们许诺多年的事情:确实提升Dataset的性能了。

  由于1.1版本Dataset令人不敢恭维的性能使得Dataset许多方面被其性能问题而掩盖。

  现在,在大幅度提升了Dataset的性能后。Dataset终于能日趋完美了。


  提升是多方面的,被提升的方面包括下面几块:

  1. 索引引擎被大大的提升
 
  在对ADO.NET 2.0的Dataset作了相当数目的测试后,微软终于宣布 Dataset2.0的数据访问能力获得极大的提高,广泛的数字是增加44倍!!!而且不像1.1中排序的陡峭曲线,2.0中的排序尽量做到了线型递增!

  真不知道.net小组是以前做的实在太烂了,还是有了新的狠招被发明了。^_^


  2. 二进序列制化的Dataset

  Dataset有个好属性是支持序列化,但是有很多人对其提出了批评。不是这些人不喜欢Dataset支持序列化,而是序列化后的Dataset真的是太 肥了。大量的

  <xs:element name=””,type…占用了大量的空间以至让人难以忍受。


  现在我们只需要设置RemotingFormat 属性为SerializationFormat.Binary(默认是SerializationFormat.XML),则在序列化时完全采用二进制的 数据格式,如此数据较小,因而较有效率。(官方的观点是缩为SerializationFormat.XML的1/4)。