事务学习总结
一、 概念理解
1.事务概念
数据库事务(Database Transaction),是指作为单个逻辑单元执行的一系列操作。事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的资源。通过将一组相关操作组合为一个要么全部成功要么全部失败的单元,可以简化错误恢复并使应用程序更加可靠。一个逻辑工作单元要成为事务,必须满足所谓的ACID(原子性、一致性、隔离性和持久性)属性。
2.ACID
【原子性】
(atomic)(atomicity)
事务必须是原子工作单元;对于其数据修改,要么全都执行,要么全都不执行。事务中的所有操作作为一个整体提交或回滚,是不可折分的,事务是一个完整的操作。
【一致性】
(consistent)(consistency)
事务在完成时,必须使所有的数据都保持一致状态。在相关数据库中,所有规则都必须应用于事务的修改,以保持所有数据的完整性。事务结束时,所有的内部数据结构(如 B 树索引或双向链表)都必须是正确的。某些维护一致性的责任由应用程序开发人员承担,他们必须确保应用程序已强制所有已知的完整性约束。例如,当开发用于转帐的应用程序时,应避免在转帐过程中任意移动小数点。(例如:提款两个步骤 :扣钱,取款一个事务 ,不会出现不扣钱取款,或扣钱不取款的情况。)
【隔离性】
(insulation)(isolation)
由并发事务所作的修改必须与任何其它并发事务所作的修改隔离。事务查看数据时数据所处的状态,要么是另一并发事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看中间状态的数据。这称为隔离性,因为它能够重新装载起始数据,并且重播一系列事务,以使数据结束时的状态与原始事务执行的状态相同。当事务可序列化时将获得最高的隔离级别。在此级别上,从一组可并行执行的事务获得的结果与通过连续运行每个事务所获得的结果相同。由于高度隔离会限制可并行执行的事务数,所以一些应用程序降低隔离级别以换取更大的吞吐量。防止数据丢失
【持久性】
(Duration)(durability)
事务完成之后,它对于系统的影响是永久性的。该修改即使出现致命的系统故障也将一直保持。
3.处理模型
事务有三种模型:
1.【显式事务】就是可以显式地在其中定义事务的开始和结束的事务。在 SQL Server 7.0 和早期版本中,显式事务也称为“用户定义的事务”或“用户指定的事务
2.【隐式事务】是指事务的开始是隐式的,事务的结束有明确的标记。当连接以隐性事务模式进行操作时,SQL Server 数据库引擎实例将在提交或回滚当前事务后自动启动新事务。无须描述事务的开始,只需提交或回滚每个事务。隐性事务模式生成连续的事务链
3.【自动事务】是系统自动默认的,开始和结束不用标记。(自动提交事务每条单独的语句都是一个事务。)
二、 SQLServer事务
新建表Table_1
|
ID |
Title |
Notes |
MTime |
|
1 |
张三 |
备注1 |
2011-01-01 |
|
2 |
李四 |
备注2 |
2011-01-02 |
|
3 |
王五 |
备注3 |
2011-01-03 |
|
4 |
赵二 |
备注4 |
2011-01-04 |
1.语法
|
BEGIN TRANSACTION --标记一个显式本地事务的起始点 --格式:BEGIN TRAN [ SACTION ] [ transaction_name | @tran_name_variable [ WITH MARK [ 'description' ] ] ]
BEGIN DISTRIBUTED TRANSACTION --指定一个由Microsoft分布式事务处理协调器(MS DTC)管理的Transact—SQL分布式事务的起始 --分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。 --比如:事务中的两个sql语句分别操作了不同服务器上的两个数据库中的数据就要使用“BEGIN DISTRIBUTED TRANSACTION”
COMMIT TRANSACTION -- 标记事务结束 COMMIT WORK --标记事务结束 --格式:COMMIT [ WORK] --此语句的功能与 COMMIT TRANSACTION 相同,但 COMMIT TRANSACTION 接受用户定义的事务名称。 ROLLBACK WORK --格式:ROLLBACK [TRAN [ SACTION ] [ transaction_name | @tran_name_variable | savepoint_name | @savepoint_variable ] ]
SAVE TRANSACTION --在事务内设置保存点 --格式:SAVE TRAN[SACTION]{savepoint_name|@savepoint_variable} --当条件回滚只影响事务的一部分时使用savepoint_name
SET XACT_ABORT {ON|OFF} --当SET XACT_ABORT为ON时,如果Transact-SQL语句产生运行时错误时,整个事务将终止并回滚。为OFF时,只回滚产生错误的Transact-SQL语句,事务将继续进行处理。 --编译错误(如语法错误)不受SET XACT——ABORT的影响。
SET IMPLICIT_TRANSACTIONS ON/OFF --开启或关闭隐式事务
SELECT @@TRANCOUNT AS [Transaction Count] --测试是否已经打开一个事务 |
2.分布事务
例1:分布式事务
|
1. 准备工作 --创建连接实例 --在连接实例(Server240DB)上创建登录,要输入用户名和密码 --查看连接实例 --删除连接实例 --删除连接名称的登录 --打开cmd,输入以下命令启动msdtc,用于支持跨服务器事务 2. sp Set XACT_abort ON --一定要加上这一个命令,不然在执行过程中报错不会直接结束事务并回滚 Update Server247DB.Bets.dbo.Users Set Point = 0 Where UserName='aaj71' --注意:在同一台服务器上的两个数据库,不能使用连接实例,会报错,因为系统会认为重复指向
|
3. 嵌入式事务
4. SET XACT_ABORT {ON|OFF}
三、 事务与锁__ sql中的隔离级别_并发控制
1. 数据库系统一个明显的特点是多个用户共享数据库资源,尤其是多个用户可以同时存取相同数据。
串行控制:如果事务是顺序执行的,即一个事务完成之后,再开始另一个事务
并行控制:如果DBMS可以同时接受多个事务,并且这些事务在时间上可以重叠执行。
2.并发控制概述
事务是并发控制的基本单位,保证事务ACID的特性是事务处理的重要任务,而并发操作有可能会破坏其ACID特性。
DBMS并发控制机制的责任:
对并发操作进行正确调度,保证事务的隔离性更一般,确保数据库的一致性。
如果没有锁定且多个用户同时访问一个数据库,则当他们的事务同时使用相同的数据时可能会发生问题。由于并发操作带来的数据不一致性包括:丢失数据修改、读”脏”数据(脏读)、不可重复读、产生幽灵数据。
(1)【丢失数据修改】
当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,会发生丢失更新问题。每个事务都不知道其它事务的存在。最后的更新将重写由其它事务所做的更新,这将导致数据丢失。如上例。
再例如,两个编辑人员制作了同一文档的电子复本。每个编辑人员独立地更改其复本,然后保存更改后的复本,这样就覆盖了原始文档。最后保存其更改复本的编辑人员覆盖了第一个编辑人员所做的更改。如果在第一个编辑人员完成之后第二个编辑人员才能进行更改,则可以避免该问题。
(2)【读“脏”数据(脏读)】
读“脏”数据是指事务T1修改某一数据,并将其写回磁盘,事务T2读取同一数据后,T1由于某种原因被除撤消,而此时T1把已修改过的数据又恢复原值,T2读到的数据与数据库的数据不一致,则T2读到的数据就为“脏”数据,即不正确的数据。
例如:一个编辑人员正在更改电子文档。在更改过程中,另一个编辑人员复制了该文档(该复本包含到目前为止所做的全部更改)并将其分发给预期的用户。此后,第一个编辑人员认为所做的更改是错误的,于是删除了所做的编辑并保存了文档。分发给用户的文档包含不再存在的编辑内容,并且这些编辑内容应认为从未存在过。如果在第一个编辑人员确定最终更改前任何人都不能读取更改的文档,则可以避免该问题。
例2:验证脏读,不重复读
新建两个查询链接
第一个查询链接里执行:
select * from Table_1
begin transaction MyTrans1--开始事务
update Table_1 set Notes='李四备注'
select ID as flag,* from Table_1
waitfor delay '00:00:10' --等待秒
rollback tran --回滚事务
select * from Table_1
第二个查询链接里执行:
set transaction isolation level read uncommitted--READ COMMITTED
print '脏读:'
select ID as flag,* from Table_1
begin
waitfor delay '00:00:10' --等待秒
print '不重复读'
select * from table1
end
(3)【不可重复读】
指事务T1读取数据后,事务T2执行更新操作,使T1无法读取前一次结果。例如:事务T1读取某一数据后,T2对其做了修改,当T1再次读该数据后,得到与前一不同的值。
不可重复读类似于脏读,只不过它发生在事务看到其他事务已经提交的数据更新的情况下。
(4)【产生幽灵数据】
按一定条件从数据库中读取了某些记录后,T2删除了其中部分记录,当T1再次按相同条件读取数据时,发现某些记录消失
T1按一定条件从数据库中读取某些数据记录后,T2插入了一些记录,当T1再次按相同条件读取数据时,发现多了一些记录。
ANSI SQL-92定义了4个隔离级别:
SQL Server使用锁来实现隔离性级别。鉴于锁影响到性能,用户必须在隔离级别和性能之间进行权衡。SQL Server的默认隔离级别是Read Committed,这对于大多数OLTP项目都是适用的。
◊ 级别1——Read Uncommitted
最不严格的隔离级别是Read Uncommitted,它不能防止任何一种事务缺陷,因为它根本没有在事务间提供隔离。把SQL Server的隔离级别设为Read Uncommitted,等同于把SQL Server的锁设置为NOLOCK。这种设置适合报表或只读的应用程序,因为此时SQL Server只为防止数据崩溃提供足够的锁,而不会为行竞争提供足够的锁,这对数据经常被更新的系统是不合适的。
◊ 级别2——Read Committed
Read Committed防止了最严重的事务缺陷,而又不会是系统陷入过度锁争用的泥潭。基于这个原因,SQL Server将她作为默认的隔离级别,对于绝大多数的OLTP项目来说,它都是一个理想的选择。
◊ 级别3——Repeatable Read
Repeatable Read可以防止脏读和不可重复读,它增加了事务的隔离级别,而因此带来的锁争用的压力没有Serializable隔离级别那样的严重。
◊ 级别4——Serializable
这是最严格的隔离级别,它防止了全部的事务缺陷,并且通过了在上面的隔离定义中所提到的串行事务测试。这种模式适用于对于绝对的事务完整性的要求比性能更为重要的情况。银行、账务系统、高度竞争性的销售数据库(例如股票市场)通常会使用Serializable隔离级别。
使用Serializable隔离级别相当于把锁设为HOLDLOCK,这将会使事务在整个执行期间都保持锁,甚至包含共享锁。这种设置虽然提供了完全的事务隔离性,却会造成恶劣的锁争用,并使性能降低。
3、SQL Server的锁机制
SQL Server用锁来实现事务之间的隔离,这样可以防止一个事务所操作的数据受到另外一个事务影响。每个所都具有以下3个特性:
◊ 粒度(Granularity)——锁的大小
◊ 模式(Mode)——锁的类型
◊ 持续期(Duration)——锁的隔离模式
3.1、锁的粒度
SQL Server的锁管理器试图在锁大小和数量之间寻求平衡以争取教好的性能。矛盾的焦点在并发(较小的锁可以允许更多的事务同时存取数据)和性能(锁越少速度越快)。为了达到平衡,锁管理器会动态地从一组锁切换到另外一组锁。
1>、25个行锁有可能升级为一个页锁。
2>、如果在同一个扩展区的其他4个以上的页面上分布着25个以上被锁定的行,上述页锁和这25个行级锁就可能升级为一个扩展盘区锁,因为该扩展盘区上有50%以上的页面都受到了锁定的影响。
3>、如果有足够的扩展盘区被锁住,所有这些锁就可能升级为一个表锁。
动态调整的锁策略为SQL Server的开发人员带来了显著的益处:
◊ 无需任何编程,就可以自动地在性能和并发之间取得最佳平衡;
◊ 随着数据库的增长,锁管理器会相应地使用与之匹配的锁粒度,从而保持数据库具有良好的性能;
◊ 动态锁定简化了管理工作。
3.2、锁模式
除了锁粒度也就是锁大小的属性之外,锁还具有锁模式属性,它确定了锁定的用途。SQL Server具有丰富的锁模式。
1>、锁争用
在SQL Server中,锁的相互作用与兼容性对事务完整性和性能都有很重要的影响。一些锁模式会排斥另一些锁模式。锁的兼容性如下:
2>、共享锁(S)
到目前为止,最常用也是最为滥用的锁就是共享锁,它是一个简单的“读锁”。事务得到了共享锁就好比是在宣称“我正在查看这个数据”。通常多个事务可以同时查看同一组数据,当然最终还要取决于隔离模式。
3>、排它锁(X)
使用排它锁意味着事务正在写数据。对于同一数据,在同一时间只能有一个事务持有排它锁,其他事务在排它锁持续期间不能查看该数据。
4>、更新锁(U)
这是一个中继锁,更新锁并不是事务执行更新时所使用的锁,更新锁意味着事务即将要使用排它锁,它当前正在扫描数据,以确定使用排它锁锁定的那些行。可以将更新锁当作即将转化为排它锁的共享锁。
为了避免死锁,在同一个时刻只运行使用一个事务持有更新锁。
5>、意向锁
意向锁是一种用于警示的锁,它警告其他事务即将要发生一些事情。意向锁的主要目的是提高性能。
3.3、查看锁
exec sp_lock
select * from sys.dm_tran_locks
注:后续版本的 Microsoft SQL Server 将删除该功能。请避免在新的开发工作中使用该功能,并着手修改当前还在使用该功能的应用程序。若要获取有关 SQL Server 数据库引擎中的锁的信息,请使用sys.dm_tran_locks动态管理视图。
四、 C#中事务
1.
/// <summary>
/// 执行多条SQL语句,实现数据库事务。
/// </summary>
/// <param name="SQLStringList">多条SQL语句</param>
public static bool ExecuteSqlTran(ArrayList SQLStringList)
{
bool result = false;
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection=conn;
SqlTransaction tx=conn.BeginTransaction();//声明数据库事务,并开始事务
cmd.Transaction=tx;//将事务与数据库执行命令对象关联
try
{
for(int n=0;n<SQLStringList.Count;n++)
{
string strsql=SQLStringList[n].ToString();
if (strsql.Trim().Length>1)
{
cmd.CommandText=strsql;
cmd.ExecuteNonQuery();
}
}
tx.Commit();//提交数据库事务
result = true;
}
catch(System.Data.SqlClient.SqlException E)
{
result = false;
tx.Rollback();//回滚数据库事务
throw new Exception(E.Message);
}
}
return result;
}
2.
/// <summary>
/// 批量添加用户与模板的关联
/// </summary>
/// <returns></returns>
public bool addList(List<UserInTemplate> list)
{
using (TransactionScope ts = new TransactionScope())//TransactionScope使代码块成为事务性代码
{
if (list.Count > 0 && this.delByTemplateID(list[0].TemplateID))
{
foreach (UserInTemplate item in list)
{
if (!this.Add(item)) return false;
}
}
else { return false; }
ts.Complete();//指示范围内的所有操作都已完成
return true;
}
}
参阅:http://baike.baidu.com/view/1298364.htm
浙公网安备 33010602011771号