为何须要事务?sql
当多个线程都开启事务操做数据库中的数据时,数据库系统要能进行隔离操做,以保证各个线程获取数据的准确性, 因此, 对于不一样的事务,采用不一样的隔离级别会有不一样的结果。数据库
若是没有事务隔离,会出现什么样的状况?并发
http://blog.itpub.net/26736162/viewspace-2638951/分布式
事务就是做为一个逻辑工做单元的SQL语句,若是任何一个语句操做失败那么整个操做就被失败,之后操做就会回滚到操做前状态,或者是上个节点。为了确保要么执行,要么不执行,就可使用事务。ide
测试数据测试
--建立一个帐户表,添加约束,余额(money)不小于零 create table Tb_bankAcount( Id int identity(1,1) primary key, Name nvarchar(20) not null, Money int not null ) alter table Tb_bankAcount add constraint CK_money CHECK(money>=0) --添加数据 insert into Tb_bankAcount values('A',200) insert into Tb_bankAcount values('B',200)
测试转帐事务spa
begin transaction --开启事务 declare @errorCount int=0;--记录错误的变量 update Tb_bankAcount set Money-=500 where Name='A' set @errorCount+=@@ERROR update Tb_bankAcount set Money+=500 where Name='B' set @errorCount+=@@ERROR if @errorCount>0 --有错误就回滚 rollback transaction else --没有错误提交 commit transaction
Ado.Net中使用事务(SqlTransaction形式).net
using (SqlConnection conn = new SqlConnection(connStr)) { //要执行的sql脚本 string sqlText = @"update Tb_bankAcount set Money-=100 where Name='A' update Tb_bankAcount set Money+=100 where Name='B'"; conn.Open(); SqlTransaction tran = conn.BeginTransaction(); using (SqlCommand com = new SqlCommand(sqlText, conn)) { try { //开启事务 com.Transaction = tran; com.ExecuteNonQuery(); //提交事务 tran.Commit(); Console.WriteLine("事务执行成功"); } catch (Exception ex) { //回滚事务 tran.Rollback(); Console.WriteLine(ex.Message); } } }
上边的代码执行时,因为知足约束条件(Money>0),执行事务提交。线程
使用SqlTransaction执行事务时,每一个事务都是基于SqlConnection的,若是咱们的事务要跨越多个程序集或者使用多个数据库时,使用SqlTransaction来实现事务就比较麻烦了,针对这个问题.net 2.0出现了TransactionScopecode
Ado.Net中使用分布式事务(TransactionScope形式)
static void Main(string[] args) { //链接字符串 string connstr1 = @"your connctionString1"; string connstr2 = @"your connctionString2"; using (TransactionScope ts = new TransactionScope()) { #region 执行任务1 using (SqlConnection conn1 = new SqlConnection(connstr1)) { using (SqlCommand com = conn1.CreateCommand()) { conn1.Open(); com.CommandText = "delete from t_stu where id=10"; com.ExecuteNonQuery(); } } #endregion #region 执行任务2 using (SqlConnection conn2 = new SqlConnection(connstr2)) { using (SqlCommand com = conn2.CreateCommand()) { conn2.Open(); com.CommandText = "insert into t_stu(stuname,age) values ('zs',22')"; com.ExecuteNonQuery(); } } #endregion //经过ts.Complete()方法进行提交 ts.Complete(); } }
上边的代码十分简单,咱们能够看到使用TransactionScope能够轻松的构建分布式的事务模型,conn1和conn2两个链接能够链接不一样的数据库。TransactionScope实现了IDispose()接口,咱们可使用using语法糖来自动释放资源。执行TransactionScope时会依此执行TranactionScope的全部代码,当执行到ts.Complete()时表示事务中的任务都执行完成了,进行提交。若是不显示地执行ts.Complete()方法,TransactionScope中代码执行完毕后执行回滚操做。
提交事务
BEGIN TRAN Tran_Money; INSERT INTO [dbo].[Money]([Name],[Money])VALUES('沐风',100) COMMIT TRAN;
回滚事务
BEGIN TRAN Tran_Money; INSERT INTO [dbo].[Money]([Name],[Money])VALUES('沐风',100) ROLLBACK TRAN;
转帐事务
BEGIN TRAN Tran_Money; --开始事务 DECLARE @tran_error INT; SET @tran_error = 0; BEGIN TRY UPDATE dbo.Money SET Money = Money - 30 WHERE Name = '张三'; SET @tran_error = @tran_error + @@ERROR; --测试出错代码,看看张三的钱减小,李四的钱是否会增长 --SET @tran_error = 1; UPDATE dbo.Money SET Money = Money + 30 WHERE Name = '李四'; SET @tran_error = @tran_error + @@ERROR; END TRY BEGIN CATCH PRINT '出现异常,错误编号:' + CONVERT(VARCHAR, ERROR_NUMBER()) + ',错误消息:' + ERROR_MESSAGE(); SET @tran_error = @tran_error + 1; END CATCH; IF ( @tran_error > 0 ) BEGIN --执行出错,回滚事务 ROLLBACK TRAN; PRINT '转帐失败,取消交易!'; END; ELSE BEGIN --没有异常,提交事务 COMMIT TRAN; PRINT '转帐成功!'; END
锁是实现事务的关键,锁能够保证事务的完整性和并发性。数据库中的锁也是为了解决在并发访问时出现各类冲突的一种机制。
主要解决多个用户同时对数据库的并发操做时会带来如下数据不一致的问题:
并发控制的主要方法是封锁,锁就是在一段时间内禁止用户作某些操做以免产生数据不一致
行级锁
select * from tablename with (rowlock) where id=3
select * from tb WITH(XLOCK) where id = 5
public partial class SqlWith { public const string NoLock = "WITH(NOLOCK) "; public const string HoldLock = "WITH(HOLDLOCK)"; public const string PagLock = "WITH(PAGLOCK)"; public const string ReadCommitted = "WITH(READCOMMITTED)"; public const string TabLockX = "WITH(TABLOCKX)"; public const string UpdLock = "WITH(UPDLOCK)"; public const string RowLock = "WITH(ROWLOCK)"; public const string Null = "Non"; }