MySQL学习之事务隔离

图片

学习了基础架构, 日志系统。今天看一下,我们平时在日常开发中用的最多的事务。

最经典的例子,就是你要给你的朋友王麻子转100块钱,而此时你的银行卡中只有100块钱。转账的过程具体到程序里会有一系列的操作,比如查询余额、做加减法、更新余额等操作。

这些操作必须保证是一体的,不然等程序查完后,你“趁着程序还没反应过来”进行扣减,你再给另一个朋友转去100元,这岂不是乱套了吗?这个时候就要用到 事务 了。

一、概念

事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消。也就是事务具有原子性,一个事务中的一系列的操作要么全部成功,要么一个都不做。

事务的结束有两种,当事务中的所以步骤全部成功执行时,事务提交。如果其中一个步骤失败,将发生回滚操作,撤消撤消之前到事务开始时的所以操作。

在MySQL中,事务是在引擎层进行实现的,MySQL是一个支持多引擎的系统,但是并不是所有的引擎都是支持事务的。MySQL 原生的MyISAM就是不支持事务,这也是MySQL5.5之后,MyISAM被InnoDB取代的重要原因之一。

二、隔离级别

提到事务,我们第一反应应该就是事务的四大特性,原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability),简称 ACID。其他的通过字面意思都还是比较好理解的,我们看下这个 “隔离性”是怎么玩的。

我们知道,在数据库上进行多个事务同时执行的时候,就可能会出现脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)的问题,为了解决这些问题,引出了隔离级别这个概念。

Tips:
脏读:读到其他事务未提交的数据;

不可重复读:前后读取的记录内容不一致(数据);

幻读:前后读取的记录数据不一致(条数);

SQL标准的事务隔离级别包括:读未提交(read uncommitted)、读提交(red committed)、可重复读(repeatable read)、串行化(serialzable)。这些分别都是什么意思呢?别急,解释来了:

  • 读未提交:一个事务还没有提交时,他做的变更数据就能被其他的事务看到。

  • 读 提 交 :一个事务提交之后,他做得变更数据才能被其他事务看到。

  • 可重复读:一个事务执行过程中看到的数据,总是跟这个事务启动时看到的数据是一致的。(在可重复读隔离级别下,未提交的变更对其他事务是不可见的),换句话就是 一个事务启动时,读到的是什么数据,在执行过程中,就是什么数据,不会发生改变,无论别的事务是否对其进行改动。

  • 串 行 化 :对同一行数据记录,写时会加写锁,读时会加读锁。当出现读写锁冲突时,后访问的事务必须要等待前一个事务完成,才能进行。换句话也就是说,我执行的时候,别的事务是不能查询到或者修改这一行数据的。

举个例子:

mysql> create table D(a int) engine=InnoDB;
insert into D(a) values(1);
事务A 事务B
启动事务查询得到值1 启动事务
查询得到值1
将1改为2
查询得到值V1
提交事务B
查询得到值V2
提交事务A
查询得到值V3

(事务演示 列表)

看下在不同的隔离级别下,事务A都会有什么结果。

  • 读未提交:V1 的值 是 2 。事务B虽然还没有提交,但是结果A已经看到了,所以V2,V3结果都是2 。

  • 读 提 交 :V2 的值 是 1 。事务B提交,但是A是看不到的,所以V2 是 1。这时事务A提交,V3的值 是 2。

  • 可重复读 :V1 ,V2 的值 是 1。V3 的是 2。V1,V2之所以是1,就是因为遵循事务执行期间,看到的数据结果是一致的。

  • 串 行 化 :事务A启动时,进行查询,这时会加读锁,事务B在进行读取,要等到事务A释放,才能进行读取。等到事务A提交事务,事务B进行执行,这时V1,V2的值是1,V3的值为2。

三、什么时候创建视图

为什么有的是能看到有的却看不到呢?因为在实现上,数据库会创建一个视图,访问的时候以视图的逻辑结果为准,进行访问。在每个隔离级别下,视图创建的时间也是不同的,数据库的行为是有所不同的。

  • 可重复读:视图是在事务启动时进行创建的,整个事务存在期间都用这个视图。

  • 读 提 交 :视图是在每个SQL语句开始执行时进行创建的。

  • 读未提交:直接返回记录上的最新值,没有视图概念。

  • 串 行 化 :采用锁的机制来避免并行访问。

也就是说。

读提交的情况下,会在每一个语句前创建一个视图,所以在读提交情况下,一个事务是可以看到其他事务提交的内容,因为它在每次查询之前都会重新用最新的数据创建一个视图。

*可重复读的级别下,视图是在开始事务的时候就创建好了,这个视图会一直使用,一直到这个事务结束。
*

四、事务隔离的实现

在MySQL中,每条记录在更新的时候都会记录一条变更记录(redo log),也同时会记录一条变更相反的回滚操作记录(undo log)。

假设一个值从 2 被按顺序改成了3、4、5,在回滚日志里面就会有类似下面的记录

图片

( 回滚日志 演示)

  • 当前值是 4,不同时刻启动的事务会有不同的 read-view

  • 在视图 A、B、C 里面,这一个记录的值分别是 1、2、4,同一条记录在系统中可以存在多个版本,就是数据库的多版本并发控制(MVCC)

  • 当没有事务再需要用到这些回滚日志时,回滚日志会被删除

同时你会发现,即使现在有另外一个事务正在将 4 改成 5,这个事务跟 read-view A、B、C 对应的事务是不会冲突的。

我想你一定有一个疑问,回滚日志总不能一直保留吧,什么时候删除呢?

在不需要的时候才删除。也就是说,系统会判断,当没有事务再需要用到这些回滚日志时,回滚日志会被删除。什么时候才不需要了呢?就是当系统里没有比这个回滚日志更早的 read-view 的时候。

五、事务的启动方式

  • 显式启动事务语句beginstart transaction,配套的提交语句是 commit,回滚语句是 rollback

  • set autocommit=0:该命令会将这个线程的自动提交关掉,如果你只执行一个 select 语句,这个事务就启动了,而且并不会自动提交

图片

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 1

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!