精华内容
下载资源
问答
  • 对事务的理解能力
    千次阅读
    2022-03-10 17:27:59

    问题描述

    事务在开发过程大家应该都经常使用,但是事务具体有哪些使用场景?什么时候需要使用事务,什么时候不需要添加事务呢?一个都是查询操作的方法是否需要添加事务?

    最常见的一种回答:
    如果一个方法中,执行了多个insert,update,delete操作就需要添加事务。

    这样的答案,我最多只能给60分,因为可以说只要是个程序员基本都知道,完全不能体现对事务认识的深度。

    事务是什么?

    Transactions are atomic units of work that can be committed or rolled back. When a transaction makes multiple changes to the database, either all the changes succeed when the transaction is committed, or all the changes are undone when the transaction is rolled back.

    事务是由一组SQL语句组成的原子操作单元,其对数据的变更,要么全都执行成功(Committed),要么全都不执行(Rollback)。
    在这里插入图片描述

    事务的特性

    InnoDB实现的数据库事务具有常说的ACID属性,即原子性(atomicity),一致性(consistency)、隔离性(isolation)和持久性(durability)。

    • 原子性:事务被视为不可分割的最小单元,所有操作要么全部执行成功,要么失败回滚(即还原到事务开始前的状态,就像这个事务从来没有执行过一样)
    • 一致性:在成功提交或失败回滚之后以及正在进行的事务期间,数据库始终保持一致的状态。如果正在多个表之间更新相关数据,那么查询将看到所有旧值或所有新值,而不会一部分是新值,一部分是旧值
    • 隔离性:事务处理过程中的中间状态应该对外部不可见,换句话说,事务在进行过程中是隔离的,事务之间不能互相干扰,不能访问到彼此未提交的数据。这种隔离可通过锁机制实现。有经验的用户可以根据实际的业务场景,通过调整事务隔离级别,以提高并发能力
    • 持久性:一旦事务提交,其所做的修改将会永远保存到数据库中。即使系统发生故障,事务执行的结果也不能丢失

    典型场景:

    1、原子性保障——多个insert,update,delete操作

    这个应该是大家最熟悉的一种场景,保证多个insert,update,delete操作要么全都执行成功(Committed),要么全都不执行(Rollback)。

    原子性的特点:

    • 1、针对单事务的控制
    • 2、针对多个insert,update,delete操作

    示例:
    执行方法,添加多个商品。添加事务控制,保障所有商品要么全部添加成功,要么全部添加失败。

        @Transactional(rollbackFor = Exception.class)
        public  void  addList(List list){
            list.forEach(e->{
                  goodsStockMapper.add(e);
             });
        }
    

    2、隔离性保障——幻读、不可重复、脏读

    事务处理过程中的中间状态应该对外部不可见,换句话说,事务在进行过程中是隔离的,事务之间不能互相干扰,不能访问到彼此未提交的数据。

    幻读、不可重复需要在同一个事务中进行多次相同的查询才能体现,真是项目中需要这样操作的场景很少。
    脏读就是读到其他事务没有提交的数据,只要隔离级别不是读未提交(Read Uncommitted)就不会出现。

    所以相比对幻读、不可重复、脏读这些开发过程中基本不会遇到的问题,我们更应该关注事务的隔离性对业务产生的影响
    事务的默认隔离级别可重复读(Repeatable Read)基本满足日常开发90%的场景,一般不建议调整。

    隔离性的特点:
    1、针对多事务间数据可见性的控制。
    2、控制加锁的粒度和加锁、释放锁的时机,提高事务的并发能力。

    示例场景:
    读到其他事务未提交数据,导致超卖。
    在这里插入图片描述

    1、幻读:

    SELECT count(1) FROM books WHERE price < 100;	/* 时间顺序:1,事务: T1 */
    INSERT INTO books(name,price) VALUES ('深入理解Java虚拟机',90); COMMIT;	/* 时间顺序:2,事务: T2 */
    SELECT count(1) FROM books WHERE price < 100;	/* 时间顺序:3,事务: T1 */
    

    可串行化(Serializable)会对事务所有读、写的数据全都加上读锁、写锁和范围锁,所以由于T1事务对价格小于100的范围内的数据都加读锁、写锁和范围锁,所以T2不能插入价格为90的数据,所以不存在幻读的情况。
    其他隔离级别下都会出现幻读。

    2、不可重复度

    SELECT * FROM books WHERE id = 1;                   /* 时间顺序:1,事务: T1 */
    UPDATE books SET price = 110 WHERE id = 1; COMMIT;	/* 时间顺序:2,事务: T2 */
    SELECT * FROM books WHERE id = 1; COMMIT;   	    /* 时间顺序:3,事务: T1 */
    

    假如隔离级别是可重复读的话,由于数据已被事务 T1 施加了读锁且读取后不会马上释放,所以事务 T2 无法获取到写锁,更新就会被阻塞,直至事务 T1 被提交或回滚后才能提交

    读已提交对事务涉及的数据加的写锁会一直持续到事务结束,但加的读锁在查询操作完成后就马上会释放。
    读已提交的隔离级别缺乏贯穿整个事务周期的读锁,无法禁止读取过的数据发生变化,此时事务 T2 中的更新语句可以马上提交成功,这也是一个事务受到其他事务影响,隔离性被破坏的表现。

    事实上由于Mysql的MVCC机制,可重复读(Repeatable Read)和读已提交(Read Committed)在读的时候都不会加锁。如果读取的行正在执行delete或者update操作,这时读操作不会因此去等待行上锁的释放。相反的,InnoDB存储引擎会去读取行的一个快照数据。实现了对读的非阻塞,读不加锁,读写不冲突

    3、读未提交
    读未提交(Read Uncommitted):对事务涉及的数据只加写锁,会一直持续到事务结束,但完全不加读锁。

    SELECT * FROM books WHERE id = 1;   	/* 时间顺序:1,事务: T1 */
    /* 注意没有COMMIT */
    UPDATE books SET price = 90 WHERE id = 1;	/* 时间顺序:2,事务: T2 */
    /* 这条SELECT模拟购书的操作的逻辑 */
    SELECT * FROM books WHERE id = 1;		/* 时间顺序:3,事务: T1 */
    ROLLBACK;	
    

    该级别下,读取数据前不用先获取读锁。由于T1读取数据时不需要去加读锁,所以T2修改数据后,不用等在commit提交释放写锁,T1立刻就能读取到修改后的数据。

    读未提交在数据上完全不加读锁,这反而令它能读到其他事务加了写锁的数据,即上述事务 T1 中两条查询语句得到的结果并不相同。如果你不能理解这句话中的“反而”二字,请再重读一次写锁的定义:写锁禁止其他事务施加读锁,而不是禁止事务读取数据,如果事务 T1 读取数据并不需要去加读锁的话,就会导致事务 T2 未提交的数据也马上就能被事务 T1 所读到。这同样是一个事务受到其他事务影响,隔离性被破坏的表现。

    3、一致性保障——针对多个表的查询统计

    很多同学一直认为,一个方法中如果都是查询请求,就不需要添加事务控制。那么真的是这样吗?

    假设现在有3个表A,B,C,由于业务请求量非常高,导致3个表一直有新的数据不停的写入。
    现在要求分别对3个表中的数据进行聚合统计,然后进行指标计算。

    大致逻辑:

          select A指标  from 表A;     //步骤1
    
          select B指标  from 表B;    //步骤2
    
          select C指标  from 表C;    //步骤3
    
          汇总指标  =  A指标 + B指标 + C指标;   //步骤4
    

    如果按照这样去统计,当查询完A指标后,由于业务在正常进行,表B和表C仍然有数据写入,所以最后会导致查询的A,B,C3个指标,并不是同一时刻的,这样的汇总指标也就没有了参考意义。
    在这里插入图片描述
    这个时候就需要对统计的方法添加事务,保证数据的一致性。

    一致性:在成功提交或失败回滚之后以及正在进行的事务期间,数据库始终保持一致的状态。如果正在多个表之间更新相关数据,那么查询将看到所有旧值或所有新值,而不会一部分是新值,一部分是旧值。

    @Transactional(rollbackFor = Exception.class,isolation = Isolation.REPEATABLE_READ,readOnly = true)
    public  int  count(){
           select A指标  from 表A; 
           select B指标  from 表B; 
           select C指标  from 表C;  
          汇总指标  =  A指标 + B指标 + C指标; 
    }
    

    说明:
    对汇总统计的方法添加事务控制,且指定事务的隔离级别为可重复读Isolation.REPEATABLE_READ,并设置只读属性readOnly对查询进行优先。

    可重复读:总是读取 CREATE_VERSION 小于或等于当前事务 ID 的记录

    由于启动了可重复读事务控制,所以当在统计时间点T1发请统计请求时,针对A,B,C3个表的查询总是只能读取 CREATE_VERSION 小于或等于当前事务 ID 的记录。这样在统计时间点T1后面新增的数据就不会影响我们的查询统计,通过事务将3个表的统计查询拉齐到了同一时间线上

    4、悲观锁

    如果一个方法中就只有一个简单的查询语句,是否需要添加事务控制?
    还真不能简单的say no。

    场景:
    利用数据库悲观锁实现分布式锁。

        @Transactional(rollbackFor = Exception.class)
        public  void  sumGoods(Integer goodsId, Integer num)  {
            //1、利用for update加悲观锁,也就是写锁,由于写锁具有排他性,保证分布式环境下也可以串行化执行
            GoodsStock  goodsStock  = goodsStockMapper.getStockForUpdate(goodsId);
            //2、计算
            int sum =  redisUtil.get(goodsId) + num;
    
            redisUtil.set(goodsId,sum)
        }
    

    说明:
    悲观锁一定要配合事务来使用,这样才能保证整个事务方法执行完毕后,自动释放锁。

    总结

    本文主要是对事务的使用场景进行来说明。
    1、典型场景,一个方法中包含多个insert,update,delete操作通过添加事务保证原子性,要么全部成功,要么全部失败。
    2、还可以通过事务的隔离级别,控制多事务间数据的可见性。
    3、针对多个表的查询统计,可以通过添加事务控制将统计时间拉起到同一时间节点,保证数据的一致性。
    4、悲观锁必须配合事务使用。

    总的来说,事务的使用场景是对事务特性ACID更深层次的认识和运用的一些解读。

    更多相关内容
  • 数据库事务一致性的理解

    千次阅读 2022-01-25 12:00:48
    一致性的定义 百度百科-一致性: 一致性就是数据保持一致,在分布式系统中,可以理解...我一致性的理解 “一致”是指数据库中的数据是正确的,不存在矛盾。事务的一致性是指事务执行前后,数据都是正确的,不存在

    一致性的定义

    • 百度百科-一致性:

    一致性就是数据保持一致,在分布式系统中,可以理解为多个节点中数据的值是一致的。

    • 百度百科-事务一致性:

    一个或多个事务执行后,原来一致的数据和数据库仍然是一致的。它主要涉及事务的原子性。

    • 维基百科-一致性(数据库)

    一致性是数据库系统的一项要求:任何数据库事务修改数据必须满足定义好的规则,包括数据完整性(约束)、级联回滚、触发器等。

    我对一致性的理解

    “一致”是指数据库中的数据是正确的,不存在矛盾。事务的一致性是指事务执行前后,数据都是正确的,不存在矛盾。如果执行后数据是矛盾的,事务就会回滚到执行前的状态(执行前是一致的)。


    满足一致性的例子

    • 学生表中的学号是唯一的。
    • 账户的余额减少了,账单中要有对应的扣款记录,且减少的金额和账单的扣款金额一致。
    • 一篇文章浏览量为100次,则浏览记录表有该文章的100条浏览记录。
    • 发布文章的用户ID是100,则用户表中存在ID为100的用户。

    不满足一致性的例子

    • 学生表中有重复的学号。
    • 转账成功了,但是付款的人余额没扣,或者收款的人余额没有增加。
    • 付款多次,只扣款1次(或者付款1次,扣款多次)。
    • 发布文章的用户ID是100,但用户表中没有ID=100的用户。
    • 文章的发布时间是空的。
    • 用户的年龄是负数。
    • 用户的年龄是几个字母。

    数据库如何实现一致性?

    • 唯一索引

    给学号添加唯一索引,创建学生信息时,如果已存在相同学号,则创建失败。

    • 外键约束

    给文章表的用户ID创建外键,创建文章时,如果不存在对应的用户,则创建失败;删除用户时,如果文章表有该用户的文章,则无法删除用户,或者将用户与文章一起删除。

    • 触发器

    插入文章的浏览记录时,使用触发器去更新对应文章的浏览量(保证每增加一条浏览记录,对应文章浏览量+1)。

    • 指定数据类型

    设置年龄的类型为非负整数,则年龄为负数、字母时保存失败。

    • 设置默认值

    如果没有指定文章的发布时间,则默认以文章记录的插入时间作为发布时间。

    • 设置字段不能为空

    设置文章的发布时间NOT NULL,没指定发布时间,或发布时间为NULL时,文章创建失败。

    • 事务的原子性
      事务的原子性是指同一个事务中的操作,要么都成功,要么都失败。以A向B转账100元转账为例,需要执行以下2个操作:

    1.将A的余额减少100元;
    2.将B的余额增加100元。

    这两个操作要么都成功,要么都失败,否则最后账目就会对不上(破坏一致性)。事务的原子性可以保证以上两个操作同时成功,或者同时失败。

    • 事务的隔离性

    在并发的情况下,只靠事务的原子性并不能保证一致性。举个例子,A有100元,A同时向B发起两笔转账请求,转账金额分别是99元和1元。
    在这里插入图片描述
    两笔转账都执行成功了,理论上A的余额为0,但实际上A的余额被请求2修改为99元,数据的一致性被破坏了!

    再举一个例子,A同时发起两笔转账,其中一笔因为某些原因操作失败,事务回滚,而另外一笔转账执行成功。执行时间线如下:

    在这里插入图片描述
    在这个场景下,A实际转账成功了1元,但是A的余额最终为0,数据的一致性又被破坏了!

    事务的隔离性可以在多个事务并发执行的情况下,通过加锁等方式,保证数据的一致性。

    什么是事务?

    事务就是「一组原子性的SQL查询」,或者说一个独立的工作单元。如果数据库引擎能够成功地对数据库应用该组查询的全部语句,那么就执行该组查询。如果其中有任何一条语句因为崩溃或其他原因无法执行,那么所有的语句都不会执行。也就是说,事务内的语句,要么全部执行成功,要么全部执行失败

    事务控制语法知道吗?

    BEGIN 或 START TRANSACTION 显式地开启一个事务;
    COMMIT / COMMIT WORK二者是等价的。提交事务,并使已对数据库进行的所有修改成为永久性的;
    ROLLBACK / ROLLBACK WORK。回滚会结束用户的事务,并撤销正在进行的所有未提交的修改;
    SAVEPOINT identifier 在事务中创建一个保存点,一个事务中可以有多个 SAVEPOINT;
    RELEASE SAVEPOINT identifier 删除一个事务的保存点;
    ROLLBACK TO identifier 把事务回滚到标记点;
    SET TRANSACTION 用来设置事务的隔离级别。InnoDB 存储引擎提供事务的隔离级别有READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ 和 SERIALIZABLE
    
    START TRANSACTION;
    SELECT balance FROM CMBC WHERE username='lemon';
    UPDATE CMBC SET balance = balance - 1000000.00 WHERE username = 'lemon';
    UPDATE ICBC SET balance = balance + 1000000.00 WHERE username = 'lemon';
    COMMIT;
    
    • 原子性(atomicity)

    一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作。

    • 一致性(consistency)

    数据库总是从一个一致性的状态转换到另外一个一致性的状态。在前面的例子中,一致性确保了,即使在执行第三、四条语句之间时系统崩溃,CMBC账户中也不会损失100万,因为事务最终没有提交,所以事务中所做的修改也不会保存到数据库中。

    • 隔离性(isolation)

    通常来说,一个事务所做的修改在最终提交以前,对其他事务是不可见的。在前面的例子中,当执行完第三条语句、第四条语句还未开始时,此时如果有其他人也准备给lemon的CMBC账户存钱,那他看到的CMBC账户里还是有100万的。

    • 持久性(durability)

    一旦事务提交,则其所做的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢失。持久性是个有点模糊的概念,因为实际上持久性也分很多不同的级别。有些持久性策略能够提供非常强的安全保障,而有些则未必。而且「不可能有能做到100%的持久性保证的策略」否则还需要备份做什么。

    什么是脏读、不可重复读、幻读?

    脏读

    在事务A修改数据之后提交数据之前,这时另一个事务B来读取数据,如果不加控制,事务B读取到A修改过数据,之后A又对数据做了修改再提交,则B读到的数据是脏数据,此过程称为脏读Dirty Read。

    在这里插入图片描述
    不可重复读

    一个事务内在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了变更、或者某些记录已经被删除了。
    在这里插入图片描述
    幻读
    事务A在按查询条件读取某个范围的记录时,事务B又在该范围内插入了新的满足条件的记录,当事务A再次按条件查询记录时,会产生新的满足条件的记录(幻行 Phantom Row)
    在这里插入图片描述

    不可重复读与幻读有什么区别?

    • 不可重复读的重点是修改:
      在同一事务中,同样的条件,第一次读的数据和第二次读的「数据不一样」。(因为中间有其他事务提交了修改)
    • 幻读的重点在于新增或者删除:
      在同一事务中,同样的条件,第一次和第二次读出来的「记录数不一样」。(因为中间有其他事务提交了插入/删除

    SQL的四个隔离级别知道吗?具体是什么解决了什么问题说说看

    SQL实现了四个标准的隔离级别,每一种级别都规定了一个事务中所做的修改,哪些在事务内和事务间是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。
    在这里插入图片描述
    各个隔离级别可以不同程度的解决脏读、不可重复读、幻读。隔离级别各有所长,没有完美的解决方案,脱离业务场景谈具体实施都是耍流氓。
    在这里插入图片描述
    MySQL中哪些存储引擎支持事务?

    MySQL中InnoDB和NDB Cluster存储引擎提供了事务处理能力,以及其他支持事务的第三引擎

    什么是自动提交?

    MySQL默认采用自动提交AUTOCOMMIT模式。也就是说,如果不是显式地开始一个事务,则每个查询都被当作一个事务执行提交操作。

    对于MyISAM或者内存表这些事务型的表,修改AUTOCOMMIT不会有任何影响。对这类表来说,没有COMMIT或者ROLLBACK的概念,也可以说是相当于一直处于AUTOCOMMIT启用的模式。

    在事务中可以混合使用存储引擎吗?

    尽量不要再同一个事务中使用多种存储引擎,MySQL服务器层不管理事务,事务是由下层的存储引擎实现的。

    如果在事务中混合使用了事务型和非事务型的表(例如InnoDB和MyISAM表),在正常提交的情况下不会有什么问题。

    但如果该事务需要回滚,非事务型的表上的变更就无法撤销,这会导致数据库处于不一致的状态,这种情况很难修复,事务的最终结果将无法确定。所以,为每张表选择合适的存储引擎非常重要。

    MySQL存储引擎类型有哪些?

    最常用的存储引擎是InnoDB引擎和MyISAM存储引擎,InnoDB是MySQL的默认事务引擎。

    InnoDB存储引擎的特点和应用场景?

    InnoDB是MySQL的默认「事务引擎」,被设置用来处理大量短期(short-lived)事务,短期事务大部分情况是正常提交的,很少会回滚。

    特点

    采用多版本并发控制(MVCC,MultiVersion Concurrency
    Control)来支持高并发。并且实现了四个标准的隔离级别,通过间隙锁next-key locking策略防止幻读的出现。

    引擎的表基于聚簇索引建立,聚簇索引对主键查询有很高的性能。不过它的二级索引secondary
    index非主键索引中必须包含主键列,所以如果主键列很大的话,其他的所有索引都会很大。因此,若表上的索引较多的话,主键应当尽可能的小。另外InnoDB的存储格式是平台独立。

    InnoDB做了很多优化,比如:磁盘读取数据方式采用的可预测性预读、自动在内存中创建hash索引以加速读操作的自适应哈希索引(adaptive
    hash index),以及能够加速插入操作的插入缓冲区(insert buffer)等。

    InnoDB通过一些机制和工具支持真正的热备份,MySQL的其他存储引擎不支持热备份,要获取一致性视图需要停止对所有表的写入,而在读写混合场景中,停止写入可能也意味着停止读取。

    MyISAM存储引擎的特点和应用场景?

    MyISAM是MySQL 5.1及之前的版本的默认的存储引擎。MyISAM提供了大量的特性,包括全文索引、压缩、空间函数(GIS)等,但MyISAM不「支持事务和行级锁」,对于只读数据,或者表比较小、可以容忍修复操作,依然可以使用它。

    InnoDB与MyISAM对比
    在这里插入图片描述
    感谢
    https://blog.51cto.com/u_14163302/2551117
    https://www.zhihu.com/question/31346392/answer/1707203839

    展开全文
  • Mysql的分布式事务原理理解

    万次阅读 多人点赞 2022-07-06 07:06:58
    本文主要讲解:Mysql的分布式事务原理及中间会遇见的问题。
    本文主要讲解:Mysql的分布式事务原理及中间会遇见的问题

    目录

    一、事务的特性与类型

    二、本地事务

    三、并发事务带来的问题

    四、事务隔离级别

    五、各种类型的锁

    六、死锁问题

    七、MVCC机制

    八、Redo Log基本原理

    九、Redo Log刷盘规则

    十、Redo Log写入机制与LSN机制

    十一、Undo Log基本概念与存储方式

    十二、Undo Log 基本原理

    十三、Undo Log 实现MVCC机制


    一、事务的特性与类型

    事务的特性(ACID):
    1)原子性
       构成事务的操作要么同时成功,要么同时失败
    2)一致性
       事务执行之前和事务执行之后,数据的一致性(如:卖票系统,卖完一张后,费用扣除、余票减一。“一致”是指数据库中的数据是正确的,不存在矛盾)
    3)隔离性
       两个事务之间互不干扰。一个事务在执行过程中,不能看见其他事务运行的中间过程。
    4)持久性
       事务提交后,此事务对数据的更改操作会被持久化到数据库中,并且不会被回滚

    事务的类型:
    1)扁平事务
       最常见的,在这期间的所有操作要么全部提交成功,要么全部提交失败(回滚)
    2)带有保存点的扁平事务
       在扁平事务的内部设置了保存点,回滚时,可以回滚到该位置
    3)链式事务
       链式事务是在带有保存点的事务的基础上,自动将当前事务的上下文隐式的传递给下一个事务。上一个事务的提交操作和下一个事务的开始操作具备原子性。
    4)嵌套事务
       多个事务嵌套,共同完成一个任务,最外层的顶层事务控制着所有的内部事务,内部事务提交后,整体事务并不会提交,只有当最外层的顶层事务提交后,整个事务才算提交完成。
    5)分布式事务
       事务的参与者、事务所在的服务器、涉及的资源服务器以及事务管理器等分别位于不同的分布式系统的不同服务或数据库的节点

    返回顶部目录


    二、本地事务

    基于关系型数据库的事务也可以称为本地事务或者传统事务。

    本地事务的特征:
    1)一次事务过程只能连接一个支持事务的数据库
    2)执行结果满足ACID
    3)使用数据库本身的锁执行事务

    本地事务的优点:
    1)支持严格的ACID属性
    2)事务可靠
    3)执行效率高
    4)事务的状态可以只在数据库中维护
    5)编程模型简单

    本地事务的缺点:
    1)不具备分布式事务的处理能力
    2)不能用于多个事务性数据库

    在这里插入图片描述

    返回顶部目录


    三、并发事务带来的问题

    更新丢失(或脏写)
    脏读
    不可重复度
    幻读

    1)更新丢失(或脏写):
       对于同一行数据来说,一个事务对该行的数据的操作会覆盖其他事务对该行数据的更新操作。本质上是写操作的冲突,解决办法是让每个事务按照串行的方式执行,按照一定顺序依次进行写操作。
       例子:张三账户余额100元,事务A对张三账户增加300元,事务B对张三账户增加400元。此时事务A与事务B同时读取到张三的余额为100,那么最终的余额取决于最后提交的那个事务+100元。

    2)脏读:
       一个事务读取了另外一个事务未提交的数据。
       本质上是读写冲突,解决脏读的方法就是先写后读,也就是写完后读。
       例子: 事务A对张三账户执行转账100元,还未提交,事务B读取到了结果,此时由于网络等原因,事务A执行了回滚,事务B读到的就是脏读。

    3)不可重复读:
       同一个事务,使用相同的查询语句,在不同时刻读取的结果数据不一致。
       本质上是读写操作的冲突,解决办法是先读后写。
       例子:事务A对张三的账户准备扣款,执行事务时,发现还有100元。但此时事务B对张三账户扣款了100。事务A对张三账户再读时,就没有100了,两次读的结果不一样。

    4)幻读:
       一个事务两次读取一个范围的数据记录,两次读取的范围结果不同。
       本质上是读写操作的冲突,解决办法就是先读后写。

    5)不可重复读与幻读的区别:
       不可重复读重点在于更新与删除,而幻读重点在于插入操作。
       使用锁机制实现事务隔离级别时,不可重复读与幻读不同。(在可重复读隔离级别中,对读的数据会加锁,其他事务无法修改这些数据,而这种方法无法对新加入的数据加锁)
       幻读无法通过行级锁来避免。(需要使用串行化的事务隔离级别,但这么做会大大的降低数据库并发能力)

       不可重复读和幻读最大的区别,就在于如何通过锁机制来解决它们产生的问题。(除了使用悲观锁来解决这两个问题,还会使用乐观锁来解决。Mysql为了提高整体性能,使用了MVCC机制:多版本并发控制,来解决不可重复读与幻读的问题。MVCC机制分为快照读与幻读,其中快照读就可以解决幻读)

    返回顶部目录


    四、事务隔离级别

    读未提交
    读已提交
    可重复读
    串行化

    事务隔离级别的区别(Mysql的InnoDB存储引擎默认是可重复读,Oracle默认是读已提交):

    在这里插入图片描述

    修改事务隔离级别的设置点击我

    返回顶部目录


    五、各种类型的锁

    本质上,锁是计算机中对多个进程或者多个线程访问某一个资源进行协调的一种机制。在Mysql中使用锁实现了事务隔离级别
    在这里插入图片描述

    悲观锁与乐观锁:
       对数据库中数据的读写持悲观态度,在整个数据处理中,将相应的数据锁定。在数据库中,悲观锁的实现需要依赖数据库提供的锁机制实现,性能不友好,特别是在长事务时。
       对于数据库中的数据的读写持乐观态度。在整个数据处理中,大多数情况下是通过数据版本(Version)记录机制来实现的。

    读锁与写锁:
       读锁,又称为共享锁或S锁(Share锁),针对同一份数据,可以加多个读锁而互不影响。但不能增加写锁了。
       写锁,又称为排它锁或X锁(Exclusive锁),如果当前写锁未释放前,它会阻塞其他的写锁和读锁。
      读锁共享,写锁排他。

    表锁、行锁、页面锁:
      表锁就是在整个数据表上加锁。在Mysql中有:1)表共享锁、2)表独占写锁
       行锁是在数据行上进行的加锁和释放锁,InnoDB有两种类型的行锁:1)共享锁(可以加多个共享锁)、2)排他锁(不允许再加锁了),锁只要加到索引上,如果对非索引字段设置条件更新,行锁可能会变成表锁。InnoDB的行锁是针对索引加锁,不是针对记录,并且加锁的索引不能失效,否则行锁可能会变成表锁。
       在页面级别对数据进行加锁和释放锁。锁粒度介于表锁与行锁之间,其并发度一般。

    间隙锁与临键锁:
       间隙锁是对两个值之间的空隙加锁。Mysql的默认隔离级别是可重复读,在该级别下可能会出现幻读的问题,而间隙锁在某种程度上可以解决幻读的问题。
       临键锁又称为Next-Key锁,是行锁与间隙锁的组合。

      

    返回顶部目录


    六、死锁问题

    死锁的必要条件(都存在才发生死锁):
    1)互斥条件
    2)不可剥夺条件
    3)请求与保持
    4)循环等待条件

    处理死锁的方法
    1)预防死锁:破坏4个条件之一
    2)避免死锁:使用某种策略放在该事件发生
    3)检测死锁:允许死锁发生,但发生后
    4)解除死锁

    返回顶部目录


    七、MVCC机制

    MVCC(多版本并发控制)主要解决多事务并发控制问题,也就是保证事务的隔离性。

    MVCC工作原理

    • 读已提交MVCC的工作原理
      1)当前事务自身产生的数据。
      2)当前事务开启之前,其他已经提交的事务。
      在这里插入图片描述
      在t1时刻事务E能读取到事务A、B、C已经提交的事务及事务D上一个提交的版本。

    • 可重复读MVCC的工作原理
      1)在系统中记录下t1时刻启动事务E时的活动事务列表,在事务E执行的过程中,一直使用在t1时刻记录的活动事务列表即可,这个一直使用的活动事务列表被称为快照。
      在这里插入图片描述
      也就是说事务E,在t1与t2时刻读取到的事务是一样的。

    读已提交隔离级别下每个SQL语句都有一个自己的快照,它们看到的数据库中的数据是不同的。而在可重复读隔离级别下,所有的SQL语句使用同一个快照,能够看到数据库中同样的数据。

    返回顶部目录


    八、Redo Log基本原理

    事务的隔离性是由锁实现的,Mysql中原子性和持久性是由redolog实现的,一致性是由undolog实现的。

    redo log基本概念:

    • 往往用来恢复提交后的物理数据页,且只能恢复到最后一次提交的位置
    • redo log 也被称为重做日志,它是在InnoDB存储引擎层产生的,用来保证事务的原子性和持久性。
    • redo log 通常包含两部分:一部分是内存中的日志缓冲(redolog buffer)。另一部分是存放在磁盘上的重做日志文件(redolog file)。

    redo log 基本原理

    • 当创建一个订单时,会写入到redo log buffer中,redo log buffer会根据一定的规则刷新到redo log文件中去。当mysql发生故障重启时,会根据redo log中的数据对订单表中的数据恢复(先恢复到order.idb文件中,再加载到order中)
      在这里插入图片描述

    返回顶部目录


    九、Redo Log刷盘规则

    1、Redo Log Buffer写日志到Redo Log File

    在mysql innodb存储引擎中,通过提交事务时强制执行写日志操作,这样一个机制来实现事务的持久化。innodb存储引擎为了保证在事务提交时,将日志提交到事务日志文件中,默认在每次将redo log buffer中的日志写入日志文件的过程中,就会调用一次操作系统的fsync()的操作

    写入时经过了用户空间与内核空间(如果使用O_DIRECT标志位,可以不经过内核空间)。
    在这里插入图片描述

    2、Redo Log刷盘规则

    • 开启事务,发出Commit(提交事务)指令后是否刷日志由变量innodb_flush_log_at_trx_commit决定。
    • 每秒刷新一次。刷日志的频率由变量innodb_flush_log_at_timeout参数的值决定,默认是1秒。需要注意的是,刷日志的频率和是否执行了commit操作无关。
    • 当Log Buffer中已经使用的内存超过一半时,也会触发刷盘操作。
    • 当事务中存在checkpoint(检查点)时,checkpoint在一定程度上代表了刷到磁盘时日志所处的LSN的位置。其中,LSN的全称为Log Sequence Number,表示日志的逻辑序列号。

    3、innodb_flush_log_at_trx_commit 变量的刷盘规则

    innodb_flush_log_at_trx_commit 不同的值(默认为1),对应不同的规则
    在这里插入图片描述

    返回顶部目录


    十、Redo Log写入机制与LSN机制

    1、Redo log 写入机制

    • redo log主要是物理日志,也就是物理上的修改操作能够恢复提交事务修改的页操作,redo log的文件是以顺序循环的方式写入的,一个文件写满会写入另外一个文件,最后一个文件写满后,会覆盖写入到第一个文件。

    • 当Write Pos追上Check Point时,表示Redo log已满,需要擦除掉前面的数据

    在这里插入图片描述

    2、Redo log LSN机制

    一般可以从LSN(Log Sequence Number 表示日志的逻辑序号列)中获取到如下信息:

    • Redo Log写入的总量
    • checkpoint位置,也就是检查点的具体位置
    • 数据页版本相关的信息
      如果页中的LSN值小于Redo log中的LSN值,则表示丢失了一部分数据,此时,可以通过Redo Log的记录来恢复到数据。否则,不需要恢复数据。
      可以在通过如下命令查看LSN值:
    show engine innodb status
    

    重点观察如下参数:

    Log sequence number:当前内存缓冲区的redo log 的LSN值。
    Log flushed up to:表示的是刷新到磁盘上的redo log文件的LSN。
    Pages flushed up to:已经刷新到磁盘数据页上的LSN。
    Last checkpoint at:上一次检查点所在位置的LSN。
    

    3、Redo log 相关参数

    查看Redo log相关的参数

    show variables like '%innodb_log%'
    

    其中重要的参数有:

    log buffer 的大小:innodb_log_buffer-size
    事务日志的大小:innodb_log_file_size
    事务日志组中的事务日志文件的个数(默认为2):innodb_log_files_group=2
    事务日志组所在的目录:innodb_log_group_home_dir
    

    返回顶部目录


    十一、Undo Log基本概念与存储方式

    Mysql的一致性是由undolog实现的。

    Undo Log基本概念:

    • Undo Log在MySQL事务的实现中,主要起到两方面的作用:回滚事务和多版本并发事务,也就是常说的MVCC机制。
    • Undo Log与Redo Log不同,Undo Log记录的是逻辑日志。可以这样理解:当数据库执行一条insert语句时,Undo Log会记录一条对应的delete语句;当数据库执行一条delete语句时,Undo Log会记录一条对应的insert语句;当数据库执行一条update语句时,Undo Log会记录一条相反的update语句。
    • Undo Log的完整性和可靠性需要 Redo log来保证,所以数据库崩溃需要先做Redo log 数据恢复,然后做undo log回滚,

    Undo Log存储方式:

    • 在MySQL中,InnoDB存储引擎对于Undo Log的存储采用段的方式进行管理
    • 在MySQL中,InnoDB存储引擎对于Undo Log的存储采用段的方式进行管理
      Undo Log默认存放在共享数据表空间中,默认就是ibdata1文件中。如果开启了innodb_file _per_table参数,就会将Undo Log存放在每张数据表的.ibd文件中。

    返回顶部目录


    十二、Undo Log 基本原理

    1、Undo Log 持久化原理

    • 写入时经过了用户空间与内核空间(如果使用O_DIRECT标志位,可以不经过内核空间)。

    在这里插入图片描述

    2、案例

    • 当mysql提交事务之前,innodb会将数据表中修改前的数据保存到undo log buffer中,undo log buffer中的数据会持久化到磁盘上的undo log file中
    • 当数据库发生故障、重启或者事务回滚时,innodb存储引擎就会读取undo log中的数据,将事务还未提交的数据回滚到最初的状态,同时系统根据需要查询加载订单表中的数据(也就是加载order.idb文件中的数据),也可以向订单表中写入数据,也就是将数据持久化到order.ibd文件中。

    在这里插入图片描述

    返回顶部目录


    十三、Undo Log 实现MVCC机制

    Undo log的字面意思是撤销操作的日志,指的是使mysql中的数据回到某个状态。在事务开启中(Undo Log是InnerDB独有的),mysql会将待修改的记录保存到Undo Log中。如果数据库崩溃或者事务需要回滚时,mysql可以通过利用Undo log日志,将数据库中的事务回滚到之前的状态。mysql新增、修改、删除数据时,在事务开启前,就会将信息写入Undo Log中,事务提交时,并不会立刻删除Undo Log,InnoDB存储引擎会将事务对应的Undo Log放入待删除列表中,之后会通过后台的purge thread对待删除的列表进行删除操作处理。

    注意的是Undo log是一种逻辑日志,记录的是一种逻辑过程。比如mysql执行delete操作,Undo log就会记录一个insert操作;mysql执行一个insert操作,Undo Log就会记录一个delete操作;mysqlk执行update操作,Undo Log记录一个相反的update操作。

    Undo Log以段的方式来管理记录日志信息,在InnoDB存储引擎的数据文件中,包含了一种rollback segment的回滚段,内部包含了1024个undo log segment 。

    Undo Log实现了事务的原子性多版本并发控制(MVCC)。原子性——Mysql出现了错误、或者手动执行了事务的回滚,Undo Log会将数据库中的数据恢复到之前的状态。下图是Undo log实现多版本并发控制,事务A未提交之前,undo log保存了未提交之前的版本,事务B读取的是之前的版本信息。
    在这里插入图片描述

    返回顶部目录
    本文参考了冰河老师的《深入理解分布式事务:原理与实战》及丁奇老师的《Mysql实战45讲》。如有侵权,请联系删除相关内容。

    展开全文
  • 导读概述随着业务的快速发展,业务复杂度越来越高,大部分互联网公司几乎都会从单体走向分布式,特别是转向微服务架构,随之而来就必然遇到分布事务这个难题。本文主要介绍一下Seat...

    baecd86797b42359045fb0d5ddfdc6d9.png

    39952411d7a7aef796c45ed07079f97d.png

    导读概述

        随着业务的快速发展,业务复杂度越来越高,大部分互联网公司几乎都会从单体走向分布式,特别是转向微服务架构,随之而来就必然遇到分布事务这个难题。

        本文主要介绍一下Seata是如何保证数据一致性的,将从以下几个方面介绍一下分布式实现方案

    1、介绍一下分布式理论(此部分如果已经掌握了可以跳过,看下面部分分享)

    2、什么是Seata

    3、Seata AT模式下如何解决分布式事务,应用场景

    4、Seata TCC模式下如何解决分布式事务、应用场景、变种场景使用

    5、Seata 下的Saga模式

        本文以下单占库存案例为主线分享Seata分布式解决方案。

    分布式理论

    什么是事务?

          指的就是一个操作单元,在这个操作单元中的所有操作最终要保持一致的行为,要么所有操作都成功,要么所有的操作都被撤销。

    使用事务目的是什么

    说明白了就是保证数据的一致性。

    什么是本地事务呢

        我们常常指的是关系型数据库的事务,关系型数据库事务四大特性:

    • Atomicity(原子性) :要么都成功,要么都失败。

    • Consistency(一致性): 在事务开始之前和事务结束以后,数据库的完整性没有被破坏,完整性包括外键约束、应用定义的等约束不会被破坏。

    • Isolation(隔离性):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。

    • Durability(持久性):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

    4b1f5609ebb707338f23958ab6bffa5f.png

    优点:可以保证单机数据库层面的数据一致性。

    为什么要使用分布式事务

    分布式事务在分布式环境下,为满足可用性、性能与降级服务的需要,降低一致性与隔离性的要求,一方面遵循 BASE 理论

    • 基本业务可用性(Basic Availability)

    • 柔性状态(Soft state)

    • 最终一致性(Eventual consistency)

    同样的,分布式事务也部分遵循 ACID 规范:

    • 原子性:严格遵循

    • 一致性:事务完成后的一致性严格遵循;事务中的一致性可适当放宽

    • 隔离性:并行事务间不可影响;事务中间结果可见性允许安全放宽

    • 持久性:严格遵循

    需求是这样的:

           随着业务发展在微服务架构下,【订单服务】和【库存服务】在两个业务系统里,同时分别调用对应的订单DB和库存DB,要求保证:要么库存和订单下单一起成功,如果库存占用和订单下单有一方失败要么就要回滚,保证数据的一致性。

        此时场景数据库级别的事务就有些捉襟见肘了,此时分布式事务就派上用场了。

    e7dd1cd72e082a0035fb8547ebefb8c4.png

    分布式事务常见解决手段有哪些呢?

            两阶段提交2PC、三阶段提交3PC、TCC、SAGA、本地消息表、基于可靠消息保证最终一致、最大努力通知等方案。

            由于分布式事务方案,无法做到完全的ACID的保证,没有一种完美的方案,能够解决掉所有业务问题。因此在实际应用中,会根据业务的不同特性,选择最适合的分布式事务方案。

    什么是分布式事务呢?

    1、分布式事务

    • 首先应用于分布式系统场景,分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。

    • 例如在电商系统中,下单接口通常会扣减库存、添加优惠券、生成订单 id, 而订单服务与库存、添加优惠券、下单接口的成功与否,不仅取决于本地的 db 操作,而且依赖第三方系统的结果,这时候分布式事务就保证这些操作要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同数据库的数据一致性。

    2、常见使用场景:

        如用户注册送积分事务、创建订单减库存事务,银行转账事务等都可以应用分布式事务,分布式事务就是为了保证在分布式场景下,数据操作的正确执行。

    提到分布式场景就绕不开CAP理论,三者只能满足两点,来解决分布式场景的问题,那么接下来先了解一下什么是CAP。

    CAP原则

        CAP 原则又称 CAP 定理,又被叫作布鲁尔定理,指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。

    b13f9b1d6942d6ed6962d15f8a88b0cf.png

        CAP 原则的精髓就是要么AP,要么CP,要么AC,但是不存在CAP。如果在某个分布式系统中数据无副本,那么系统必然满足强一致性条件,因为只有独一数据,不会出现数据不一致的情况,此时C和P两要素具备,但是如果系统发生了网络分区状况或者宕机,必然导致某些数据不可以访问,此时可用性条件就不能被满足,即在此情况下获得了CP系统,但是CAP不可同时满足。

        即要保证数据一致还要保证可用,鱼和熊掌不可兼得,那么就要取舍,于是产生以下几种一致性策略。

    强一致性、弱一致性、最终一致性

    1.强一致性

        任何一次读都能读到某个数据的最近一次写的数据。系统中的所有进程,看到的操作顺序,都和全局时钟下的顺序一致。简言之,在任意时刻,所有节点中的数据是一样的。如:2PC、3PC、XA都属于强一致性方案。

    2.弱一致性

        数据更新后,如果能容忍后续的访问只能访问到部分或者全部访问不到,则是弱一致性。

    3.最终一致性

        不保证在任意时刻任意节点上的同一份数据都是相同的,但是随着时间的迁移,不同节点上的同一份数据总是在向趋同的方向变化。简单说,就是在一段时间后,节点间的数据会最终达到一致状态。如:TCC和消息队列模式、Saga模式属于最终一致性的解决方案。

    了解了分布式事务中的强一致性和最终一致性理论,下面介绍几种常见的分布式事务的解决方案。

        Seata 分布式事务

    什么是Seata?

            Seata是一款阿里巴巴开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。

    什么是Seata 的AT模式?

        AT模式的特点就是对业务无入侵式,整体机制分二阶段提交

    •  基于支持本地 ACID 事务的关系型数据库。

    • Java 应用,通过 JDBC 访问数据库。

    整体机制:两阶段协议的演变

    一阶段:

            业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。

    二阶段

            提交异步化,非常快速地完成;回滚通过一阶段的回滚日志进行反向补偿。

    Seata术语:

    • TC (Transaction Coordinator) 事务协调者 :维护全局和分支事务的状态,驱动全局事务提交或回滚。

    • TM (Transaction Manager) 事务管理器:定义全局事务的范围:开始全局事务、提交或回滚全局事务。

    • RM (Resource Manager) 资源管理器:管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

            在 AT 模式下,用户只需关注自己的业务SQL,用户的业务SQL 作为一阶段,Seata 框架会自动生成事务的二阶段提交和回滚操作。

    Seata 的AT模式使用场景

           怎么理解AT模式呢,咱们以占用库存和创建订单为例:

    90830f215e1724422b371741c749e464.png

    • TM端功能包括【占用库存】和【创建订单】的操作,使用@GlobalTransaction进行全局事务开启、提交、回滚。

    • TM开始RPC调用远程服务调用分支【库存】服务和【创建订单】服务。

    • RM端seata-client通过扩展DataSourceProxy,实现自动生成undo_log与TC上报。

    • TM告知TC提交/回滚全局事务。

    • TC通知RM各自执行commit/rollback操作,同时清除undo_log。

        下面为你介绍一下AT模式下的占库存和生单流程拆解如下:

    第一阶段(占用库存过程)

    c5d8a3da4ac1956e4bf0ea16393ff688.png

    • TM开启全局事务。

    • 库存注册分支事务,获取全局锁。

    • 本地事务提交:插入前镜像beforeImage->undolog日志,占用库存,插入后镜像afterImage->undolog日志,生成undo log一并提交,将本地事务提交的结果上报给TC。

    第一阶段(创建订单过程)

    22b21c540b1498852769328b93b9e0e0.png

    • 创建订单注册分支事务,获取全局锁。

    • 本地事务提交:插入前镜像beforeImage->undolog日志,创建订单成功,插入后镜像afterImage->undolog日志,生成undo log一并提交,将本地事务提交的结果上报给TC。

    第二阶段:提交或回滚

    f244854594e493507befc93a24a081d7.png

    • 场景1.如果占用库存成功 订单创建成功,则提交全局事务结束,删除本地undolog记录

    • 场景2.如果占用库存或订单创建某一方失败,则反向操作undolog回滚日志,删除本地undlog记录。

    AT模式下如何保证数据一致的?

    一阶段提交如何保持一致性?

    • TM:method下单服务方法执行时,由于该方法具有@GlobalTranscational标志,该TM会向TC发起全局事务,生成XID(全局锁)。

    • RM写表的过程,Seata 会拦截业务SQL,首先解析 SQL 语义,在业务数据被更新前,将其保存成before image,然后执行业务SQL,在业务数据更新之后,再将其保存成after image,最后生成行锁。以上操作全部在一个数据库事务内完成,这样保证了一阶段操作的原子性。

    5c39c0f82e55b79f2790d1bf5c2e3d99.png

    二阶段如何保持一致性的?

    • 因为“业务 SQL”在一阶段已经提交至数据库, 所以 Seata 框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可。

    • 正常:TM执行成功,通知TC全局提交,TC此时通知所有的RM提交成功,删除UNDO_LOG回滚日志。

    07e8ea74b7260608004008a16e6b85c1.png

    异常阶段如何保持一致性?

    异常:TM执行失败,通知TC全局回滚,TC此时通知所有的RM进行回滚,根据undo_log反向操作,使用before image还原业务数据,删除undo_log,但在还原前要首先要校验脏写,对比“数据库当前业务数据”和 “after image”,如果两份数据完全一致就说明没有脏写,可以还原业务数据,如果不一致就说明有脏写,出现脏写就需要转人工处理了。

    5e13a9b1455a25d9fcc807e96c3a8241.png

    什么是Seata 的TCC模式?

            TCC 模式需要用户根据自己的业务场景实现 Try、Confirm 和 Cancel 三个操作;事务发起方在一阶段执行 Try 方式,在二阶段提交执行 Confirm 方法,二阶段回滚执行 Cancel 方法

    • 一阶段 :Try 资源的检测和预留。

    • 二阶段 commit 或 rollback回滚行为。

      Confirm:执行的业务操作提交;要求 Try 成功 Confirm 一定要能成功。

      Cancel:预留资源释放。

    相应的,TCC模式(try-confirm-cancel),不依赖于底层数据资源的事务支持:

    • 一阶段 prepare 行为:调用自定义的 prepare 逻辑,Try 资源的检测和预留。

    • 二阶段 commit 行为:调用自定义的 commit 逻辑,执行的业务操作提交;要求 Try 成功Confirm 一定要能成功,失败则重试。

    • 二阶段 rollback 行为:调用自定义的 rollback 逻辑,预留资源释放。

    所谓 TCC 模式,是指支持把自定义的分支事务纳入到全局事务的管理中。

    e4f1aff29a82af0f9f1a62db8200b1d1.png

    Seata 的TCC模式使用场景

        怎么理解TCC模式呢,咱们还以下单扣库存为例,Try 阶段去占库存,Confirm 阶段则实际扣库存,如果库存扣减失败 Cancel 阶段进行回滚,释放库存。

    TCC不存在资源阻塞的问题,因为每个方法都直接进行事务的提交,一旦出现异常通过则 Cancel 来进行回滚补偿,这也就是常说的补偿性事务,拆解流程请看下图:

    占用库存storage TCC流程图如下:

    eb4bf44a21637673d78c17ba997f1b52.png

    • try: 冻结库存操作。

    • confirm: 占用库存操作。

    • cancel: 回滚事务,直接调用回滚库存接口。

    订单系统 order TCC流程图如下:

    397f13a8ef7f3e386ec1fc73785cc50a.png

    • try: 新增订单-状态更新成【创建中】。

    • confirm: try成功,下单操作成功,订单状态由【创建中】更新成【创建成功】。

    • cancel: 回滚事务,删除该订单记录。

    小结

            当占用库存成功同时订单下成功时,提交全局事务,当接入TCC模式,最重要的事情就是考虑如何将业务模型拆成2阶段,实现成TCC的3个方法,并且保证Try成功Confirm一定能成功。相对于AT模式,TCC模式对业务代码有一定的侵入性,但是TCC模式无AT模式的全局行锁,TCC性能会比AT模式高很多。

    Seata 的TCC模式变种实现方案?

    在说TCC变种简化变种方案前,先说一说现实的业务场景:

        现实业务并不一定如我们所愿,在业务已成规模,订单系统 和 库存系统,都是在分布式场景,各自的业务接口,各自的DB表,如何保证数据一致呢?

    已实现逻辑采用业务代码异常调用返向补偿实现的,伪代码如下:

    try{
       占用库存()
       try{
         下单方法()
       }cach(){
       释放库存();
       取消订单();
       }  
    }cach(){
      释放库存();
    }
    • 库存服务:1.占用库存接口 2.释放库存接口。

    • 订单服务:1.下单接口 2.取消订单接口。

    流程图如下:

    b66f4b393a43714a474407e19837df91.png

    基于现有逻辑如何改造成分布式事务实现方案呢?

    方案1:采用AT模式,需要做的工作:

    • 在业务库 订单数据库和库存数据库 分别创建undo_log日志。

    • RM端(库存业务和订单业务代码)seata-client通过扩展DataSourceProxy,实现自动生成undo_log与tc上报,需要业务嵌入代码实现。

    缺点:需要对库存业务库和订单业务库存分别存加 undo_log表,事务相对比较长。

    方案2:采用Seata TCC模式

    • try阶段:提供冻结库存方法 和 预占用订单方法 需要改造库存表和订单表提供中间状态标识。

    • confirm阶段:提供占用库存方法和下单成功方法。

    • cancel阶段:提供返向 释放库存和取消订单方法。

    缺点:业务代码要写库存锁定状态业务逻辑+订单创建中的中间态。

         从上面两个方案不难看出都有一个通病,都需要业务代码改造,侵入强,耦合度高,能否有一种,即不需要业务代码改造成本又低的方案呢?

    方案3:接下来的方案是在TCC模式上做了些简化:

    • 即在try阶段:直接占库存成功和下单成功。

    • 在confirm阶段:不做业务处理,直接返回true,认为是成功的。

    • rollback阶段:做返回的取消操作即可。

    缺点:浪费了一次commit空提交 IO交互。

    优点:业务侵入性小,可以适应现有业务场景,偶合度低。

    简化版本的TCC实现方案的时序图如下:

    8fcc08ff3a16a499f798392ccdafaac9.png

    Seata TCC分布式场景下如何保证幂等呢?

    首先说一下什么是幂等?

    幂等意思:通常指对同一个系统,使用同样的条件,一次请求和重复的多次请求对系统资源的影响是一致的.因为网络抖动或拥堵可能会超时,事务管理器会对资源进行重试操作,所以很可能一个业务操作会被重复调用,为了不因为重复调用而多次占用资源,造成业务脏数据,就需要保证幂等性。

    如何保证幂等呢?

        此时需要对服务设计时进行幂等控制,通常我们可以用事务xid或业务主键判重来控制,或采用业务唯一标识来做幂等,总之还是需要业务自己来保证幂等的。

    Seata下的Saga模式?

    1.Saga 是一种补偿协议,Saga 理论出自 Hector & Kenneth 1987发表的论文 Sagas,其核心思想是将长事务拆分为多个本地短事务。

    解决了什么问题?

    2.Saga模式是seata提供的长事务解决方案,在Saga模式中,业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务都由业务开发实现。

    ffae9a281ef458a667aea70852a145e3.png

    如图:T1~T3都是正向的业务流程,都对应着一个冲正逆向补偿操作C1~C3

    适用场景:

    • 业务流程长、业务流程多。

    • 参与者包含其它公司或遗留系统服务,无法提供 TCC 模式要求的三个接口。

    优势:

    • 一阶段提交本地事务,无锁,高性能。

    • 事件驱动架构,参与者可异步执行,高吞吐。

    • 补偿服务易于实现。

    缺点:

        Saga 模式由于一阶段已经提交本地数据库事务,且没有进行“预留”动作,所以不能保证隔离性。后续会讲到对于缺乏隔离性的应对措施。

        seata分布式事务常见问题

    常见问题答疑

    问:Seata框架如何来保证事务的隔离性的?

    因seata一阶段本地事务已提交,为防止其他事务脏读脏写需要加强隔离。

        1.读隔离:Seata(AT 模式)的默认全局隔离级别是读未提交,必须要求全局的读已提交 ,目前 Seata 的方式是通过select语句加for update代理方法增加@GlobalLock+@Transactional或@GlobalTransaction实现。

        2.写隔离:

    • 一阶段本地事务提交前,需要确保先拿到全局锁 。

    • 拿不到全局锁 ,不能提交本地事务。

    • 拿 全局锁 的尝试被限制在一定范围内,超出范围将放弃,并回滚本地事务,释放本地锁。

    问:脏数据回滚失败如何处理?

    答:1.脏数据需手动处理,根据日志提示修正数据或者将对应undo删除(可自定义实现FailureHandler做邮件通知或其他人工介入操作)。

           2.关闭回滚时undo镜像校验,不推荐该方案。

    注意事项:建议事前做好隔离保证无脏数据。

    问:Seata支持哪些RPC框架?

    答:1. AT 模式支持:Dubbo、Spring Cloud、Motan、GRPC 和 Sofa-RPC。

    2. TCC 模式支持:Dubbo、Spring Cloud和Sofa-RPC。

    问:Seata现阶段支持哪些分库分表解决方案?

    现阶段只支持ShardingSphere。

    问:Seata目前支持高可用吗?

    答:支持

    1.tc使用db模式共享全局事务会话信息,注册中心使用非file的seata支持的第三方注册中心。

    2.注册中心包含:eureka、consul、nacos、etcd、zookeeper、sofa、redis、file (直连)。

    小结:

    • 在当前的技术发展阶段,不存一个分布式事务处理机制可以完美满足所有场景的需求。一致性、可靠性、易用性、性能等诸多方面的系统设计约束,需要用不同的事务处理机制去满足。

    • 目前使用的流行度情况是:AT>TCC > Saga,Seata 项目最核心的价值在于:构建一个全面解决分布式事务问题的标准化平台。基于 Seata,上层应用架构可以根据实际场景的需求,灵活选择合适的分布式事务解决方案。

    2f883ffa6f7a670c80fff8ee5b6882cc.png

    分布式方案对比

    Seata针对不同的业务场景提供了四种不同的事务模式,对比如下:

    • AT模式:AT 模式的一阶段、二阶段提交和回滚(借助undo_log表来实现)均由 Seata 框架自动生成,用户只需编写“业务SQL”,便能轻松接入分布式事务,AT 模式是一种对业务无任何侵入的分布式事务解决方案。

    • TCC模式:相对于 AT 模式,TCC 模式对业务代码有一定的侵入性,但是 TCC 模式无 AT 模式的全局行锁,TCC 性能会比 AT模式高很多。适用于核心系统等对性能有很高要求的场景。

    • SAGA模式:Sage 是长事务解决方案,事务驱动,使用那种存在流程审核的业务场景。

    • XA模式:XA模式是分布式强一致性的解决方案,但性能低而使用较少。

    e388333fc604b075c442cdd0734daab1.png

     • 后记 • 

            本文介绍了分布式事务的一些基础理论,主要对Seata分布式事务方案进行了讲解,在文章的后半部分主要给出了各种方案的常用场景。分布式事务本身就是一个技术难题,业务中具体使用哪种方案还是需要根据自身业务特点自行选择,每种方案在实际执行过程中需要考虑的点都非常多,复杂度较大,所以在非必要的情况下,分布式事务能不用就尽量不用,希望对大家有所帮助,如果感觉有所收获,可以动动小手指给点个赞,感谢阅读!

    —————END—————

    推荐阅读

    干货|如何步入Service Mesh微服务架构时代

    实战|Service Mesh微服务架构实现服务间gRPC通信

    再见Nacos,我要玩Service Mesh了!

    玩转Service Mesh微服务熔断、限流骚操作

    利用阿里云免费镜像仓库,实现微服务的k8s部署

    如何在Service Mesh微服务架构中实现金丝雀发布?

    展开全文
  • 深入理解mysql事务隔离级别和锁机制

    千次阅读 2022-03-13 10:30:52
    我们日常在使用mysql的时候,可能同时会有多个事务对同一条或者同一批数据进行增删改查,然后会导致我们常说的脏写、脏读、不可重复读、幻读等现象。其实本质上,出现出现这些问题的本质就是数据库的多事务并发问题...
  • 文章目录什么是分布式事务数据库事务隔离级别Read uncommitted 读未提交Read committed 读已提交Repeatable read 可重复读Serializable 序列化分布式事务产生的原因 什么是分布式事务 分布式事务关注的是分布式场景...
  • 事务理解

    千次阅读 2016-12-01 18:44:30
    一、事务事务(Transaction),是指访问并可能更新数据库中各种数据项的一个程序执行单元(Unit),通俗点也就是指针对数据库中形形色色的数据,我们进行一些存取操作。 事务通常是由用户编写的程序执行所引起的...
  • Hibernate中的事务理解

    千次阅读 2015-12-17 09:45:30
    1.数据库事务的概念 数据库事务是指由一个或多个SQL语句组成的工作单元,这个工作单元中的SQL语句相互依赖,如果有一个SQL语句执行失败,就必须撤销整个工作单元。  在并发环境中,多个事务同时访问相同的...
  • 深入理解分布式事务(XA及rocketmq事务) 发布时间:2018-04-16 来源:网络 上传者:用户 关键字: 事务 分布式 RocketMq 深入 理解 发表文章 摘要:本文由码农网–吴极心原创,转载请看清文末的转载要求,...
  • @[TOC] 目录 NO1.框架自动提交事务与异常回滚 NO2.控制框架的提交和事务回滚 NO3.AOP+注解实现事务的提交和回滚 NO4.spring事务源码解读
  • 深入理解分布式事务

    千次阅读 2016-07-04 14:04:57
    分布式事务就是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。以上是百度百科的解释,简单的说,就是一次大的操作由不同的小操作组成,这些小的操作
  • 数据库中事务理解

    千次阅读 2017-06-01 16:21:38
    也可以理解为集合中的一系列操作协作完成事务。 二、特性 事务的特性也成为ACID特性。即原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。 1.原子性:原子性是指事务中...
  • 深入理解分布式事务,高并发下分布式事务的解决方案
  • 分布式事务七种解决方案,最后一种经典了!

    万次阅读 多人点赞 2021-11-14 00:57:17
    随着业务的快速发展、业务复杂度越来越高,几乎每个公司的系统都会从单体走向分布式,特别是转向微服务架构。随之而来就必然遇到分布式事务这个难题,这篇文章总结了分布式事务最经典的解决方案,分享给...
  • 数据库事务和隔离级别的理解

    千次阅读 2019-03-09 10:36:10
    数据库事务和隔离级别的理解 1.事务的概念 ​ 事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功。 2. MySQL数据库事务操作命令 ​ 直接通过例子来说明 1.事务开始但没有提交 ...
  • 为了保证事务操作的原子性,必须实现基于日志的REDO/UNDO机制:将所有数据的更新操作都写入日志,如果一个事务中的一部分操作已经成功,但以后的操作,由于断电/系统崩溃/其它的软硬件错误而无法继续,则通过回溯...
  • 事务处理 :本地 、全局、分布式

    千次阅读 2022-01-10 21:34:28
    事务处理 :本地 、全局、分布式 单个服务使用单个数据源:本地事务 单个服务使用多个数据源:全局事务 多个服务使用单个数据源:共享事务(通常不会这么使用) 多个服务使用多个数据源:分布式事务
  • 彻底理解事务的4个隔离级别

    千次阅读 2017-04-07 09:47:00
    所以一旦事务被提交之后,数据就能够被持久化下来,又因为事务是满足隔离性的,所以,当多个事务同时处理同一个数据的时候,多个事务直接是互不影响的,所以,在多个事务并发操作的过程中,如果控制不好隔离级别,就...
  • MySQL事务——万字详解

    千次阅读 多人点赞 2021-11-25 16:06:50
    介绍什么是事务,隔离级别和操作 解释MVCC原理
  • 事务 跨库事务 分布式事务及解决方案

    千次阅读 多人点赞 2019-08-29 05:42:46
    什么是事务? 本地事务 事务传播行为: 事务的四大特性 ACID 并发事务产生的问题可以分为4类 事务的隔离级别 什么是分布式事务 分布式涉及到的原理: CAP原理: BASE理论 柔性事务和刚性事务 柔性事务分为:...
  • 事务并发控制(一)

    万次阅读 2019-01-15 21:29:51
    1、事务的概念 事务是用户定义的一组数据库操作序列。 事务具有ACID特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持续性(Durability)。 原子性指:事务包含的所有操作要么全部被执行,...
  • JDBC之事务详解

    千次阅读 多人点赞 2020-04-02 16:31:55
    数据库中事务的使用Get一下?
  • 一文搞懂MySQL XA如何实现分布式事务

    千次阅读 多人点赞 2021-11-07 17:36:42
    一文搞懂MySQL XA如何实现分布式事务前言XA 协议如何通过MySQL XA实现分布式事务 前言 MySQL支持单机事务的良好表现毋庸置疑,那么在分布式系统中,涉及多个节点,MySQL又是如何实现分布式...导致结果理解混乱。 那
  • 本地事务 本地事务流程 在介绍分布式事务之前,我们先来看看本地事务。...缺乏分布式事务的处理能力。 数据隔离的最小单元由RM(资源管理器决定),开发人员无法决定数据隔离的最小单元。比如:数据库中
  • 数据库事务的概念及其实现原理

    千次阅读 2021-07-02 10:59:05
    1. 认识事务 1.1 为什么需要数据库事务 1.2 什么是数据库事务 1.3 事务如何解决问题 1.4 事务的ACID特性以及实现原理概述 2.并发异常与并发控制技术 2.1 常见的并发异常 2.2 事务的隔离级别 2.3 ...
  • 事务处理原理 第2版

    热门讨论 2012-12-30 10:49:38
     阅读完本书之后,您会理解事务中间件的工作原理和何时使用事务中间件,以及事务中间件和数据库系统如何协同工作以支持可靠的分布式事务处理应用程序。还会迅速掌握如何使用任何事务中间件产品或数据库系统来支持...
  • RabbitMq分布式事务解决方案第一篇

    万次阅读 2021-03-07 10:35:40
    可以依据自己所在项目的特点,有选择的使用,比如数据的一致性要求严格而并发数可容忍的,可以考虑使用seata解决,并发要求高,同时数据一致性的要求也比较高的,可以考虑使用rocketMq事务消息 下面要介

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 137,940
精华内容 55,176
热门标签
关键字:

对事务的理解能力