什么是事务?

举个例子(没错,就是那个被举烂了的取钱例子):
A转账B一百块;
分为两个步骤:

  1. A账户扣款一百块;
  2. B账户增加一百块;
    这两个步骤要按照顺序,且要保证都要完成。
    那么:
    A账户转账完成以后,停电了怎么办?B并没有收到钱;
    A账户转账一百块,账户对应减少一百块,那B账户只增加六十块怎么办?

事务的英文名称是Transition,本身就是交易的意思,最初的事务机制就是从为了解决这类问题而开发的。
分布式——我理解的事务
从字面意思理解的话,相信大家多多少少扫一眼就能大体明白,那么我们举个例子详细介绍对应的特点以及应用的场景:
原子性理解:
原子性是从交易执行的过程的角度来审视的(我只关心这转账这个两个动作是不是顺序且都完成的状态?至于你转了多少我并并不知道,我也不care);
一致性理解:
一致性是从交易执行的结果这样的业务逻辑的角度来审视的(还是上面那个转账的例子,如何判断是否一致?那么就是A少了一百块,B多了一百块);
隔离性的理解:
关于隔离性的理解,我们先了解一下会涉及到的一些名词:

脏读:脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。
不可重复读:在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。
幻读:第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。

事物的执行过程中总是并发的,如果在A转给B一百块了以后,C又给B转了一百块,此时B多了两百块,那我们如何判断A和B之间完成了一次预期中的交易?(肯定有人说要看看转账记录啊…)

事务的执行肯定是并发的,多个事务之间互相隔离的程度就是隔离性的体现。隔离性的实现是通过锁机制实现的,最简单粗暴的是排它锁,但排它锁的效率很低,而我们的业务大多数时候并不需要非常强的隔离性,读到脏数据并没有什么问题,在这样的前提下,就发展出了各种颗粒度的锁。

分布式——我理解的事务
Read Uncommited只会在写入的时候添加行级共享锁,并且马上释放,无法避免以上任何问题。
Read Commited从名字看就知道是在整个事务提交后才释放锁,事务会在读到某行时添加行级共享锁,读完后马上释放,在修改某行时添加行级排他锁,并在事务提交时释放。这样在事务执行过程中其他事务无法读取被本事务修改的数据就解决了赃读问题,但因为读取时添加的是共享锁,无法解决不可重复读的问题。
Repeatable Read就是为了解决不可重复读出现的,事务会在读某行时添加行级共享锁,写某行时添加行级排他锁,并都在事务提交时释放,这样在整个事务过程中其他事务只可能读到一个版本的数据。
Serializable:从名字也可以看出来,在读取数据时添加表级共享锁,在更新数据时添加表级排它锁,并都在事务结束时释放,这种加锁颗粒度完全不考虑并行,因此叫Serializable。

为什么要加锁?隔离的本质是什么?
隔离性的实现实际就是加锁,不过在加锁时间、加锁范围做了不同的控制,这里还有很多细节问题,比如读写锁怎么实现,表/行锁互相冲突怎么实现,死锁问题怎么解决?

应用场景(数据库的JDBC):
在java中,我们的业务系统一般都有个Resource Manager,通过Resource Manager来做事务管理。Resource Manager会管理数据库的资源。Resource Manager会通过代理等方式调用具体的事务管理的实现类来进行事务管理。也就是通过JDBC Driver来做事务管理。

原子性/持久性的实现

原子性实现使用Undo/Redo Log,核心思想是既然执行过程中会发生意外,那么就将过程记录下来并存储在磁盘中,这样当过程被打断了,可以使用磁盘中的记录数据进行重试或者回滚,对应的实现就是Redo/Undo log。

Undo Log用于存放数据被修改前的值,假设需要将某张表中的id=2的数据里name=‘B’修改为B2,Undo Log就会将name='B’记录下来,在发生意外终止后恢复,undo log 分为UPDATE_UNDO、INSERT_UNDO。

Redo Log记录修改后的记录,数据库在修改数据时需要将数据页从磁盘读到buffer pool中,然后修改,这样造成的不一致称为dirty page,如果这个时候发生重启数据就会丢失,因此需要将这些变更写入到一个文件中暂存,保证数据不丢,数据库为Undo Log分配了一篇连续的空间保证写入的性能。

那么说到这里(BB了这么多枯燥的话),总结一下对于事物的理解:
在事务的ACID中,一致性才是最终诉求。
怎么理解这句话?在实际上生活中:
用户在转账完成以后,最关心的问题是什么?那就是最终的结果:
A的钱是不是少了一百?且B是不是多了一百?(其实大家在转账的时候只希望对方多了一块就够了 是吧?哈哈)这就是一致性。
那么要想达成一致性,那么需要哪些东西作为基础?
原子性首先要实现的吧?因为这个是最基础的。
那么其中有别人转账,并发了怎么判断结果?是不是需要隔离避免脏数据影响判断?
要想这笔钱转过去以后确实是生效了,以后的每天B查询自己的账户确实是多了一百块,是不是需要持久性来保证?
我认为这ACID之间是存在逻辑上的关系的、是密切相关的、是缺一不可的。
传统的单机架构中,事物的使用通过关系型数据库可以直接体现。那么在分布式的结构中,这种事物还能适用么?

相关文章: