文章详情

短信预约信息系统项目管理师 报名、考试、查分时间动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

MySQL的本地事务、全局事务、分布式事务

2019-01-19 16:01

关注

MySQL的本地事务、全局事务、分布式事务

本地事务

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

实现隔离性

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

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

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

隔离级别

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

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

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

幻读是指在事务执行过程中,两个完全相同的范围查询得到了不同的结果集。譬如现在准备统计一下 Fenix"s Bookstore 中售价小于 100 元的书有多少本,会执行以下第一条 SQL 语句:
    
SELECT count(1) FROM books WHERE price < 100					
INSERT INTO books(name,price) VALUES ("深入理解Java虚拟机",90)	   		
SELECT count(1) FROM books WHERE price < 100					

两次执行之间有另外一个事务在数据库插入了一本小于 100 元的书籍,那这两次相同的查询就会得到不
一样的结果,原因是可重复读没有范围锁来禁止在该范围内插入新的数据,这是一个事务受到其他事务影响,隔离性被破坏的表现。

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

读已提交:有脏读问题。

MVCC

MVCC是并发访问控制技术,解决了读写冲突问题(幻读)。

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

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

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

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

MVCC超售问题

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

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

SELECT quantity FROM books WHERE id=1					
SELECT quantity FROM books WHERE id=1					


UPDATE books SET quantity=2 WHERE id=1					
commit


UPDATE books SET quantity=2 WHERE id=1					
commit

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

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

也正是为了解决这类情况,InnoDB之类采用MVCC的引擎,都会提供诸如“lock in share mode”的语法,让开发者在“写-写”的场景中显式加共享锁,让数据库进行当前读而非快照读

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

SELECT quantity FROM books WHERE id=1 lock in share mode;		
SELECT quantity FROM books WHERE id=1 lock in share mode;		

全局事务

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 将事务提交拆分成为两阶段过程:

缺点

3PC

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

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

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

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

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

分布式事务

CAP与ACID

柔性事务与最终一致性。

可靠事件队列

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

TCC事务

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

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

Reference

《凤凰架构》

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-数据库
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯