事务

目录

1、ACID四大特性:

2、四种隔离级别:

3、锁

4、MVCC

6、redo/undo log


1、ACID四大特性:

  • 原子性:事务必须是原子工作单元;对于其数据修改,要么全都执行,要么全都不执行。通常,与某个事务关联的操作具有共同的目标,并且是相互依赖的。如果系统只执行这些操作的一个子集,则可能会破坏事务的总体目标。原子性消除了系统处理操作子集的可能性。实现事务的原子性,是基于日志的Redo/Undo机制
  • 一致性:事务在完成时,必须使所有的数据都保持一致状态。在相关数据库中,所有规则都必须应用于事务的修改,以保持所有数据的完整性。事务结束时,所有的内部数据结构(如 B 树索引或双向链表)都必须是正确的。某些维护一致性的责任由应用程序开发人员承担,他们必须确保应用程序已强制所有已知的完整性约束。例如,当开发用于转帐的应用程序时,应避免在转帐过程中任意移动小数点
  • 隔离性:由并发事务所作的修改必须与任何其它并发事务所作的修改隔离。事务查看数据时数据所处的状态,要么是另一并发事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看中间状态的数据。这称为可串行性,因为它能够重新装载起始数据,并且重播一系列事务,以使数据结束时的状态与原始事务执行的状态相同。当事务可序列化时将获得最高的隔离级别。在此级别上,从一组可并行执行的事务获得的结果与通过连续运行每个事务所获得的结果相同。由于高度隔离会限制可并行执行的事务数,所以一些应用程序降低隔离级别以换取更大的吞吐量
  • 持久性:事务完成之后,它对于系统的影响是永久性的。该修改即使出现致命的系统故障也将一直保持

主要说说这个描述很抽象一致性,举个例子:A向B转账,假设转账之前这两个用户的钱加起来总共是2000,那么A向B转账之后,不管这两个账户怎么转,A用户的钱和B用户的钱加起来的总额还是2000,这个就是事务的一致性

2、四种隔离级别:

  • Read Uncommitted(读取未提交内容):在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)
  • Read Committed(读取提交内容):这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别 也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果
  • Repeatable Read(可重读):这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题
  • Serializable(可串行化):这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争

这四种隔离级别采取不同的锁类型来实现,若读取的是同一个数据的话,就容易发生问题。例如:

  • 脏读(Drity Read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。
  • 不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。
  • 幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。

在MySQL中,实现了这四种隔离级别,分别有可能产生问题如下所示:

幻读和不可重复度的区别:

(1)不可重复读是读取了其他事务更改的数据,针对update操作

解决:使用行级锁,锁定该行,事务A多次读取操作完成后才释放该锁,这个时候才允许其他事务更改刚才的数据。

(2)幻读是读取了其他事务新增的数据,针对insert操作

解决:使用表级锁,锁定整张表,事务A多次读取数据总量之后才释放该锁,这个时候才允许其他事务新增数据。

3、锁

数据库使用到的锁概念:乐观锁、悲观锁、共享锁,参考:JAVA锁

  • 乐观锁:无锁机制,MVCC是乐观锁的一种实现
  • 悲观锁:写锁;
  • 共享锁:读锁;

早期数据库不论读取还是写入,都用锁来实现。但是锁会带来性能的问题。人们尝试各种优化方案。写入和读取的优化方式不同。对于数据库写入操作,没有特别好的办法,因为无论如何要避免并发修改一个数据,就得靠锁。不同的数据库对于写入操作都会加悲观锁(比如MySQL是X锁)。为了避免X锁带来的性能问题,人们在合适的场合会选择用乐观锁来优化。有的数据库内建乐观锁,但是有的没有(比如MySQL就没有),所以需要开发人员自己在数据表里加version列,自己写业务代码实现。

注意:乐观锁并不一定总是比悲观锁性能表现更好,这要看竞争的程度。如果数据访问竞争的非常厉害,乐观锁只会让CPU和IO白白浪费而已。

4、MVCC

Multi-Version Concurrency Control 多版本并发控制。

对于读取,优化就是MVCC。现在主流的商业数据库都是基于MVCC,如MySQL InnoDB和Postgres。MVCC的意思用简单的话讲就是对数据库的任何修改的提交都不会直接覆盖之前的数据,而是产生一个新的版本与老版本共存,使得读取时可以完全不加锁。这样读某一个数据时,事务可以根据隔离级别选择要读取哪个版本的数据。过程中完全不需要加锁。

MVCC只针对RC和RR,read uncommitted可以直接读脏数据,读版本链最开始的记录就可以了,不需要读旧版本信息。serializable需要对读加锁,不需要应用多版本信息。

  • Read Committed - 一个事务读取数据时总是读这个数据最近一次被commit的版本
  • Repeatable Read - 一个事务读取数据时总是读取当前事务开始之前最后一次被commit的版本(所以底层实现时需要比较当前事务和数据被commit的版本号)

例子:

  1. 一个事务A(txnId=100)修改了数据X,使得X=1,并且commit了
  2. 另外一个事务B(txnId=101)开始尝试读取X,但是还X=1。但B没有提交。
  3. 第三个事务C(txnId=102)修改了数据X,使得X=2。并且提交了
  4. 事务B又一次读取了X。这时
  • 如果事务B是Read Committed。那么就读取X的最新commit的版本,也就是X=2
  • 如果事务B是Repeatable Read。那么读取的就是当前事务(txnId=101)之前X的最新版本,也就是X被txnId=100提交的版本,即X=1

注意,这里B不论是Read Committed,还是Repeatable Read,都不会被锁,都能立刻拿到结果。这也就是MVCC存在的意义。

在基于MVCC的数据库实现中,根本就不需要出现Read Uncommitted这种情况。Read Uncommitted是早期数据库,读写都基于锁进行实现的产物。在实际业务中Read Uncommitted毫无意义(如果真有意义,你咋不去用NoSQL数据库?)因此:

  • 对于Postgres,Read Committed和Read Uncommitted是一样的。
  • 对于Oracle,压根就没有Read Uncommitted这个级别。

所以从实际的角度出发,我想所有人都忘记有“Read Uncommitted”这件事。

6、redo/undo log

https://blog.csdn.net/officiallallana/article/details/90236577

https://www.jianshu.com/p/57c510f4ec28

https://blog.csdn.net/define_us/article/details/84139912

redo:(重做)记录事务操作中对数据修改后的值,用于将事务重新执行一遍

undo:(撤销)记录事务操作中对数据修改前的值,用户将事务回滚

redo/undo log 和 binlog 的区别:

  • 层次不同。redo/undo 是 innodb 引擎层维护的,而 binlog 是 mysql server 层维护的,跟采用何种引擎没有关系,记录的是所有引擎的更新操作的日志记录。
  • 记录内容不同。redo/undo 记录的是 每个页/每个数据 的修改情况,属于物理日志+逻辑日志结合的方式(redo log 是物理日志,undo log 是逻辑日志)。binlog 记录的都是事务操作内容,binlog 有三种模式:Statement(基于 SQL 语句的复制)、Row(基于行的复制) 以及 Mixed(混合模式)。不管采用的是什么模式,当然格式是二进制的。
  • 记录时机不同。redo/undo 在 事务执行过程中 会不断的写入,而 binlog 是在 事务最终提交前 写入的。binlog 什么时候刷新到磁盘跟参数 sync_binlog 相关。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值