zoukankan      html  css  js  c++  java
  • Transactions-事务

    一. 概念

    事务是提供一种机制将一个活动涉及的所有操作纳入到一个不可分割的执行单元,组成事务的所有操作只有在所有操作均能正常执行的情况下方能提交,只要其中任一操作执行失败,都将导致整个事务的回滚。

    二. 事务的的四大特性,ACID

    A:原子性(Atomicity)

    事务的原子性指的是,事务中包含的程序作为数据库的逻辑工作单位,它所做的对数据改操作要全部执行,要么全部不执行。这种特性称为原子性。 

    事务的原子性要求,如果把一个事务看作是一个程序,它要么完整的被执行,要么完全执行。就是说事务的操纵序列或者完全应用到数据库或者完全不影响数据库。这种特性称为原子性  假如用户在一个事务内完成了对数据库的更新,这时所有的更新对外部世界必须是可见的,或者完全没有更新。前者称事务已提交,后者称事务撤销。DBMS必须确保由成功提交的事物完成的所有操作在数据库内有完全的反映,而失败的事务对数据库完全没有影响

     C:一致性(Consistency)

    事务的一致性指的是,在一个事务执行之前和执行之后数据库都必须处于一致性状态。这种特性称为事务的一致性。假如数据库的状态满足所有的完整性约束,就说该数据库是一致的。

    一致性处理数据库中对所有语义约束的保护。假如数据库的状态满足所有的完整性约束,就说该数据库是一致的。例如,当数据库处于一致性状态S1时,对数据库执行一个事务,在事务执行期间假定数据库的状态是不一致的,当事务执行结束时,数据库处在一致性状态S2

    I:隔离性(Isolation)

    隔离性指并发的事务是相互隔离的。即一个事务内部的操作及正在操作的数据必须封锁起来,不被企图进行修改的事务看到  分离性是DBMS针对并发事务间的冲突提供的安全保证。DBMS可以通过加锁在并发执行的事务间提供不同级别的分离。假如并发交叉执行的事务没有任何控制。操纵相同的共享对象的多个并发事务的执行可能引起异常情况。

    DBMS可以在并发执行的事务间提供不同级别的分离。分离的级别和并发事务的吞吐量之间存在反比关系。较多事务的可分离性可能会带来较高的冲突和较多的事务流产。流产的事务要消耗资源,这些资源必须要重新被访问。因此,确保高分离级别的DBMS需要更多的开销

    D:持久性(Durability)

    持久性意味着当系统或介质发生故障时,确保已提交事务的更新不能丢失。即一旦一个事务提交,DBMS保证它对数据库数据的改变应该是永久性的,耐得住任何系统故障

    。持久性通过数据库备份和恢复来保证  持久性意味着当系统或介质发生故障时,确保已提交事务的更新不能丢失。即对已提交事务的更新恢复。一旦一个事务被提交,DBMS必须保证提供适当的冗余,使其耐的住系统故障。所以,持久性主要在于DBMS的恢复性能

    当然,如果你需要自定义事务的原子性逻辑,就需要了解一下显式事务和隐式事务。

    • 隐式事务,就是SQL Server为你自动提交事务,当事务中运行到某一条失败时,会依次回滚之前的操作。
    • 显式事务,就是由你自己指定需要哪些工作必须完成的事务,当事务运行中如果指定工作未完成,会触发回滚操作。

    下面演示一下,在NetCore中,事务的几种用法。

    1.简单事务

    using (var context = new BloggingContext())
    {
        using (var transaction = context.Database.BeginTransaction())
        {
             //插入一条记录
            context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
            context.SaveChanges();
            //再插入一条记录
            context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/visualstudio" });
           context.SaveChanges();
           //查询记录
           var blogs = context.Blogs
                .OrderBy(b => b.Url)
                .ToList();
    
           //提交事务
           transaction.Commit();
           //提交事务如果所有命令都成功,事务将自动回滚
        }
    }

    2.跨上下文事务

    //首先,我们创建同一连接
    var options = new DbContextOptionsBuilder<BloggingContext>()
        .UseSqlServer(new SqlConnection("数据库连接字符串"))
        .Options;
    //创建第一个上下文
    using (var context1 = new BloggingContext(options))
    {
        //创建事务
        using (var transaction = context1.Database.BeginTransaction())
        {
                //第一个上下文进行业务添加
                context1.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
                context1.SaveChanges();
                //创建第二个上下文
                using (var context2 = new BloggingContext(options))
                {
                    //使用DbContext.Database.UseTransaction(DbTransaction)API在同一事务中登记两个上下文
                    context2.Database.UseTransaction(transaction.GetDbTransaction());
                    //第二个上下文进行业务查询
                    var blogs = context2.Blogs
                        .OrderBy(b => b.Url)
                        .ToList();
                }
    
                //提交事务
                transaction.Commit();
        }
    }          

    PS:以上写法,仅在测试SQLServer2017数据库时成功组合,使用MySql数据库则抛出了异常,应该是因为数据库特性导致

    3.多种数据访问技术时共享事务

    using (var connection = new SqlConnection(connectionString))
    {
        connection.Open();
        using (var transaction = connection.BeginTransaction())
        {
            try
            {
                // 使用ADO.NET访问数据库
                var command = connection.CreateCommand();
                command.Transaction = transaction;
                command.CommandText = "DELETE FROM dbo.Blogs";
                command.ExecuteNonQuery();
    
                // 创建EF Core连接
                var options = new DbContextOptionsBuilder<BloggingContext>()
                    .UseSqlServer(connection)
                    .Options;
                //创建上下文实例
                using (var context = new BloggingContext(options))
                {
                    //添加事务到上下文
                    context.Database.UseTransaction(transaction);
                    //上下文业务操作
                    context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
                    context.SaveChanges();
                }
    
                //提交事务
                transaction.Commit();
            }
            catch (System.Exception)
            {
                // TODO: Handle failure
            }
        }
    }

    4.环境事务

    环境事务内,当发生任何异常时,都会触发事务的回滚。

    我们可以通过在创建TransactionScope作用域时,使用IsolationLevel来设定环境事务的隔离性以及超时时间

    4.1 TransactionOptions

    TimeOut获取或设置该事务的超时时间
    IsolationLevel获取或设置事务的隔离级别

    4.2 TransactionScopeOption

    Required该范围需要一个事务。 如果已经存在环境事务,则使用该环境事务。 否则,在进入范围之前创建新的事务。 这是默认值
    RequiresNew总是为该范围创建新事务。
    Suppress环境事务上下文在创建范围时被取消。 范围中的所有操作都在无环境事务上下文的情况下完成。
    using (var scope = new TransactionScope(
        TransactionScopeOption.Required,
        new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
    {
        using (var connection = new SqlConnection(connectionString))
        {
            connection.Open();
    
            try
            {
                // Run raw ADO.NET command in the transaction
                var command = connection.CreateCommand();
                command.CommandText = "DELETE FROM dbo.Blogs";
                command.ExecuteNonQuery();
    
                // Run an EF Core command in the transaction
                var options = new DbContextOptionsBuilder<BloggingContext>()
                    .UseSqlServer(connection)
                    .Options;
    
                using (var context = new BloggingContext(options))
                {
                    context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
                    context.SaveChanges();
                }
    
                // Commit transaction if all commands succeed, transaction will auto-rollback
                // when disposed if either commands fails
                scope.Complete();
            }
            catch (System.Exception)
            {
                // TODO: Handle failure
            }
        }
    }

    5.显式事务

    using (var transaction = new CommittableTransaction(
        new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
    {
        var connection = new SqlConnection(connectionString);
    
        try
        {
            var options = new DbContextOptionsBuilder<BloggingContext>()
                .UseSqlServer(connection)
                .Options;
    
            using (var context = new BloggingContext(options))
            {
                context.Database.OpenConnection();
                context.Database.EnlistTransaction(transaction);
    
                // Run raw ADO.NET command in the transaction
                var command = connection.CreateCommand();
                command.CommandText = "DELETE FROM dbo.Blogs";
                command.ExecuteNonQuery();
    
                // Run an EF Core command in the transaction
                context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
                context.SaveChanges();
                context.Database.CloseConnection();
            }
    
            // Commit transaction if all commands succeed, transaction will auto-rollback
            // when disposed if either commands fails
            transaction.Commit();
        }
        catch (System.Exception)
        {
            // TODO: Handle failure
        }
    }

    6.嵌套事务

    当嵌套类的TransactionScope的TransactionScopeOption为Required的时候,二者的事务的ID都是同一个,只有当2个TransactionScope都complete的时候才能算真正成功。

    如果把TransactionScopeOption设为RequiresNew,则嵌套的事务块和外层的事务块各自独立,互不影响。

    TransactionScopeOption设为Suppress则为取消当前区块的事务,一般很少使用。

    对于多个不同服务器之间的数据库操作,TransactionScope依赖DTC(Distributed Transaction Coordinator)服务完成事务一致性。

    static void NestedScopes()
            {
                using (var scope = new TransactionScope())
                {
                    Transaction.Current.TransactionCompleted += OnTransactionCompleted;
    
                    Utilities.DisplayTransactionInformation("Ambient TX created",
                          Transaction.Current.TransactionInformation);
    
                    using (var scope2 =
                          new TransactionScope(TransactionScopeOption.RequiresNew))
                    {
                        Transaction.Current.TransactionCompleted += OnTransactionCompleted;
    
                        Utilities.DisplayTransactionInformation(
                               "Inner Transaction Scope",
                               Transaction.Current.TransactionInformation);
    
                        scope2.Complete();
                    }
                    scope.Complete();
                }
    
            }

    五. 相关资料

    5.1 分布式事务,布鲁尔定理,BASE,2PC,TCC,MQ,Saga的资料

    5.2 微软官方文档

    5.3 TransactionScope的使用方法和原理

    声明:本文部分内容摘自上述博客以及文档。

    PS:如果你需要第一时间查阅到我的博客,欢迎访问我的个人主页:https://www.magicalconch.com

  • 相关阅读:
    SpringBoot2.x前后端分离跨域问题及Swagger不能访问
    SpirngBoot2.x整合Swagger2接口文档
    SpringBoot2.x整合Druid数据源
    SpringBoot2.x整合logback 实现自动打印日志
    docker 进入 mysql中的操作
    Intellij Springboot (子模块)访问jsp页面404
    运行rabbitmq 的docker
    mybatis拦截器修改sql重新set后不生效?
    oracle+mybatis如何在新增时返回主键(自增序列)的值?
    oracle+mybatis报“未找到要求的from关键字”错误?
  • 原文地址:https://www.cnblogs.com/magicalconch/p/14457569.html
Copyright © 2011-2022 走看看