精华内容
下载资源
问答
  • 事务一致性
    千次阅读 多人点赞
    2020-12-05 15:21:19

    何为一致性?

    初学数据库事务时老师讲到数据库四大特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。

    原子性和持久性概念基本秒懂,隔离性也好理解。但这个一致性实在让人摸不着头脑。什么和什么一致?体现在哪里?

    先看看官方的套娃解释:一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。

    我让你解释一致性,你又拿一致性来解释一致性。这不是在套娃吗?

    于是百度了一下,发现好多半吊子博主,把原子性的解释套在一致性上,自己满脑子浆糊,还大言不惭的误人子弟。

    还有些博主估计段位太高,不屑把话说的太明白,他们是这么解释一致性的:“一致性是指数据处于一种语义上的有意义且正确的状态。一致性是对数据可见性的约束,保证在一个事务中的多次操作的数据中间状态对其他事务不可见的。因为这些中间状态,是一个过渡状态,与事务的开始状态和事务的结束状态是不一致的。”

    我反正是更晕了。

    于是继续百度之,有人用AB转账举例,A账户总共5000元,不论怎么他怎么给B转,AB账户合计总额还是5000.这就是一致性。貌似有些意思了,但终归感觉说的不是很透彻。

    最后终于看到了一句解释点醒了我。原话忘了,但含义总结如下:

    所谓一致性是数据库处理前后结果应与其所抽象的客观世界中真实状况保持一致。这种一致性是一种需要管理员去定义的规则。管理员如何指定规则,数据库就严格按照这种规则去处理数据。

    就拿那个老掉牙的AB转账来进一步解释,如果说AB账户总金额5000就是数据库的一致性规则,那么我能不能把A账户转走10000给B,让B账户有10000,而A剩下-5000?从数学上来看完全正确,但这显然是不符合常理的。而这种常理,就是所谓的一致性。

    你悟了吗?


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

    千次阅读 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

    展开全文
  • 2、事务一致性,和原子性类似,都是从一个状态变到另一个状态,但不同的是,原子性追求这个过程不能出错,不论结果对不对,不能出错。但一致性更追求结果一致,比如A减少100,B增加100,这是一致的。当A减少100,B...

    关于各种一致性的理解:
    1、数据一致性,往往指的是缓存和数据库的一致性。

    2、事务的一致性,和原子性类似,都是从一个状态变到另一个状态,但不同的是,原子性追求这个过程不能出错,不论结果对不对,不能出错。但一致性更追求结果一致,比如A减少100,B增加100,这是一致的。当A减少100,B增加60,这是原子的,但不是一致的。

    3、分布式事务的一致性:本质上来说,分布式事务就是为了保证在分布式场景下,数据操作的正确执行。但分布式事务不像本地事务,可以做到ACID,分布式事务做不到。比如分布式存储场景下,一个存储节点通过本地事务减少100,不能同时保证另一个存储节点事务增加100,这实际上就是数据不一致。而在本地事务中,是可以做到的,本地事务把减100和加100当作一个原子操作来执行。
    但是分布式事务也是可以解决这一类问题的,但从而带来了很高的代价,也就是我们说的CAP理论,P分区容错是一定的。那么当C很强时,也就是强一致性的时候,也就是说用户访问任何一台数据库服务器,必须都是要数据一致的,第一个存储节点减少100,另一个存储节点必须增加100,这就是强一致性,但是问题是有网络延迟,第一个节点减少了100,第二个还没来得及加100,此时用户要访问第二台数据库服务器,怎么办? 那就不让他访问,也就是吸收Availability可用性。那如果要保证可用性怎么办?那就牺牲一致性,数据不一致没关系,最终一致就可以了。

    那解决这样的问题的框架有哪些?比如seata的AT,TCC等,就是实现全局事务的统一提交,要么就不提交,那这样就能保证所有数据的一致了。但当全局事务没有统一提交的时候,可能用户的请求查数据库,可能查到的还是旧数据。

    4、分布式事务的原子性:单个分枝事务成功,但其他事务可能失败,需要全局回滚。

    5、注意:CAP理论强调的是说在分布式存储系统中的。

    以上纯属个人理解,有问题请指出探讨。

    参考:https://blog.csdn.net/yeyazhishang/article/details/80758354
    参考:https://segmentfault.com/a/1190000040321750
    参考:http://www.dockone.io/article/9804

    展开全文
  • 数据库怎么保证(分布式)事务一致性

    万次阅读 多人点赞 2019-03-15 10:15:43
    浅谈事务一致性问题 原文地址 https://www.jianshu.com/p/f0a1b00a6002 在高并发场景下,分布式储存和处理已经是常用手段。但分布式的结构势必会带来“不一致”的麻烦问题,而事务正是解决这一问题而引入的一种...

    浅谈事务与一致性问题 原文地址 https://www.jianshu.com/p/f0a1b00a6002

    在高并发场景下,分布式储存和处理已经是常用手段。但分布式的结构势必会带来“不一致”的麻烦问题,而事务正是解决这一问题而引入的一种概念和方案。我们常把它当做并发操作的基本单位。

    从MySQL事务说起(刚性事务)
    提到事务,脑海里第一个反应当然是数据库里的Transaction了。紧接着就是事务的四大特性:ACID (原子性,一致性,隔离性,持久性),所以我们先从这四大特性说起。

    原子性

    原子性是我们对事务最直观的理解:事务就是一系列的操作,要么全部都执行,要么全部都不执行。
    想要保证事务的原子性,就意味着需要在操作发生异常时,对该事务所有之前执行过的操作进行回滚。
    在MySQL中,这个回滚是通过**回滚日志(Undo Log)**实现的。简单的说,回滚日志就是记录了你所有操作的逆操作,在需要回滚时,就把这个事务的回滚日志里的操作全部执行一次。
    比如你的事务里每一个create其实都对应了一个效果跟其相反的delete语句,他们被记录在回滚日志里,当事务发生异常触发ROLLBACK时,就按照日志逻辑地将回滚日志里的操作全部执行,从而达到“撤销”操作的效果。

    事务的状态
    宏观上看事务是具有原子性的,是一个密不可分的最小单位。但是它是有几种不同的状态的:Active,Commited,Failed,它要么在执行中,要么执行成功,要么就失败。
    深入事务的内部,他就变为一系列操作的集合,不再具有原子性了,包括了很多的中间状态,比如部分提交,参考如下的事务状态图:

    [图片上传失败...(image-30ed42-1517751460929)]

    Active 事务的初始状态,表示正在执行
    Partially Commited 部分执行,或者说在最后一条语句执行后
    Failed 发现操作异常,事务无法继续执行后
    Commited 成功执行整个事务
    Aborted 事务被回滚,数据库恢复到执行前状态后

    并行事务的原子性

    正常情况下事务都是并行执行的,这就会出现很多复杂的新问题。
    首先是**事务依赖,**举一个直观的例子来说明:
    假设事务T1对数据A进行了读写,然后(T1还没有执行完)在同时,T2读取了数据A,然后成功提交了事务。这时候T1发生了异常,进行回滚。我们可以看到事务T2是依赖于T1所修改的数据的,如果要保证T1的原子性,那就需要同时对T2进行回滚,但是它已经被提交了,我们没法再回滚了,这种问题被称为“不可恢复安排”。
    为了避免这种情况的出现,在出现事务的依赖时,必须遵循以下的原则:
    如果事务T2依赖于事务T2,那么T1必须在T2提交之前完成提交操作。

    接下来我们还不得不面对级联回滚,也就是出现了多个事务都依赖于事务A的时候,如果A回滚,那么这些事务必须也一并回滚。这会导致大量的工作撤回,至于这件事情如何处理才合适,我们会在后面介绍。

    持久性

    这是理解起来相对简单的一个特性,持久性就是指,事务一旦被提交,那么数据一定会被写入到数据库中并持久储存起来
    另外,当事务被提交后就无法再回滚,如果想要撤销一个已经提交的事务,那就只能执行一个效果与其相反的事务,这也是持久性的一种体现。关于这点,MySQL依然是通过日志实现的。

    重做日志
    重做日志由两部分组成,一是内存中的重做日志缓冲区,另一个是磁盘上的重做日志文件
    这个缓冲区和日志的关系跟我们日常IO中使用的buffer是差不多的:当我们在事务中尝试对数据进行更改时,首先将数据从磁盘读入内存更新内存缓存的数据,然后会生成一条重做日志(本次修改的逆操作)缓存,放在重做日志缓冲区中。当事务真正提交时,再将刚才缓冲区中的日志写入重做日志中做持久化保存,最后再把内存中的数据变动同步到磁盘上
    上面这个流程用图片描述如下:
    [图片上传失败...(image-626f5a-1517751460929)]
    再具体一点,InnoDB中,重做日志都是以512B的块形式储存的,因为磁盘的扇区也是512B,所以重做日志的写入就保证了原子性,即便机器断电也不会出现日志仅仅写入一半而留下脏数据的情况。
    另外需要注意的一点是,在原子性一节中提到的回滚日志也是需要持久化储存的,因此他们也会创建对应的重做日志,在发生错误后,数据库重启时,会从重做日志中找出未被更新到的数据库磁盘上的日志,重新执行来满足事务的持久性。

    *事务日志

    在数据库系统中,事务的原子性和一致性是由事务日志实现的,在具体的实现上,使用的就是之前提到的回滚日志和重做日志,它们保证了两点:

    发生错误或者需要回滚的事务能够成功回滚(原子性)
    事务提交后,数据还没来得及写入磁盘就宕机时,重启后能够成功恢复数据(一致性)

    在数据库中这两者往往一起工作,因此我们可以把他们看作一个整体。一条事务日志的内容可以抽象成下面这样:
    [图片上传失败...(image-e7e2a2-1517751460929)]
    一条记录同时保存了对应数据修改前后的值,就可以非常方便的实现回滚和重做两种功能。

    隔离性

    事务的隔离性会跟并发等相关概念联系的非常密切,因为它主要就是为了保证并行事务处理能够达到“互不干扰”的效果。
    我们在一致性中讨论过事务在并发情况下执行时,可能发生的一系列问题:虽然单个事务执行并没有错误,但是它的执行可能会牵连到其他事务的执行,最终导致数据库的整体一致性出现偏差。
    谈到这里我们就要看看事务之间的互相干扰都有哪些层级,也就是我们数据库中非常重要的概念:

    事务的隔离级别
    事务的隔离级别,其实是数据库对数据隔离性能的一种约束,选择不同的隔离级别会影响数据一致性的程度,同时也会影响数据库的操作性能。
    标准SQL中定义了以下4种隔离级别:

    未提交读
    使用查询语句不会加锁,可能会读到未提交的行(脏读)

    提交读
    只对记录加记录锁,而不会在记录之间增加间隙锁,所以允许新的记录被插入到被锁定记录附近,在多次使用查询语句时,可能会得到不同的结果(不可重复读)

    可重复读
    多次读取同一范围的数据会返回第一次查询的快照,不会返回不同的数据行,但是可能发生幻读
    幻读 : 是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。 同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象 发生了幻觉一样。

    串行化
    隐式地将全部的查询语句都加上了共享锁。

    从上到下一致性逐渐增强,但是数据库的读写性能也逐渐变差
    大部分数据库中使用提交读作为默认的隔离级别,这是出于性能和一致性的平衡,而MySQL中则默认采用可重复读作为配置。

    对于开发者而言,不必去了解每个隔离级别具体的实现,但要能够根据不同的场景选择最合适的隔离级别。

    隔离的实现
    隔离的实现说到底其实是并发控制,因此不同隔离级别的实现,其实就是采用了不同的并发控制机制。

    1.锁
    这个自然是最简单的,也是相当常用的并发控制机制了。
    不过在一个事务中,自然是不可能把整个数据库都加锁的,而是只对要访问的数据加锁(具体的粒度有行、表等)。而这些资源锁也是理所当然地分为共享锁(读锁)和互斥锁(写锁)两种。
    读锁可以保证操作并发执行而不受影响,写锁则保证了更新数据库时不会受到其他事务的干扰。

    2.时间戳
    用时间戳实现隔离性,需要为记录配置两个字段

    读时间戳:用于保存所有访问该记录的事务中的最大时间戳(最后读取时间)

    写时间戳:用于保存将记录改到当前值的事务的时间戳(最后修改时间)

    这样的事务在并行执行时,用的是乐观锁,先任由事务对数据进行修改,在写回去的时候在判断记录的时间戳有没有修改,如果没有被修改,就写入,否则,就生成一个新的时间戳并再次尝试更新数据。

    PostgreSQL就使用了这种思想来控制事务。

    3.多版本和快照隔离

    通过维护多个版本的数据,数据库便可以允许事务并发执行遇到互斥锁时,转而读取旧版本的数据快照。这样就能显著地提升读取的性能。我们简称这一手段为MVCC

    级联回滚

    之前在讨论原子性问题时,讨论过级联回滚的问题,那是因为事务之间产生了依赖而导致的。因此我们将事务隔离之后,就不会再产生需要级联回滚的场景了。
    比如一个事务写入了A数据,那么这时候是需要加排他锁的,因此其它的事务无法读取A,当事务A回滚时不用考虑对其它事务的影响,因为其它的事务并不可能读到数据。

    一致性

    好了,这时候我们终于回归到了本文所想讨论的主题上来。“一致性”在数据库领域有两个意义,一个是ACID中的C,另一个是CAP的C,前者是我们经常讨论的,也是普遍意义上的数据库事务一致性,而后一个将是之后会展开讨论的,有关分布式事务的一致性

    ACID
    事务的一致性定义基本可以理解为是事务对数据完整性约束的遵循。这些约束可能包括主键约束、外键约束或是一些用户自定义约束。事务执行的前后都是合法的数据状态,不会违背任何的数据完整性,这就是“一致”的意思。
    当然这个含义中也隐含着对开发者的要求,就是不能写出错误的事务逻辑,比如银行的转账不能只加钱不减钱,这是应用层面的一致性要求。

    CAP
    CAP定理是分布式系统理论的基础。CAP告诉我们,对于一个分布式系统(或者由于网络隔离等原因产生的分区系统),它无法同时保证一致性、可用性和分区容忍性,而是必须要舍弃其中的一个。

    p.s. 对于分布式系统一般我们是不可能舍弃分区容忍性的(因为分区的情况是无法避免的),所以一般是根据业务,在一致性和可用性中二选一。
    这里说的一致性,具体在数据库上,就是分布式数据库中,每一个节点对于同一个数据必须有相同的拷贝(每个库里的同一个数据内容必须是一致的)

    分布式事务
    现在我们来看一看,当数据分布式储存后,操作所带来的一些问题。
    众所周知,现在大型服务出于性能和容灾的考虑,都会使用分布式的服务架构,这意味着一个服务会有多个数据库,分开储存不同的数据,这种情况下就很容易出现数据不一致的问题了,一个最简单的例子:
    A要B给转100元。但是A和B的记录被分在了不同的数据库实例上,如果这时候执行的某个事务中途出现了bug,如果没有一个好的处理方式,回滚将会是一件难以面对的事情。
    所以我们可以看到,在分布式环境下,事务的设计方案变得更加复杂,也更加重要了,下面我们来谈谈分布式事务的一些常见实现方式:

    两阶段提交(2PC)
    原理
    两阶段提交是一种提交协议,在这种协议下,事务的实现被拆分成了几个不同的模块,一般分为协调器和若干的事务执行者,如下图:
    [图片上传失败...(image-5fa697-1517751460929)]
    在分布式系统中,每个节点虽然可以知道自己操作是否成功,但是却无法得知其他节点上操作是否成功,因此当一个事务跨越了多个节点的时候,就需要一个协调者,能够掌控到所有节点的执行情况,进而保证事务的ACID特性

    现在我们来分析2PC协议条件下,转账问题是如何被解决的(我们假设A是你的支付宝余额,B是你的余额宝)。

    A发起请求到协调器,协调器开始工作

    准备凭证

    协调器将prepare信息写到本地日志,这就是回滚日志了。

    向所有的参与者发起prepare信息,当然对于不同的执行者,这个prepare信息是不同的,这取决于他们的数据实例上要发生什么样的变动,比如这个例子中,A得到的prepare消息是通知支付宝余额数据库扣除100元,而B得到的prepare消息是通知余额宝数据库增加100元。

    执行者收到prepare消息之后,执行本机的具体事务,但不会commit,如果成功则向协调者发送yes回执,否则发送no。
    协调者判断收集到的所有回执,如果均为yes,就向所有的执行者发送commit消息,执行器收到该消息后就会正式执行提交。反之,如果收到任何一个no,就向所有的实行者发送abort消息,执行器收到后会放弃提交并回滚相应的改动

    协调器上保存的回滚日志,可以用于某个执行器失败后恢复的工作的场景,此时执行器可能会再次向协调器发送回执来确定自己的执行状态。

    问题
    2PC实现的思路倒是很简单,不过这个思路中存在着几个非常严重的问题,因此几乎不被使用:

    涉及多次节点间的通信,假设网络延迟比较高,通信时长基本是不可忍受的
    事务时间变长了,也意味着资源上锁的时间变长了,性能大打折扣
    如果参与者多了,协调器的工作效率会下降,而整个流程也变得复杂起来

    其实分布式事务的种种实现方案基本都借鉴了2PC的思路,但很快人们就发现一个问题,在分布式的系统中,如果仍然采用事务模型来进行数据的修改,性能将受到不可避免的影响,这在高并发的场景下是不能接受的。

    最终一致性(柔性事务)
    刚才我们讲了分布式事务在高并发场景下的败北,其实根据CAP原则我们很容易明白,想要保证可用性的同时保证一致性是不可能的,于是现在大多数的分布式系统中都对一致性做出了妥协:
    我们不追求整个操作过程中每一时刻的一致性(强一致性),转而追求最终结果的一致性(最终一致性)
    也即是说,在整个事务执行的流程中,我们是可以接受的短暂的数据不一致的,只要最后的结果没问题就行。
    至此,我们对于事务的研究,从满足ACID的刚性事务,拓展到BASE(基本可用,软状态,最终一致性)的柔性事务

    BASE
    BASE原则是在分布式场景下,为了保证高可用性,而做出的一种“妥协性”思想。总的来说是允许局部的错误和故障,但要保证全局的稳定。事实上当前大多数的分布式系统,或者说大多数的大型系统里,都在运用这种思想了。
    在展开柔性事务之前,我们先来补充一些基础知识。

    重试与幂等
    在接下来讲到的各种思路中,我们都无法避免一个问题,那就是接口调用或者说操作的失败,分布式情况下系统的状态往往不如单机条件下确定,所以可能经常需要重试,而不是一失败就回滚。
    因此我们必须尽可能的避免重试对系统稳定性和性能的影响,于是有了幂等这个概念:

    幂等
    数学定义:**f(x) = f(f(x))**的性质
    编程定义:对同一个系统,使用同样的条件,一次请求和重复的多次请求对系统资源的影响是一致的

    然后我们需要探讨一下保证幂等常用的思路,我们以微博点赞这个操作为实际例子来看一下(点赞是不能重复的):

    MVCC
    数据更新时需要比较持有数据的版本号,版本号不一致的话是无法操作成功的。
    每个版本只有一次执行成功的机会,一旦失败了就要重新获取版本号。
    这样每次点赞操作都对应着一个不同的版本号,即便失败重复尝试,也不会出现点赞数错误增加或减少的情况。

    去重
    这个主要依赖数据库的索引唯一性(键),以点赞操作为例,可以对[user_id,weibo_id]这个组合做一张“点赞操作表”,如果成功点赞,就添加一条新记录。
    如果出现了错误的重试,因为表的索引是唯一的,已经有了记录自后就不会再次插入,自然也就不会出现错误的情况了。

    异步确保

    2PC的处理过程中一个很大的问题是,存在大量的同步等待,这便意味着操作之间的强耦合,一旦发生了失败或是超时,造成的影响往往是灾难性的。但是分布式情况下,超时和失败又是很可能出现的情况,所以2PC手段没法保证系统的可用性。

    那么怎么优化呢?可以将操作解耦,使用消息队列(或者某种可靠的通信机制)来连接不同的实例上的操作。这样的通信机制使操作异步化,于是我们还需要一个能够确保消息执行成功的确保机制,以上两点的综合就是现在最常用的柔性事务解决方案,我们暂且叫它“异步确保”(因为这种方案并非有一个统一的叫法),核心思路其实就是:用消息队列保证最终一致性
    下面我们一步一步深入,了解这种方案的基本思想和流程。

    问题
    我们依然使用经典的转账问题来展开讨论:A要向B转100元,但是A和B的账户在不同的实例上存储。
    异步确保的思想,操作的流程应该如此处理:

    A所在的实例扣除A账户100元
    向B所在的实例发送操作消息,通知它给B的账户增加100元

    这是一个很理想的情况,其实我们有很多的问题要处理。
    首先是原子性,其实很容易发现,无论顺序如何,如果1和2这两个操作有任何一个失败了,那另一个操作也必然变得没有意义,所以必须保证1和2这两个操作的整体原子性

    这里很多人会想,直接利用刚性事务的ACID特性,把1和2放在同一个事务里不就ok了。但这是不可能的,原因如下:

    网络的2将军问题:发送消息如果失败了,发送方并没有办法知道,是接收方没收到消息,还是接收方返回响应的时候出现了故障,其实已经收到了?
    在DB事务里插入网络操作,如果出现延迟,会导致事务执行时间变长,对DB性能影响极大,严重的话可能block整个DB。

    所以事情没那么简单,所以在我们得做不少额外的工作才能解决这个问题,下面是现在常用的解决思路:消息表。

    先说生产方(A的实例)

    生产方添加一张消息表,用于记录发送的消息以及消息的回执等内容。

    生产者在向消费者发送业务操作数据时,同时也要在消息表里增加一个消息记录,这两个都是对生产者DB的操作,我们要把它们放在同一个事务里来保证一致性。举个例子,转账问题在A端上这个操作的sql就是这样的(有点随意,会意即可):

    begin transaction;
    update account set amount = ($amount - 100) where user = A;
    insert into message values(‘b’,‘account’,’-100’);
    end transaction;

    对于这张消息表,我们需要一个维护者,它的职责是,不断地把表中未发送的消息放入消息队列,另外检测消息的执行是否超时或失败,如果遇到这种异常情况,就进行重试。注意:允许消息重复,但是不能丢失,顺序也不会打乱

    再说消费方(B的实例)

    消费方的接口(我们称为下游接口),必须实现幂等。这是因为生产方可能会发来很多的重试消息,我们必须保证重试操作不会对系统产生不良影响。如果之前说的幂等手段不适用,可以简单的为消费方准备一个判重表,利用判重表的Insert操作来实现幂等(如果这么做,请注意在业务中保证消费操作和Insert判重表操作的原子性)。
    消费方完成操作后,利用消息队列向生产方发送确认消息就ok。

    可以看到这个实现方案对于业务的生产方来说,需要维护很多额外的操作,尤其是需要设计维护消息表,可能还要做后台任务处理等,某种程度上这会增加业务端不必要的逻辑耦合,以及性能负担。
    简要工作流程如下图所示:
    [图片上传失败...(image-ec7c6-1517751460929)]
    事务消息
    正如上文所说,异步确保的思路中,大多数操作其实与业务无关,可以封装到消息队列中去。于是产生了“事务消息”这一概念,也就衍生了很多能够很好的支持分布式事务消息相关操作的消息队列或者中间件,如RocketMQ和Notify

    我们来看看事务消息是如何优化和整合异步确保的逻辑的。
    首先,把消息发送分成了2个阶段:准备和确认阶段,于是生产方步骤变为如下3步:

    发送prepared消息给MQ
    执行本地事务
    根据本地事务执行结果,确认或者取消prepared消息

    这里有一个问题,就是如果1和2失败了,还是很容易回滚和取消的,但是第三步失败或者超时了,要怎么做呢?
    以RocketMQ为例,MQ会定期地扫描所有的prepared消息,询问发送方,到底是要确认发送这条消息,还是要取消这条消息?这点底层是通过让生产方实现一个约定好的Check接口来实现的,有点像订阅者模式。
    我们可以看出来,异步回调中,扫描消息表,确认或重发消息这个步骤被消息队列实现了,减少了业务方开发的难度。
    对于消费方,事务消息支持重试的特性,也就是说不必生产者去主动发起重试消息,消息队列可以自动帮你重试这些操作,可以说是非常解放生产力了。
    如果有极端情况,比如消费端异常,无论怎么重试都失败,是否要回滚呢?其实最好的办法就是人工介入,人工去处理这种概率极低的case,比开发一个高复杂的自动回滚系统要可靠的多,也更简单。

    事务补偿(TCC)

    除了比较常用的异步确保,我们再介绍一种常见的实现柔性事务的思路,称为事务补偿。
    总结之前的内容,我们不难发现,分布式事务的难点在于,一方执行事务成功之后,无法确定其他参与方对应的事务是否能够成功(除非牺牲系统可用性)。
    事务补偿的想法和回滚日志有些类似。既然我们没办法同时保证所有的参与方事务执行都成功,不如就让他们随意执行,谁成功了就提交本地事务。但是每个参与方的每个操作,都要注册(注意是注册,不是自动生成)一个对应的补偿操作,这个补偿操作由人为定义,用于撤销已执行事务带来的影响。

    当某一方的事务执行失败时,所有已经成功提交了事务的参与方,需要按照顺序(提交的倒序)去执行各自的补偿事务,来将整个系统“回滚”到之前的状态
    补偿型思路的一个典型实现是TCC(Try-Confirm-Cancel)事务,其实说是事务,不如说是一种业务模式,因为Try,Confirm,Cancel这三个操作都必须由业务方实现。

    Try:资源预留&锁定。事务发起方将调用服务提供方的Try方法来锁定业务所需要的所有资源。
    Confirm:确认执行业务逻辑操作。这里使用的资源一定都是在Try中预留的资源,Try + Confirm 组合起来是一次完整的业务逻辑。
    Cancel:取消执行业务逻辑。这里和普通的补偿性事务不同,因为Try阶段只是预留资源,并未真正执行操作,因此取消操作只需要释放Try阶段预留的资源,而不需要执行数据库操作来补偿。

    其实TCC可以认为是应用层的2CP协议。网上关于TCC的相关逻辑说法很多,也比较混乱,这里找到一个比较通俗普遍的例子来解释TCC的流程。当然实际应用中,根据业务的场景不同,TCC的实现也不同:它只是一种思路,而并非是一种规范。
    例子仍然是转账问题,我们把范围稍微扩大一点,现在我们有三个用户A,B,C分别位于三个不同的数据库实例上,现在A,B要分别向C转账40元(一共80元)

    Try阶段:尝试执行。

    业务检查(一致性):检查A,B,C的账户状态是否正常,以及A,B的账户余额是否都不低于40元。
    预留资源(准隔离性):账户A、B的余额均冻结40元。这样保证其他并发事务不会把A、B的余额扣成负数。

    Confirm阶段:确认执行。

    真正执行事务:执行实际的业务操作:A、B账户减少40元,C账户增加80元。(这一步还是需要消息传递机制)

    Cancel阶段:取消执行。

    释放A,B账户上被成功冻结的金额。

    小结
    分布式的结构下,事务的实现依然没有一个放之四海而皆准的标准。但是可以看到一个统一的原则,那就是尽可能的让服务变得更具有弹性,能够灵活地应对多种情况。
    总的来说,分布式事务更大的挑战在于,相关业务逻辑的开发思路:可用性与一致性的平衡。

    展开全文
  • mq实现分布式事务-补偿事务一致性CAP原则Rocket mq实现思路Rabbit mq实现思路需要考虑的问题后记 严格的来说,消息中间件并不能实现分布式事务,而是通过事后补偿机制,达到和分布式事务一样的数据一致性。这里主要...
  • 分布式事务一致性

    千次阅读 2018-05-25 17:18:45
    有人的地方,就有江湖有江湖的地方,就有纷争问题的起源在电商等业务中,系统一般由多个独立的服务组成,如何解决分布式调用时候数据的一致性? 具体业务场景如下,比如一个业务操作,如果同时调用服务 A、B、C,...
  • 事务一致性

    千次阅读 2018-12-01 18:29:50
    作者:孟波 ...来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明...首先,我们需要搞清楚为什么会出现事务.[1]Transactions are not a law of nature; they were created with a purpose,...
  • 我们看一个跨库事务一致性的问题,这是一个简单的场景:有新老两个系统,对应新老两套数据库,新数据库采用分库分表的设计,考虑到项目发布之后可能存在风险,采取了新老系统的并行方案。这个系统的业务比较简单:...
  • 事务一致性的理解

    万次阅读 多人点赞 2018-04-05 20:37:54
    要想真正弄清楚这个问题,那是必须要把数据库理论中的事务机制从头开始看起,牵扯的内容比较多。当然,如果只是想粗略的了解下,我就来举个例子吧——当然不可能太严谨。假设我们10个人,每人有一个账号,...
  • 微服务架构下的事务一致性保证

    千次阅读 2019-04-04 14:07:56
    今天我给大家分享的题目是微服务架构下的事务一致性保证。 主要内容包括4部分: 传统分布式事务不是微服务中一致性的最佳选择 微服务架构中应满足数据最终一致性原则 微服务架构实现最终一致性的三种模式 对账...
  • SpringBoot+Mybatis配置多数据源并且实现事务一致性

    万次阅读 多人点赞 2019-10-27 16:57:54
    呃,本文一共分三部分:SpringBoot+Mybatis环境搭建、两种方式配置多数据源、两种方式实现跨数据源事务,您可以直接跳到喜欢的部分,不过按顺序看完也不会花很多时间。。。 一、搭建SpringBoot+Mybatis框架环境 看...
  • 为什么要实现事务一致性?通常在一个服务中,新增或修改操作功能常常会涉及操作多张表,当发生异常时,极大可能会产生脏数据,若实现事务一致性,可避免这些操作带来的脏数据的产生。 那什么是事务呢?那就简单介绍...
  • 事务一致性简述

    千次阅读 2015-11-15 14:35:40
    事务一致性简述
  • 目的:事务一致性 组网图:不涉及 工具:java version “1.8.0_65” ;Apache Maven 3.6.3;mysql-5.7.26;IDEA版本 2018.3 (准备步骤见本人其它博文); 简介:Springboot中使用@Transactional完成...
  • 写数据库同时发mq消息事务一致性的一种解决方案 事件驱动(event driven)的系统设计,服务之间的交互大多数都是通过消息队列中间件,那么我们都会面临一个微服务之间数据一致性的问题。 假设如下场景:服务A在一...
  •  1)支付宝在扣款事务提交之前,向实时消息服务请求发送消息,实时消息服务只记录消息数据,而不真正发送,只有消息发送成功后才会提交事务;  2)当支付宝扣款事务被提交成功后,向实时消息服务确认发送。只有在...
  • dubbo如何保证事务一致性

    千次阅读 2018-06-25 18:53:00
    作者:清风链接:...目前比较多的解决方案有几个: 一、结合MQ消息中间件实现的可靠消息最终一致性 二、TCC补偿性事务解决方案 三、最大努力通知型方案第一种方案:可靠消息最终一致性,需要业务系统结...
  • 事务一致性与原子性的区别

    千次阅读 2017-12-29 14:19:50
    其实一致性和原子性在事务里是两个不太相关,但又很相关的逻辑来的 原子性:这个侧重点是事务执行的完整,一套事务下来,如果有一个失败,那整体失败。也就是要么大家一起成功,要么全都回滚 一致性:这个讲的...
  • 第10章 事务一致性 一致性问题分为两类: 1.事务一致性 2.多副本一致性 10.1 随处可见的分布式事务问题 分布式时代,数据库的单机机制不管用了,因为数据库本身只能保证单机事务,对于分布式事务,只能靠...
  • LCN分布式事务框架框架介绍LCN分布式事务框架其本身并不创建事务,而是基于对本地事务的协调从而达到事务一致性的效果。核心步骤创建事务组是指在事务发起方开始执行业务代码之前先调用TxManager创建事务组对象,...
  • 关于事务一致性,《数据库系统概念》中是这样描述的 第二段说的三个特性是指原子性、隔离性、持久性。 就算这样,相信大家也是懵懵的,我也是,所以才会写下这篇博客。 看到别的博客说,一致性事务的最终...
  • 分布式系统事务一致性到CAP,BASE理论

    千次阅读 2017-02-08 10:44:00
     2、一致性(Consistency):事务一致性是指事务执行之前和执行之后数据保持一致。  3、隔离行(Isolation):事务的隔离性是指两个或多个事务之间相互隔离,互不影响。  4、持久性(Durability):事务持久...
  • 今天和同事讨论了下微服务架构下的事务一致性问题,查了下资料,抄录如下。说到底,最终都必须保证数据一致性,至于中间具体采用哪种模式,还是需要根据具体的业务逻辑来选择。1、可靠事件模式可靠事件模式属于事件...
  • 分布式系统事务一致性解决方案

    千次阅读 2018-08-31 15:18:00
    在OLTP系统领域,我们在很多业务场景下都会面临事务一致性方面的需求,例如最经典的Bob给Smith转账的案例。传统的企业开发,系统往往是以单体应用形式存在的,也没有横跨多个数据库。我们通常只需借助开发平台中特有...
  • 1.使用Seata做强一致性分布式事务 还是我们开头提出的问题:如何保证1.1、1.2、1.3要么同时成功,要么同时失败,本小节,使用alibaba seata作为分布式事务的解决方案,达到这个目的。 2.Seata的AT模型介绍 seata...
  • 事务一致性是指:事务必须是使数据库从一个一致性状态变到另一个一致性状态。为了保证事务的正确执行,维护数据库的完整性,事务必须具有以下特性:原子性、一致性、隔离性和持久性。事务一致性是指:事务必须是...
  • 上篇文章:分布式系统漫谈【玖】_分布式事务一致性:协议支持 其实对于生产环境的分布式事务一致,各大互联网公司都是自己实现的解决方案,总结起来无非是异步、补偿、实时查询、定期校对几种模式,大部分场景都...
  • 分布式事务数据一致性解决方案

    千次阅读 2020-09-03 01:01:53
    2PC 两阶段提交 由一个事务协调器器去通知各事务执行者准备事务、提交或回滚事务,它解决了数据一致性问题,但是通信时间、资源锁定时间太长,系统的可用性受到影响 利用RocketMq的消息事务机制,将生产者本地...
  • 点击上方“朱小厮的博客”,选择“设为星标”后台回复”加群“获取公众号专属群聊入口随着业务的快速发展、业务复杂度越来越高,传统单体应用逐渐暴露出了一些问题,例如开发效率低、可维护差、架构...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 426,112
精华内容 170,444
关键字:

事务一致性

友情链接: thetime.zip