mysql事务详解

事务的四大特性

1.原子性(Atomicity)

要么全做,要么全不做的规则称之为原子性。比如说转账,要么成功要么失败.

2.一致性(Consistency)

如果数据库中的数据全部符合现实世界中的约束(all defined rules),我们说这些数据就是一致的,或者说符合一致性的。比如身份证号不能重复

更多的一致性需求需要靠写业务代码的程序员自己保证。比如说金额不能小于0 , 性别只能是男或女.

3.隔离性(Isolation)

对某些数据库操作来说,不仅要保证这些操作以原子性的方式执行完成,而且要保证其它的状态转换不会影响到本次状态转换,这个规则被称之为隔离性。

4.持久性(Durability)

转换的结果将永久的保留,这个规则被称为持久性. 也就意味味着该转换对应的数据库操作所修改的数据都应该在磁盘上保留下来

 


MySQL中事务的语法

只有InnoDB和NDB存储引擎支持事务.如果表不支持事务,那么将无法回滚. 不显式的开启事务,那么每一条sql都是一个独立的事务

开启一个事务

  • BEGIN;
  • START TRANSACTION;

START TRANSACTION语句后边跟随几个修饰符,就是它们几个:

  • READ WRITE:标识当前事务是一个只读事务
  • READ WRITE:标识当前事务是一个读写事务
  • WITH CONSISTENT SNAPSHOT:启动一致性读

提交事务

  • COMMIT

中止事务

  • ROLLBACK

事务并发执行遇到的问题

对于服务器来说可能同时处理多个事务,理论上为了完全实现隔离性我们应该让事务都排队执行,但是这样子对性能影响过大,所以只能舍弃一部分隔离性从而提高性能.

舍弃隔离性,也就是不保证事务之间串行化执行,会出现下面这些问题:

脏写

如果一个事务修改了另一个未提交事务修改过的数据,那就意味着发生了脏写,

 

脏读

如果一个事务读到了另一个未提交事务修改过的数据,那就意味着发生了脏读

A事务读了一个B事务修改后的值,但是B事务最后回滚了,相当于B的修改操作没发生,但是A却读到这个修改的值,这就是脏读.

 

不可重复读

如果一个事务能读到(另一个已经提交的事务)修改过的数据,并且其他事务每对该数据进行一次修改并提交后,该事务都能查询得到最新值,那就意味着发生了不可重复读

简单来说,就是A事务内同样的select了好多次,但是每次读到的值都不一样,都能把别的事物修改后的值立刻读出来.这就说明发生了不可重复读,按照理想情况,同一个事务内相同的语句得到的结果应该是相同的.

 

幻读

如果一个事务先根据某些条件查询出一些记录,之后另一个事务又向表中插入了符合这些条件的记录,原先的事务再次按照该条件查询时,能把另一个事务插入的记录也读出来,那就意味着发生了幻读

比如说, 在A事务内两次select id > 10, 第一次只查到了一条id=11的数据, 然后B事务又插入了一个id=12的数据, A事务第二次select id>10的时候, 把id=12的数据也查了出来, 导致出现幻读.


四种隔离级别

READ UNCOMMITTED:未提交读

只能解决脏写问题, 其它三个问题都不能解决..

 

READ COMMITTED:已提交读

可以解决脏写,脏读, 无法解决不可重复读合幻读问题.

 

REPEATABLE READ:可重复读

在mysql中,默认为该隔离级别,且该隔离级别下,不会发生并发的四种问题. 我们需要重点了解,mysql的innodb是如何在该隔离级别下,解决并发可能发生的四种问题的.

 

SERIALIZABLE:可串行化

事务排队执行


如何解决并发的四大问题

脏写发生的情况是 写-写 并发发生的情况下.

  • 如何解决脏写

通过加锁, 如果AB两个事务中都打算对同一个记录修改,那么无论哪个事务想要开始执行,都需要在内存中生成一个和这个记录对应的锁结构.后来的想要执行的事务,想要执行也需要获取该锁.(获取锁准确来说是锁结构的中is_waiting属性变成false)

 

 

  • 如何解决脏读
  • 如何解决不可重复读
  • 如何解决幻读

脏读,不可重复读,幻读 本质上都是同时有 读-写 事务并发发生的情况下出现的问题. 那么解决这三种问题的思路其实就是相同的了.

方案一:读操作利用多版本并发控制(MVCC),写操作进行加锁。

方案二:读、写操作都采用加锁的方式。

 

我们在这个文章讨论的是,如何通过方案一 来解决 脏读,不可重复读,幻读。

首先要先了解 MVCC

 


 

MVCC多版本并发控制

下面的讨论是基于mysql默认的可重复读隔离级别下做的解释.该隔离级别下同样利用mvcc读,加锁写的方式解决了幻读问题.

.首先要了解,每次对某个记录进行修改,会记录一条该事务对应的undo日志,从而形成版本链.

假设系统中有一个事务id为100的事务正执行 ,那么假设undo日志如上图所示.

# Transaction 100

BEGIN;

UPDATE hero SET name = '关羽' WHERE number = 1;

UPDATE hero SET name = '张飞' WHERE number = 1;

 

 

假设现在有一个别的事务开始执行:

# 查询不会分配事务id

BEGIN;

# SELECT1:Transaction 100未提交

SELECT * FROM hero WHERE number = 1;

 

这个SELECT1的执行过程如下:

 

  1. 执行select的时候,生成一个readView,这个readVIew的m_ids列表的内容就是[100](当前系统正在活跃的事务id列表)

 

  1. 从版本链中第一个开始查找,发现name的值是张飞, 但是这个记录对应事务id为100,在

m_ids列表内,所以不符合要求,往下遍历.

 

  1. 当遍历到name=刘备这条记录的时候,发现该记录的事务id=80,小于m_ids列表最小值100,所以返回的结果就是name=刘备这个记录.

 

在可重复读隔离级别下 , 在该例子中的select所在的事务里,即便有多个select, 也只会使用第一次select生成的readView, 通过这种方式就保证了,无论有多少次select,中间插入再多别的事务的对这个记录的修改,或者是插入符合的记录, 最终select的结果都是和第一次select一样.利用这种方式同时解决了脏读,不可重复读,幻读问题.

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值