本地事务

事务特性:ACID,其中C一致性是目的,AID是手段。

实现隔离性

写锁:数据加了写锁,其他事务不能写也不能读。

读锁:数据加了读锁,其他事务不能加写锁可以加读锁,可以允许自己升级为写锁。

范围锁:对某个范围加写锁,范围内数据不能写入。

隔离级别

以锁为手段来实现隔离性才是数据库表现出不同隔离级别的根本原因。

可串行化:对事务所有读、写数据加上三种锁。

可重复读:不加范围锁,会有幻读问题。

幻读是指在事务执行过程中,两个完全相同的范围查询得到了不同的结果集。譬如现在准备统计一下 Fenix's Bookstore 中售价小于 100 元的书有多少本,会执行以下第一条 SQL 语句:

SELECT count(1) FROM books WHERE price < 100					/* 时间顺序:1,事务: T1 */
INSERT INTO books(name,price) VALUES ('深入理解Java虚拟机',90) /* 时间顺序:2,事务: T2 */
SELECT count(1) FROM books WHERE price < 100 /* 时间顺序:3,事务: T1 */ 两次执行之间有另外一个事务在数据库插入了一本小于 100 元的书籍,那这两次相同的查询就会得到不一样的结果,原因是可重复读没有范围锁来禁止在该范围内插入新的数据,这是一个事务受到其他事务影响,隔离性被破坏的表现。

读已提交:写锁会一直持续到事务结束,读锁在查询操作完成后马上释放。有不可重复读问题,读已提交的隔离级别缺乏贯穿整个事务周期的读锁,无法禁止读取过的数据发生变化。

读已提交:有脏读问题。

MVCC

基本思路是对数据库的修改不会直接覆盖之前的数据,而是产生一个新版副本与老版本共存。

“版本”可以理解为每一行记录存在俩看不见的字段:CREATE_VERRSION和DELETE_VERSION,这两个字段都是事务ID,事务ID是全局递增的值,根据以下规则写入数据。

  • 插入数据:CREATE_VERRSION记录插入数据的事务ID,DELETE_VERSION为空。
  • 删除数据:DELETE_VERSION记录删除数据的事务ID,CREATE_VERRSION为空。
  • 修改数据:将修改数据视为“删除旧数据,插入新数据”,将原有数据复制一份,原有数据DELETE_VERSION记录修改数据的事务ID。复制出来的新数据CREATE_VERSION记录修改数据的事务ID。

此时,有另一个事务读取这些发生了变化的数据,根据隔离级别决定读取哪个版本的数据。

  • 可重复读:在“总是读取CREATE_VERSION小于等于当前事务ID的数据”前提下,如果数据有多个版本,读取事务ID最大的。
  • 读已提交:总是读取最新版本,即最近被Commit版本的数据。

MVCC是针对“读+写”的优化,“写+写”只能加锁解决。竞争激烈的情况下,乐观锁可能更慢。

MVCC超售问题

数据库采用的是MVCC方案,是否有可能出现以下这种超售情况

初始quantity值为10,事务: T1和事务: T2都想要将quantity减8

SELECT quantity FROM books WHERE id=1					/* 时间顺序:1,事务: T1 */
SELECT quantity FROM books WHERE id=1 /* 时间顺序:2,事务: T2 */ /*事务: T1 运算后将quantity改为2 因为MVCC方案中事务2的select并不会加读锁,所以这条语句可以顺利执行并commit*/
UPDATE books SET quantity=2 WHERE id=1 /* 时间顺序:3,事务: T1 */
commit /*事务: T2 运算后将quantity改为2 ,此时没有其他的事务了,所以这条语句可以顺利执行并commit*/
UPDATE books SET quantity=2 WHERE id=1 /* 时间顺序:4,事务: T2 */
commit

这种写法会出现超售,相当于卖了两次8本书。

之前提到过,MVCC只解决“读-写”事务的情况(也就是解决可重复读级别下的幻读),在“写-写”的场景中它是不适用的。

也正是为了解决这类情况,InnoDB之类采用MVCC的引擎,都会提供诸如“lock in share mode”的语法,让开发者在“写-写”的场

景中显式加共享锁,让数据库进行当前读而非快照读

以MySQL为例,把代码修改为这样,它就可以保证T1的Update语句被T2的共享锁阻塞了,达到避免超售的目的了。

SELECT quantity FROM books WHERE id=1 lock in share mode;		/* 时间顺序:1,事务: T1 */
SELECT quantity FROM books WHERE id=1 lock in share mode; /* 时间顺序:2,事务: T2 */

全局事务

2PC

假如你平时以声明式事务来编码,那它与本地事务看起来可能没什么区别,都是标个@Transactional注解而已,但如果以编程式事务来实现的话,就能在写法上看出差异,伪代码如下所示:
public void buyBook(PaymentBill bill) {
userTransaction.begin();
warehouseTransaction.begin();
businessTransaction.begin();
try {
userAccountService.pay(bill.getMoney());
warehouseService.deliver(bill.getItems());
businessAccountService.receipt(bill.getMoney());
userTransaction.commit();
warehouseTransaction.commit();
businessTransaction.commit();
} catch(Exception e) {
userTransaction.rollback();
warehouseTransaction.rollback();
businessTransaction.rollback();
}
}
从代码上可看出,程序的目的是要做三次事务提交,但实际上代码并不能这样写,试想一下,如果在
businessTransaction.commit()中出现错误,代码转到catch块中执行,此时userTransaction和
warehouseTransaction已经完成提交,再去调用rollback()方法已经无济于事,这将导致一部分数据被提
交,另一部分被回滚,整个事务的一致性也就无法保证了。

为了解决这个问题,XA 将事务提交拆分成为两阶段过程:

  • 准备阶段:又作投票阶段,协调者询问事务的所有参与者是否准备好提交,参与者如果准备好提交则回复Prepared。对于数据库来说,准备操作是在重做日志中记录全部事务提交操作所要做的内容,不释放隔离性,继续持有锁。
  • 提交阶段:又作执行阶段,协调者在上一阶段收到Prepared消息,先自己在本地持久化事务状态为Commit,操作完成之后向所有参与者发送Commit指令;否则,任意一个参与者回复了 Non-Prepared 消息,或任意一个参与者超时未回复,协调者将将自己的事务状态持久化为 Abort 之后,向所有参与者发送 Abort 指令,参与者立即执行回滚操作。

缺点

  • 单点问题:协调者在两段提交中具有举足轻重的作用,协调者等待参与者回复时可以有超时机制,允许参与者

    宕机,但参与者等待协调者指令时无法做超时处理。一旦宕机的不是其中某个参与者,而是协调者的话,所有

    参与者都会受到影响。如果协调者一直没有恢复,没有正常发送 Commit 或者 Rollback 的指令,那所有参与

    者都必须一直等待。

  • 性能问题:两段提交过程中,所有参与者相当于被绑定成为一个统一调度的整体,期间要经过两次远程服务调

    用,三次数据持久化(准备阶段写重做日志,协调者做状态持久化,提交阶段在日志写入 Commit

    Record),整个过程将持续到参与者集群中最慢的那一个处理操作结束为止,这决定了两段式提交的性能通

    常都较差。

  • 一致性风险

3PC

三段式提交把原本的两段式提交的准备阶段细分为两个阶段,分别称为CanCommit、PreCommit,提交阶段改为

DoCommit阶段。CanCommit是询问阶段,协调者让每个参与者根据自身状态评估事务是否可能完成。

将准备阶段一分为二的理由是:协调者发出开始准备的消息,参与者开始写重做日志,如果此时,某一个参与者宣布无法完成,相当于大家做了一轮无用功。

因此,在事务需要回滚的场景中,三段式的性能通常是要比两段式好很多的,但在事务能够正常提交的场景中,两者的性能都依然很差,甚至三段式因为多了一次询问,还要稍微更差一些。

同样也是由于事务失败回滚概率变小的原因,在三段式提交中,如果在 PreCommit 阶段之后发生了协调者宕机,即参与者没有能等到 DoCommit 的消息的话,默认的操作策略将是提交事务而不是回滚事务或者持续等待,这就相当于避免了协调者单点问题的风险。

分布式事务

CAP与ACID

柔性事务与最终一致性。

可靠事件队列

最大努力一次提交:将最有可能出错的业务以本地事务的方式完成后,采用不断重试的方式促使分布式事务中的其他关联业务全部完成。

TCC事务

如果业务需要隔离,该方案天生适合用于需要强隔离性的分布式事务中。

TCC 较为烦琐,它是一种业务侵入式较强的事务方案,要求业务处理过程必须拆分为“预留业务资源”和“确认/释放消费资源”两个子过程。如同 TCC 的名字所示,它分为以下三个阶段。

  • Try:尝试执行阶段,完成所有业务可执行性的检查(保障一致性),并且预留好全部需用到的业务资源(保障隔离性)。
  • Confirm:确认执行阶段,不进行任何业务检查,直接使用 Try 阶段准备的资源来完成业务处理。Confirm 阶段可能会重复执行,因此本阶段所执行的操作需要具备幂等性。
  • Cancel:取消执行阶段,释放 Try 阶段预留的业务资源。Cancel 阶段可能会重复执行,也需要满足幂等性。

资料

凤凰架构

MySQL的本地事务、全局事务、分布式事务的更多相关文章

  1. petapoco定制,比较SQL事务,存储过程,分布式事务(MSDTC)的区别和场景

    使用分布式事务时 就锁死了,而且是只锁编辑的行 使用.netSQL事务一定要执行了一个CUD的SQL才会锁死,而且也是锁行,但是也锁读的行 .netSQL事务要在这里才锁死 结论,对于产品要求细粒度的 ...

  2. spring + ibatis 多数据源事务(分布式事务)管理配置方法(转)

    spring + ibatis 多数据源事务(分布式事务)管理配置方法(转) .我先要给大家讲一个概念:spring 的多数据源事务,这是民间的说法.官方的说法是:spring 的分布式事务.明白了这 ...

  3. 全局事务/分布式事务 (Global Transaction/ A distributed transaction)之我见

    这里参考的是Oracle对于XA的支持,其他的应该雷同吧... 1个分布式事务由多个行为在不同的数据库上执行,1个分布式事务的执行成功意味着相关数据库上的行为执行均成功.“XA协定”(http://w ...

  4. EF 事务(非分布式事务)

    在EF 中怎么使用事务? 这个问题纠结了我好久,直到有人跟我一起讨论,我和同事一起讨论查资料. 查的好多资料都是使用 TransactionScope,用 TransactionScope 可处理分布 ...

  5. springboot(整合事务和分布式事务)

    springboot +mybatis 单数据源,事务 事务:简单理解指的是一组操作,里面包含许多个单一的逻辑,只要有一个逻辑没有执行成功 ,那么都算失败.所有的数据都回归到最初的状态(回滚) 代码实 ...

  6. 分布式事物(同样适用于dubbo事务等分布式事务)

  7. php + mysql 分布式事务(转)

    事务(Transaction)是访问并可能更新数据库中各种数据项的一个程序执行单元: 事务应该具有4个属性:原子性.一致性.隔离性.持续性 原子性(atomicity).一个事务是一个不可分割的工作单 ...

  8. mysql 分布式事务

    php + mysql 分布式事务 事务(Transaction)是访问并可能更新数据库中各种数据项的一个程序执行单元: 事务应该具有4个属性:原子性.一致性.隔离性.持续性 原子性(atomicit ...

  9. php + mysql 分布式事务

    事务(Transaction)是访问并可能更新数据库中各种数据项的一个程序执行单元: 事务应该具有4个属性:原子性.一致性.隔离性.持续性 原子性(atomicity).一个事务是一个不可分割的工作单 ...

  10. MySQL数据库分布式事务XA优缺点与改进方案

    1 MySQL 外部XA分析 1.1 作用分析 MySQL数据库外部XA可以用在分布式数据库代理层,实现对MySQL数据库的分布式事务支持,例如开源的代理工具:ameoba[4],网易的DDB,淘宝的 ...

随机推荐

  1. mybatis传入参数为0被误认为是空字符串的解决方法

    在mbatis中使用Xml配置sql语句时,出现了这样一个问题.当我传入的参数为0去做判断时,mybatis会把参数0当成是空字符串去判断而引起查询结果错误 所以在做项目时一定要注意,用到MyBati ...

  2. vue组件里不用的css还在搜索过滤来删除?试一下vue-clearcss吧!

    这篇文章其实是推广介绍我个人的npm工具库,但你不会后悔点进来的(应该吧...)vue-clearcss 为什么要用它? 一个vue文件在长期迭代中css会越来越冗余,它不像html和js那么好删除, ...

  3. MySQL-LSN

    查看lsn:   show engine innodb status Log sequence number 2687274848548    Log flushed up to 2687274848 ...

  4. DorisDB升级为StarRocks,全面开源!

    今天被朋友圈刷屏了,StarRocks开源--携手未来,星辰大海! 原文链接:StarRocks开源--携手未来,星辰大海! 可能大家对StarRocks不太熟悉,但是DorisDB想必都是听说过的. ...

  5. Python3实现打格点算法的GPU加速

    技术背景 在数学和物理学领域,总是充满了各种连续的函数模型.而当我们用现代计算机的技术去处理这些问题的时候,事实上是无法直接处理连续模型的,绝大多数的情况下都要转化成一个离散的模型再进行数值的计算.比 ...

  6. JS_DOM操作之操作标签

    <标签名 属性1="属性值1" 属性2="属性值2"-->文本</标签名> 1 - 文本操作 <div class="c ...

  7. 20201219 u,v,w

    开考前刚起床,所以一边考一边吃饭,然后整场都很迷... A. u 考场 半天才搞懂"下三角区域"指哪个区域,手模样例确认后打了 \(O(qn^2)\) 的裸暴力,然后就不会做了. ...

  8. openswan协商流程之(七):main_inR3

    主模式第六包(收包):main_inR3 1. 序言 main_inR3()函数是ISAKMP协商过程中第一阶段的最后一个报文的接收处理函数,它的作用同main_inI3_outR3()部分功能相同: ...

  9. 斐波那契数(Java)

    斐波那契数,通常用 F(n) 表示,形成的序列称为 斐波那契数列 .该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和.也就是: F(0) = 0,F(1) = 1 F(n) = F(n ...

  10. npm 设置同时从多个包源加载包的方法

    随着前后端分离技术的发展成熟,越来越来越多的后台系统甚至前端系统采用前后端分离方式,在大型前后端分离系统中,前端往往包含大量的第三方js 包的引用,各个第三方包又可能依赖另外一个第三方包,因此急需要一 ...