本地事务

事务特性: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. (int)a、&a、(int)&a、(int&)a的区别,很偏僻的题

    (int)a.&a.(int)&a.(int&)a的区别,很偏僻的题 #include <iostream> #include <stdio.h> #i ...

  2. Kubernetes集群部署笔记

    本作品由Galen Suen采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可.由原作者转载自个人站点. 概述 本文用于整理基于Debian操作系统使用kubeadm工具部署Kub ...

  3. 国内Git官网下载windows版本慢的问题,推荐一个国内镜像

    话不多说了,给你们镜像地址: https://npm.taobao.org/mirrors/git-for-windows/ 不用谢.点右边打赏个一毛钱,告诉我你来过        !_!

  4. 状态码1xx-6xx的含义

    1xx (临时响应)表示临时响应并需要请求者继续执行操作的状态代码. 100 (继续) 请求者应当继续提出请求. 服务器返回此代码表示已收到请求的第一部分,正在等待其余部分. 101 (切换协议) 请 ...

  5. Spring笔记(4)

    集成Web环境 1.步骤 导入Spring-web坐标 <!-- spring-web--> <dependency> <groupId>org.springfra ...

  6. 基于Nginx实现负载均衡的部署

    Nginx(enginex)是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP服务器. nginx官方网站:http://nginx.org/ nginx plus收费软件, ...

  7. Spring依赖注入的四种方式

    首先,我们需要定义一个Bean的class类: package framework.spring; import org.springframework.beans.BeansException; i ...

  8. 源码编译安装nginx及设置开机启动项

    1.上传nginx文档:解压到/data目录下,并安装依赖包tar xf nginx-1.20.1.tar.gz -C /data/cd /data/nginx-1.20.1/ && ...

  9. 单片机学习(十一)I2C总线和AT24C02的使用

    一. 存储器介绍 存储器分类图 1. RAM 这类存储器中的数据都是掉电即失的,例如计算机中的内存就是DRAM,但它们数据读写速度都是要比ROM要快得多的. SRAM:本质是电路,使用电路构成的触发器 ...

  10. Intel® QAT加速卡之编程demo框架

    QAT demo流程框架 示例一: 代码路径:qat1.5.l.1.13.0-19\quickassist\lookaside\access_layer\src\sample_code\functio ...