第三讲笔记——事务隔离

事务:保证一组数据库操作,要么全部成功,要么全部失败。在MySQL中,事务支持是在引擎层实现的,所以本次分析事务是以 InnoDB 为例。

隔离性与隔离级别

事务有四大特性:ACID(Atomicity、consistency、Isolation、Durability),分别是原子性、一致性、隔离性、持久性。

当多个事务同时执行的时候就有以下问题:

  • 脏写(dirty write):事务 B 去修改了事务 A 修改过的值,但是此时事务 A 还没提交,所以事务 A 随时会回滚,导致事务 B 修改的值也没了。
  • 脏读(dirty read):事务 B 去查询了事务 A 修改过的数据,但是此时事务 A 还没提交,所以事务 A 随时会回滚导致事务 B 再次查询就读不到刚才事务 A 修改的数据。
  • 不可重复读(nonrepeatable read):事务A首先读取了一条数据,然后执行逻辑的时候,事务B将这条数据改变了,然后事务A再次读取的时候,发现数据不匹配了。也就是当前事务先进行了一次数据读取,然后再次读取到的数据是别的事务修改成功的数据,导致两次读取到的数据不匹配。
  • 幻读(phantom read):事务A首先根据条件索引得到N条数据,然后事务B改变了这N条数据之外的M条或者增添了M条符合事务A搜索条件的数据,导致事务A再次搜索发现有N+M条数据了。

注:隔离越严实,效率就会越低

SQL标准的隔离事务级别包括:

  • 读未提交(read uncommitted):一个事务没有提交,它做的变更就能被其他事务看到。
  • 读提交(read committed):一个事务提交之后,他做的变更才能被别的事务看到。
  • 可重复读(repeatable read):一个事务执行中看到的数据,总是跟这个事务在启动时看到的数据是一样的。当前这个事务没提交,其他事务也看不到更变内容。
  • 串行化(serializable):对同一记录,“写”会加“写锁”,“读”会加“读锁”。

例如:

insert into T(c) values(1)
运行顺序事务A事务B
启动事务,查询得到值为 1启动事务
查询得到值为1
将 1 改为 2
查询得到值 V1
提交事务B
查询得到值 V2
提交事务 A
查询的到值 V3

在不同的隔离条件下 V1、V2、V3 的值不同:

  • 读未提交:V1、V2、V3 都是 2。
  • 读提交:V1 是 1,V2、V3 都是 2。
  • 可重复读:V1、V2、V3 都是 1。事务在执行期间看到的数据前后一致
  • 串行化:在事务 B 执行“将 1 改为 2”时(应该是执行动作之前),会被锁住。直到 A 提交事务后才能执行。所以 V1、V2 是 1,V3 是 2。

在底层实现上,数据库会创建一个视图,访问的时候以视图的逻辑结果为准。

  • 读未提交:直接返回记录上的最新值,没有视图概念。
  • 读提交:这个视图是在每个 SQL 语句
  • 可重复读:这是视图是在事务启动时创建的,整个事务期间都用这个视图。
  • 串行化:直接用加锁方式来避免并行访问。(最霸道)

注:Orcale 默认使用的是“读提交”,MySQL 默认使用的是“可重复读”

事务隔离的实现

这里展开说明“可重复读”。

在 MySQL 中,每条记录在更新的时候都会同时记录一条回滚操作。

在下图中,就是一个值从 1 改到了 2、3、4,在回滚日志里面就会有类似的记录。

当前值是 4 ,但是在查询这条记录的时候,不同时刻启动的事务就有不同的 read-view。在图中的 A、B、C 视图里面,这个记录的值是 1 、2、4,同一条记录有着不同的版本,就是数据库的多版本并发控制(MVCC)。对于 read-view A,要想得到 1,就必须把图中的操作全部回滚。

而且如果同时有另一个事务正在将 4 改成 5,那么这个事务和 read-view A、B、C 是不会冲突的。

当系统中没有这个回滚段更早的 read-view 时,那这个日志文件就会被删除。

所以尽量不要使用长事务。长事务意味着系统中会有很古老的事务视图,会导致占用大量的存储空间。除了对回滚段的印象,长事务还占用锁资源,也可能会拖垮整个库。

事务的启动方式

MySQL事务的启动方式有以下几种:

  1. 显示启动事务语句,beginstart transaction。配套的提交的语句是 commit,回滚语句是 rollback
  2. set autocommit = 0,这个命令会将这个线程的自动提交关掉。哪怕只执行一个 select 语句,这个事务也启动了,不会自动提交。直到你主动执行 commitrollback 语句,或者断开连接。

建议使用 setautocommit = 1 来显示启动事务。

如果纠结“多一次交互”问题。对于一个需要频繁使用事务的业务,第二种方法在事务开始时不会执行一次 begin ,减少语句的交互。建议使用 commit work and chain 语法。

对于 commit work and chain会提交事务并自动启动下一个事务,省去了下次执行 begin 语句。同时,从程序开发的角度明确知道了每个语句是否处于事务中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值