一、事务需要遵循四个基本原则:

1、原子性:一个事务要不全部执行完要不都不执行

2、一致性:事务执行前后没有破坏数据完整性,从一种正确状态转换到另一种正确状态,比如A给B转账,先给A扣款再给B账户增加两个事情保持一致。

3、隔离性:在一定条件下多个事务之间相互隔离互不影响

4、持久性:事务执行完后就要保存进数据库不会丢失

二、事务隔离级别就是针对第三个隔离性

在说隔离级别之前先说一下几个相关的名词:

1、读未提交:读取到事务没有提交的内容,比如在A事务执行同时B事务在修改数据,A事务读取的是B修改之前的数据状态就会出问题,是一个隔离级别。

2、读已提交:跟第一个正好对应,A读取的是B提交之后的数据状态,是一个隔离级别

3、不可重复读:不可重复读是在一个事务中重复读取同一条数据产生差别的问题,并不是一个隔离级别只是产生的一个错误,很多文章中都说这是一个隔离级别是错的,是一个错误状态

4、可重复读:跟第三个对应,在一个事务中可以重复读取相同数据,并且数据状态不变,是一种隔离级别。

5、脏读:是跟读未提交向对应的错误状态,是一个错误状态

6、幻读:在一个事务执行过程中有新的数据行插入导致多次读取数据条数不一样,但是每次读取已有数据状态没有变,所以产生错误,是一种错误状态。

7、串行化:将所有对同一个数据的操作加锁串行执行,是一个隔离级别

隔离级别

脏读

不可重复读

幻读

读未提交




读已提交

没有



可重复读

没有

没有


串行化

没有

没有

没有

三、mysql事务隔离实现原理

1、快照读和当前读

快照读:读取的是快照版本,也就是历史版本

当前读:读取的是最新版本

普通的SELECT就是快照读,而UPDATE、DELETE、INSERT、SELECT ...  LOCK IN SHARE MODE、SELECT ... FOR UPDATE是当前读。

2、mysql事务中锁的种类和理解

有这样三种锁我们需要了解

  • Record Locks(记录锁):在索引记录上加锁,行锁。
  • Gap Locks(间隙锁):在索引记录之间加锁,或者在第一个索引记录之前加锁,或者在最后一个索引记录之后加锁,用来防止幻读。
  • Next-Key Locks:在索引记录上加锁,并且在索引记录之前的间隙加锁。它相当于是Record Locks与Gap Locks的一个结合。

假设一个索引包含以下几个值:10,11,13,20。那么这个索引的next-key锁将会覆盖以下区间:

(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)

3、MVCC原理

事务隔离有三种实现方式:加锁,时间戳,多版本和快照隔离(MVCC)

mysql使用的是MVCC,MVCC是行级锁的一个变种,但是它在很多情况下避免了加锁操作,因此开销更低,虽然实现机制有所不同,但大都实现了非阻塞的读操作,写操作也只锁定必要的行.

    MVCC的实现是通过保存数据在某个时间点的快照来实现的,也就是说,不管需要执行多长时间,只要事务开始时间相同,每个事务看到的数据都是一致的,事务开始的时间不同时,每个事务对同一张表,同一时刻看到的数据可能是不一样的(因为不同的时间点可能数据就已经产生了不同的快照版本,而每个事务在默认的RR隔离级别下只能看到事务开始时的数据快照)

在InnoDB中,给每行增加两个隐藏字段来实现MVCC,一个用来记录数据行的创建时间,另一个用来记录行的过期时间(删除时间)。在实际操作中,存储的并不是时间,而是事务的版本号,每开启一个新事务,事务的版本号就会递增。

于是乎,默认的隔离级别(REPEATABLE READ)下,增删查改变成了这样:

  • SELECT
  • 读取创建版本小于或等于当前事务版本号,并且删除版本为空或大于当前事务版本号的记录。这样可以保证在读取之前记录是存在的。
  • INSERT
  • 将当前事务的版本号保存至行的创建版本号
  • UPDATE
  • 新插入一行,并以当前事务的版本号作为新行的创建版本号,同时将原记录行的删除版本号设置为当前事务版本号
  • DELETE
  • 将当前事务的版本号保存至行的删除版本号