InnoDB存储引擎中的事务

下面对innodb存储引擎中的事务部分做一个简单的总结:

1、什么是事务,事务有哪些与传统的文件操作不一样的地方?

事务是与传统的文件系统不一样的地方,事务可以保证数据从一种状态变成另外一种状态,数据库提交的时候,可以确保要么所有的修改都已经修改了,要么所有的都没有修改。这更加符合我们的实际生活,对用户来说,事务操作更加符合,向银行转账,对于用户来说就是从A转到了B一笔钱,这就是一个事物,其中包含了其他的操作,但是用户不关心,每次交易的都是这样的一种事务。

2、数据库的事务必须保证的4个原则

原子性:事务的操作要么成功,要么失败,没有其他的状态
一致性:事务将数据库从一种状态变成另外一种状态,事务结束后,数据库的完整性约束没有破坏。
隔离性:一个事务提交之前对其他的事务是不可见的。
持久性:事务一旦提交,其结果就是永久性的。

3、那么数据库的事务必须保证的4个原则是怎么实现的呢?只有实现了,才可以说这个数据库是支持事务性操作的。
原子性:

通过操作日志来完成,一旦事务失败,则通过回滚操作日志的方式来保证原子性的有效

一致性:

通过操作日志来完成,如果某一个事务提交的实时,发现前后的约束条件不一致,例如一笔交易完成之后,前后的总的价钱发生了改变,不满足一致性条件,该事务提交失败,这个时候数据库会进行回滚操作

隔离性:

对于上面锁的时候提到了事务读取的时候可能发生的3中错误,数据库设置不同的隔离性,对这三种错误有不同的容忍条件,分别对应不同的隔离条件。

隔离的等级:

不同的等级对于隔离的容忍是不一致的。从低到高:

Read uncommitted:

最低隔离级别,什么锁都需要加,这个时候对于脏读,不可重复读,幻读都有可能发生。

Read committed:

通过加行锁,解决脏读的问题,加锁的过程中,其他事物不能对读的这行进行操作。但是不能解决不可重复读和幻读。也可以用MVCC来实现

repeaetable read:

通过加next-key lock锁,锁住一段的范围以及该行本身,这样可以解决脏读,不可重复读,幻读,也可以用MVCC来实现

Serializable read:

事物串行话执行,完全牺牲并发,串行话没有并发可以避免解决脏读,不可重复读,幻读

对于Read committed+repeaetable read和两个级别,为了更好的并发,实际数据库中会使用乐观锁,即MVCC来保证读的时候不加锁,其他时候加锁,减少加锁的次数,并且也能保证只会读取到符合标准的行。

对于Serializable read:直接利用悲观锁来实现的。

4、除了这些基本加锁的方式,MySql中为了更好的并发,使用的更多的是MVCC多版本控制的方法来实现上述的隔离性。
InnoDB 中事务隔离性的实现:

READ COMMITED 和 REPEATABLE READ 的隔离性实现:MVCC

MVCC(多版本控制系统)的实现(目的: 实现更好的并发,可以使得大部分的读操作不用加锁, 但是insert,delete,update是需要加锁的):

MVCC 只在 READ COMMITED 和 REPEATABLE READ 这两个事务隔离性级别中使用。这是因为MVCC 和其他两个不兼容,READ UNCOMMITED 总是读取最新的行,不关事务, 而Seriablizable则会对每一个读都加共享锁。

在InnoDB中,会在每行数据后添加两个额外的隐藏的值来实现MVCC,这两个值一个记录这行数据何时被创建,另外一个记录这行数据何时过期(即何时被删除)。 在实际操作中,存储的并不是时间,而是系统的版本号,每开启一个新事务,系统的版本号就会递增。

通过MVCC,虽然每行记录都需要额外的存储空间,更多的行检查工作以及一些额外的维护工作,但可以减少锁的使用,大多数读操作都不用加锁,读数据操作很简单,性能很好,并且也能保证只会读取到符合标准的行,也只锁住必要行。


select (不加锁): 满足两个条件的结果才会被返回:

  1. 创建版本号<= 当前事务版本号,小于意味着在该事务之前没有其他事务对其进行修改,等于意味着事务自身对其进行了修改;
  1. 删除版本号 > 当前事务版本号 意味着删除操作是在当前事务之后进行的,或者删除版本未定义,意味着这一行只是进行了插入,还没有删除过。
    INSERT ; 为新插入的每一行保存当前事务的版本号作为创建版本号
    DELETE ; 为删除的行保存当前事务的版本号为删除版本号
    UPDATE; 为修改的每一行保存当前事务的版本号作为创建版本号
“读”与“读”的区别

MySQL中的读,和事务隔离级别中的读,是不一样的, 在REPEATABLE READ 级别中,通过MVCC机制,虽然让数据变得可重复读,但我们读到的数据可能是历史数据,是不及时的数据(存储在缓存等地方的数据),不是数据库当前的数据!这在一些对于数据的时效特别敏感的业务中,就很可能出问题。

对于这种读取历史数据(缓存数据)的方式,我们叫它快照读 (snapshot read),而读取数据库当前版本数据的方式,叫当前读 (current read)。很显然,在MVCC中:

快照读:就是select ,是不加锁的, 在REPEATABLE READ 和READ COMMITED 级别中 select语句不加锁。

select * from table ….; 
当前读:插入/更新/删除操作,属于当前读,处理的都是当前的数据,需要加锁。 
select * from table where ? lock in share mode; 
select * from table where ? for update; 
insert; 
update ; 
delete; 

事务的隔离级别实际上都是定义了当前读的级别,MySQL为了减少锁处理(包括等待其它锁)的时间,提升并发能力,引入了快照读的概念,使得select不用加锁。而update、insert这些“当前读”,就需要另外的模块来解决了。(这是因为update、insert的时候肯定要读取数据库中的值来与当前事务要写入的值进行对比,看看在该事务所处理的数据在这一段时间内有没有被其他事务所操作(就是先读取数据库中数据的版本号与当前的版本号做检查))

为了解决当前读中的幻读问题,MySQL事务使用了Next-Key锁。
Next-Key锁是行锁 和 GAP(间隙锁)的合并

GAP(间隙锁)就是在两个数据行之间进行加锁,防止插入操作

行锁防止别的事务修改或删除,解决了数据不可重复读的问题

行锁防止别的事务修改或删除,GAP锁防止别的事务新增,行锁和GAP锁结合形成的的Next-Key锁共同解决了RR级别在读数据时的幻读问题

InnoDB 中 Serializable 的隔离性实现

Serializable级别使用的是悲观锁的理论, 读加共享锁,写加排他锁,读写互斥, 在Serializable这个级别,select语句还是会加锁的。

参考博客:
https://blog.csdn.net/woshiluoye9/article/details/68954515

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值