事 务
ACID
- Atomic :要么全成功,要么全失败
- Consistent :
- Isolated :每个事务都是一个独立主体,不会影响其它事务。
- Durable :
ASP.NET web application 中三种基本事务类型:
1. Stored procedure transactions: 整个事务完全在数据库内部发生,所以性能最好。缺点:必须用 sql 语句编写事务逻辑。
2. Client-initiated (ADO.NET) transactions: 这些事务被我们编写的程序控制。但在底层会执行与 Stored procedure transactions 一样的命令。在我们编写的程序中用某些 ADO.NET 对象将这些底层细节给包住了,对于我们来说是透明的。
3. COM+ transactions:
事务会对系统的性能有所开销,其实用原则如下:
1. 尽可能短的使用事务。
2. 避免将 select 查询语句放到事务中,最好放到事务之前,这样会减少事务锁定的数据。
3. 如果检索数据,尽可能压缩到必须的数据,减少锁定数据的数量。
4. 最好在 stored procedures 中写入事务,而不是在 ADO.NET transactions ,这样效率更高,数据库服务器不必与系统有额外的数据交换。
5. 如果有多项独立工作块,尽量将工作块用各自的事务,而不是放到一个大事务中。
6. Avoid updates that affect a large range of records if at all possible.
7. 如果只是一个 UPDATE 或 DELETE 或 INSERT 语句,不需要声明事务,因为这些单一语句在数据库内部天生就是事务处理的。
Client-Initiated ADO.NET Transactions
例子:
SqlTransaction tran = null;
try
{
con.Open();// 打开连接
tran = con.BeginTransaction();// 创建事务
// Enlist two commands in the transaction.
cmd1.Transaction = tran;// 将连个命令对象添加到事务中。
cmd2.Transaction = tran;
// Execute both commands.
cmd1.ExecuteNonQuery();
cmd2.ExecuteNonQuery();
// Commit the transaction.
tran.Commit();// 提交事务
}
catch
{
// In the case of error, roll back the transaction.
tran.Rollback();// 如果有异常,事务回滚。
}
finally
{
con.Close();
}
Isolation Levels
基本概念:
- Dirty reads (脏读):一个事务从数据库中读某值,而这值正是另一事务刚修改但还没提交。有回滚的可能。
- Nonrepeatable reads (非重复读) : 在同一个事务中多次查询相同的行,可能返回不同的值。
- Phantom rows ( 幻像读 ) : 在同一个事务中,当初始读时,无此数据,当再次读时,发现多了数据。
不同的 Isolation Level 对应不同的锁
锁的分类:
l Shared locks : 多用户可以利用同一个 Shared locks 来同步 读数据,但锁定的数据不可修改。
l Exclusive locks : 此锁可阻止两个以上 的事务同步 修改锁定的数据。当应用此锁时,其它用户无法读和修改数据。解决方法:在整个事务中,数据库锁住读的行。
在 ADO.NET 中,我们可以自定义我们事务的 Isolation Level ,方法 : 将 IsolationLevel enumeration 值传到 Connection.BeginTransaction() 方法中。
Values of the IsolationLevel Enumeration
1. ReadUncommitted : 无锁,级别最低,可能脏读,性能最好。
2. ReadCommitted ( SQL Server 默认): 用 Shared locks ,无脏读(即, 不能读取事务已修改,但未提交的记录 ),缺点: 不可重读( Non-Repeatable read )和幻读( phantom read )。
3. Snapshot :
4. RepeatableRead : 用 Shared locks ,避免了脏读和 不可重读( Non-Repeatable read ),无法避免幻读( phantom read )。
5. Serializable : range lock 。避免 幻读( phantom read ),但其效率低,用的少。
Creating the Factory
每种 provider 都提供自己的 Factory :
1. Sql Server : System.Data.SqlClient.SqlClientFactory
2. Oracle : System.Data.OracleClient.OracleClientFactory
System.Data.Common.DbProviderFactories 的静态方法 GetFactory() 根据 provider 名返回其 factory 对象。
如:获得 SqlClientFactory :
string factory = "System.Data.SqlClient";// 此字符串可放在系统配置文件里,
DbProviderFactory provider = DbProviderFactories.GetFactory(factory);
可用此 factory 创建 Connection 和 Command 对象。
例子:
<configuration>
<connectionStrings>
<add name="Northwind" connectionString=
"Data Source=localhost; Initial Catalog=Northwind; Integrated Security=SSPI"/>
</connectionStrings>
<appSettings>
<add key="factory" value="System.Data.SqlClient" />
<add key="employeeQuery" value="SELECT * FROM Employees" />
</appSettings>
...
</configuration>
Next, here ’ s the factory-based code:
// Get the factory.
string factory = WebConfigurationManager.AppSettings["factory"];
DbProviderFactory provider = DbProviderFactories.GetFactory(factory);
// Use this factory to create a connection.
DbConnection con = provider.CreateConnection();
con.ConnectionString =
WebConfigurationManager.ConnectionStrings["Northwind"].ConnectionString;
// Create the command.
DbCommand cmd = provider.CreateCommand();
cmd.CommandText = WebConfigurationManager.AppSettings["employeeQuery"];
cmd.Connection = con;
// Open the Connection and get the DataReader.
con.Open();
DbDataReader reader = cmd.ExecuteReader();