SQL Server 事务与锁

序言

为何须要事务?sql

  当多个线程都开启事务操做数据库中的数据时,数据库系统要能进行隔离操做,以保证各个线程获取数据的准确性, 因此, 对于不一样的事务,采用不一样的隔离级别会有不一样的结果。数据库

若是没有事务隔离,会出现什么样的状况?并发

http://blog.itpub.net/26736162/viewspace-2638951/分布式

事务

  事务就是做为一个逻辑工做单元的SQL语句,若是任何一个语句操做失败那么整个操做就被失败,之后操做就会回滚到操做前状态,或者是上个节点。为了确保要么执行,要么不执行,就可使用事务。ide

 

 

Sql中使用事务

测试数据测试

--建立一个帐户表,添加约束,余额(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)
View Code

测试转帐事务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
View Code

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); } } }
View Code

  上边的代码执行时,因为知足约束条件(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(); } }
View Code

  上边的代码十分简单,咱们能够看到使用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;
View Code

回滚事务

BEGIN TRAN Tran_Money; INSERT INTO [dbo].[Money]([Name],[Money])VALUES('沐风',100) ROLLBACK TRAN;
View Code

转帐事务

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
转帐事务

锁是实现事务的关键,锁能够保证事务的完整性和并发性。数据库中的锁也是为了解决在并发访问时出现各类冲突的一种机制。

锁的目的是什么?

主要解决多个用户同时对数据库的并发操做时会带来如下数据不一致的问题:

  • 丢失更新,同时修改一条数据
  • 读脏,A修改了数据后,B读取后A又取消了修改,B读脏
  • 不可重复读,A用户读取数据,随后B用户读取该数据并修改,此时A用户再读取数据时发现先后两次的值不一致
  • 还有一种是幻读,这个状况好像很少。

并发控制的主要方法是封锁,锁就是在一段时间内禁止用户作某些操做以免产生数据不一致

 锁的粒度有哪些?

  • 数据库锁:锁定整个数据库,这一般发生在整个数据库模式改变的时候。
  • 表锁:锁定整个表,这包含了与该表相关联的全部数据相关的对象,包括实际的数据行(每一行)以及与该表相关联的全部索引中的键。
  • 区段锁:锁定整个区段,由于一个区段由8页组成,因此区段锁定是指锁定控制了区段、控制了该区段内8个数据或索引页以及这8页中的全部数据行。
  • 页锁:锁定该页中的全部数据或索引键。
  • 行或行标识符:虽然从技术上将,锁是放在行标识符上的,可是本质上,它锁定了整个数据行。

行级锁

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"; }

 

 

 

资料

事务的4种隔离级别(Isolation Level)分别是什么?

相关文章
相关标签/搜索