mysql数据库事务隔离级别

一.事务的四个特征(ACID)

  • 原子性(Atomicity): 事务不可分割,要么全部发生,要么全部不发生。
    eg:A给B转钱,A少了钱,B多了钱。两个都要发生,不能只发生一个。

  • 一致性(Consistency): 事务操作前后的状态要一致。
    eg:A给B转钱200,A少了200,B多了200,不能多300或者其它数字。

  • 隔离性(Isolation): 事务之间不能影响,相互隔离。
    eg:A给B转钱和C给B转钱是分别两个事务,不能相互影响。

  • 持久性(Durability): 事务被提交到数据库中了,就是永久的。数据库发生故障也不应该对它有影响.
    eg:A给B转钱200,事务提交后,B多了200,数据库故障了再回复后B依然要是多200。

二.事务隔离级别

在这里插入图片描述
常用命令:

  1. 查看当前session数据库隔离级别 (@代表用户自定义变量,@@代表系统变量 (mysql @和@@))
	select @@transaction_isolation;  
  1. 修改当前session数据库隔离级别
	set session transaction isolation level xxx;
  1. 开始事务
	start transaction;
  1. 提交事务
	commit;
  1. 关闭事务自动提交
	set autocommit = 0;
1.Read uncommitted(读未提交)

所有事务都可以看到其他事务未提交的事务的执行结果。
如A,B事务,B事务插入一条数据,未提交,A事务就可以读到该未提交的数据,如果B事务回滚后,该未提交的数据不存在了,A事务刚刚读到的数据就是’脏数据’,这一现象也叫脏读

事务A事务B
1.设置事务隔离等级为read uncommited在这里插入图片描述
2.开启事务,插入一条数据并且不提交事务在这里插入图片描述
3.未提交读隔离等级下读到了该未提交的数据在这里插入图片描述
4.将刚刚未提交的数据回滚在这里插入图片描述
5.刚刚读到的数据消失(读到了脏数据)在这里插入图片描述
2.Read committed(读已提交)

大多数的数据库默认隔离等级为此等级,如Oracle、Sql Server。该隔离等级下读不到未提交的事务的执行结果,不会发生脏读
但是如果A开启了事务,第一次读到了数据,在这个时候,事务B修改了这条数据并提交了事务,事务A再读,发现读到了事务B修改的数据结果,这样导致了一个事务内读同一条数据是不同的结果,这种现象就叫做不可重复读

事务A事务B
1.设置事务隔离级别为read committed在这里插入图片描述
2.开启事务读取到一条数据 在这里插入图片描述
3.开启事务将该条数据修改并提交在这里插入图片描述
4.一个事务下读取相同数据,数据却发生了变化(不可重复读)在这里插入图片描述
3.Repeatable read(可重复读)

mysql默认采用该隔离级别.该隔离级别下,在一个事务内,即使有事务修改了数据并且提交,它也不会读到刚刚修改的数据,而是原来的数据,不会发生不可重复读
但是如果A开启事务,第一次读到了数据,在这个时候B开启事务,插入了一条数据,事务A再次读取数据,会发现刚刚插入的数据也被读取到了,导致一个事务内读取的数据数量发生了变化,像是产生了幻觉一样,这种现象就叫幻读。(幻读不可重复读有点类似,不同的是幻读主要是做增加或者删除操作,产生了新数据,数据数量发生了变化。不可重复读则是做修改操作,是原来的某条数据发生了变化)

事务A事务B
1.mysql默认隔离级别为repeatable read
在这里插入图片描述
2.开启事务,读取数据
在这里插入图片描述
3.开启事务,插入一条数据,并提交事务在这里插入图片描述
4.再次读取数据,发现并没有读到刚刚插入的数据在这里插入图片描述
这里并没有发生幻读,原因之后再解释
5.加锁的方式再次读取数据,读到了刚刚插入的数据(幻读)
在这里插入图片描述
4.Serializable(可串行化)

可串行化是最高的事务隔离等级。事务隔离等级其实就是并发与事务的权衡。在这个隔离等级下,事务很严格,每个操作都会加锁,不会出现脏读不可重复读幻读的现象。虽然效率可能低一点但是用于事务性很强的情况,比如银行业务。

事务A事务B
1.设置事务隔离等级为Serializable
在这里插入图片描述
2.开启事务,读取数据
在这里插入图片描述
3.开启事务,插入一条数据,发现执行插入的sql语句被阻塞(数据插入不了,即解决了幻读)
在这里插入图片描述

三.深入理解事务隔离

1.基本概念
①版本链

Internally, InnoDB adds three fields to each row stored in the database. A 6-byte DB_TRX_ID field indicates the transaction identifier for the last transaction that inserted or updated the row. Also, a deletion is treated internally as an update where a special bit in the row is set to mark it as deleted. Each row also contains a 7-byte DB_ROLL_PTR field called the roll pointer. The roll pointer points to an undo log record written to the rollback segment. If the row was updated, the undo log record contains the information necessary to rebuild the content of the row before it was updated. A 6-byte DB_ROW_ID field contains a row ID that increases monotonically as new rows are inserted. If InnoDB generates a clustered index automatically, the index contains row ID values. Otherwise, the DB_ROW_ID column does not appear in any index.

mysql每行有三个隐藏字段:

  • DB_TRX_ID
    记录事务唯一标识,每个事务都有ID叫做TRX_ID,DB_TRX_ID就是记录操纵这行数据的事务的ID。
    可以通过select * from information_schema.innodb_trx \G查看当前事务的TRX_ID。
    在这里插入图片描述

  • DB_ROLL_PTR
    回滚指针,当某条聚集索引记录被修改后,旧的版本都会写入undo log中,通过回滚指针形成链表,可以找到以前的版本。

  • DB_ROW_ID
    记录唯一标识,mysql每张表都会有主键。(该字段与事务隔离无关,只是顺便提及)

    1. 用户指定某列为主键
    2. 如果用户没有手动指定主键,则将非空唯一索引作为主键
    3. 如果以上情况都没有,则改记录增加一列DB_ROW_ID作为记录唯一标识

了解了DB_TRX_IDDB_ROLL_PTR也就大致理解了版本链。

假设有一条记录,插入这条记录的事务TRX_ID为100,事务A TRX_ID为200,事务B TRX_ID为300

原始数据版本链
在这里插入图片描述在这里插入图片描述
事务A(200)事务B(300)版本链
1.事务A修改name为sjj在这里插入图片描述在这里插入图片描述
2.事务B修改name为dhh在这里插入图片描述在这里插入图片描述
②read view

read view 类似于快照(什么是快照? 快照与备份有什么区别?),通过read view 这一数据结构和可见性比较算法来判断当前版本链中的哪个版本是当前事务可见的
对read view的具体判断方法可以参考以下两篇博客:

③ innodb中的锁

在这里插入图片描述
1.共享锁(S)和排它锁(X)

共享锁和排它锁都是行级锁,行级锁作用在索引而不是整条记录上的。

  • 共享锁: 共享锁也叫读锁,允许事务读一行数据
  • 排它锁: 排它锁也叫写说,允许事务修改或者删除一行数据

2.意向共享锁(IS)和意向排它锁(IX)

意向共享锁和意向排它锁都是表级锁

  • 意向共享锁: 事务想要获得一张表中某几行的共享锁 select xxx from xxx lock in share mode
  • 意向排它锁: 事务想要获得一张表中某几行的排它锁 select * from xxx for update
④行级锁的三种算法

假设表中有3个索引值: 10,20,30.

  • Record Lock: 单个行记录上的锁
    eg:锁定范围为 [10],[20],[30]三个值

  • Gap Lock: 锁定一个范围,但不包括记录本身
    eg:锁定范围为(-∞,10),(10,20),(20,30),(30,+∞)

  • Next Lock: Record Lock+Gap Lock
    eg:锁定范围为(-∞,10],(10,20],(20,30],(30,+∞)

⑤快照读和当前读

快照读: 读取的是快照版本,也就是历史版本,可能不是最新的版本。普通的select xxx都是快照读。
当前读: 读取的都是最新的版本。select xxx from xxx lock in share modeselect * from xxx for updateinsertdeleteupdate都是当前读。

⑥LBCC和MVCC
  • LBCC(Lock-Based Concurrent Control): 基于锁的并发控制,事务的每次操作都会加锁,事务串行化执行,避免了脏读不可重复读幻读。缺点是由于锁的存在,花销大,由于锁的竞争等可能出现事务的阻塞和死锁。
  • MVCC(Multiversion Concurrency Control): 多版本并发控制,MVCC原理基于之前提到的隐藏字段(DB_TRX_ID,DB_ROLL_PTR)+read view+undo log实现。
    由隐藏字段和undo log形成版本链,形成多版本。在读取时根据read view选取当前事务可见的版本。
2.具体事务隔离级别分析

有了上面的基本概念,我们就可以针对具体的事务隔离等级进行分析了。

①Read uncommitted分析

Read uncommitted下每次读到的都是最新的数据,即当前读,所以才会出现脏读的现象。

②Read committed分析

Read committed 基于MVCC,也会创建版本链,但是在开始事务后的查询时,每次快照都会被重置,即每次查询都会生成read view.导致出现不可重复读.

③Repeatable read分析

Repeatable read基于MVCC+Gap Lock

Repeatable read下的MVCC与Read committed下的MVCC不同的是,在开始事物后的查询时,只有第一次查询才会生成read view,这样才能可重复读.

我们之前说过Repeatable read下会出现幻读的现象.

但是我们在实际测试的时候,第一次却没有出现幻读的现象。在这里插入图片描述
但是我们在第二次查询时却出现了幻读的现象。
在这里插入图片描述
对比这两次查询语句,很明显第一次是普通的select,即快照读,但是第二次的select xxx lock in share mode 加了S锁,为当前读

所以MVCC只解决了快照读下的幻读,并不能解决当前读下的幻读

但是在RR隔离级别下可以通过MVCC+Gap Lock解决当前读下的幻读

④Serializable分析

串行化下每次操作都加了锁,通过next-key即可以解决幻读

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Selenium399

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值