2、MySQL事务隔离
什么是事务?
保证一致性,保证一组数据库操作,要么全部成功,要么全部失败。
CAID(Atomicity,Consistency,lsolation,Durability):
- 原子性:要么全部成功commit,要么全部失败rollback;
- 一致性:rollback后的数据和之前一样;
- 隔离性:事务和事务之间不干扰;
- 持久性:事务一旦提交就不能改了;
事务的隔离性与隔离级别
四种事务隔离级别
- 读未提交(read uncommitted):一个事务还没提交时,他做的变更就能被其他事务看到;
- 读提交(read committed):一个事务提交后,他做的变更就会被其他事务看到;
- 可重复读(repeatable read):一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。
- 串行化(serializable):对同一行记录,读写都会加锁。当读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。
Mysql视图概念
- view;用查询语句定义的虚拟表,语法create view...
- InnoDB在实现MVCC时用到的一致性视图,即consistent read view,用于支持RC(Read Committed读提交)和RR(Repeatable Read可重复读)隔离级别实现的。
视图创建时间
数据库会创建一个视图,访问的时候以视图的逻辑结果为准。
- 读未提交:直接返回记录最新值,没有视图概念;
- 读提交:视图是在每个SQL语句开始执行的时候创建的;
- 可重复读:视图是在事务启动时创建的,整个事务存在期间都用这个视图;
- 串行化:直接用加索的方式来避免并行访问;
开启事务方式
- 查看当前事务:
show variables like 'tx_isolation';(或者transaction-isolation) - 开启事务:
transaction-isolation(或tx_isolation,与数据库版本有关)参数设置为READ-COMMINTTED(读提交模式)
隔离级别操作
- 查看当前会话隔离级别:select @@tx_isolation;
- 查看系统当前隔离级别:select @@global.tx_isolation;
- 设置当前会话隔离级别:set session transaction isolation level read committed;
- 设置系统当前隔离级别:set global transaction isolation level read committed;
事务隔离实现——可重复读
- 每条记录在更新时都会同时记录一条回滚操作。记录上的最新值,通过回滚操作可以得到前一个状态的值。
- 数据库多版本并发控制(MVCC):同一条记录在系统中可以存在多个版本。
- 回滚日志(undo log)何时删除?
当没有事务再需要用到这些回滚日志时(即当系统里没有比这个回滚日志更早的read-view的时候),回滚日志就会被删除。 - 为什么尽量不要使用长事务
a、 长事务意味着系统里面会存在很老的事务视图。由于这些事务可能会访问数据库中的数据,所以在这个事务提交前,数据库里面他可能用到的回滚记录都必须保留,导致大量占用存储空间。
b、长事务占用锁资源。
事务启动方式
- 显示启动事务语句,begin或start transaction。提交语句时commit,回滚rollback。
- set outcommit=0;将自动提交关闭,执行select语句就会打开事务,直到主动执行commit或rollback才提交事务。
建议使用:
set outcommit = 1;使用begin显示启动事务,执行commit提交事务。(执行commit work and chain,用于提交事务并自动启动下一个事务,省去下一次begin的开销,建议使用)。
查找长事务语句(查找持续时间超过60s的事务):
select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60;
事务启动时机
- 第一种:begin/start transaction,一致性视图是在执行第一个快照读语句时创建的。
- 第二种:start transcation with consistent snapshot,执行这个命令时创建的。
“快照”在MVCC工作方式
-
每个事务有唯一的事务ID,transcation id;在事务开始时向InnoDB引擎申请的;
-
每行数据有多个版本;每次事务更新数据时,都会生成一个新的数据版本,把transcation id赋值给这个数据版本的事务ID,记为row trx_id;旧版本保留,通过新版本和回滚日志可以得到旧版本。
U1、U2、U3为undo log(回滚日志);V1、V2、V3并不是物理存在的,而是根据当前版本和undo log计算出来。
InnoDB如何定义数据“快照的”——可重复读、读提交模式
InnoDB为每个事务构造了一个数组,用来保存这个事务启动瞬间,当前正在“活跃”(启动但未提交)的所有事务ID。数组里面事务ID最小值记为“低水位”,当前系统里已经创建过的事务ID的最大值加1记为高水位。这个视图数组和高水位就组成了当前事务的一致性视图(read-view)。
对于当前事务的启动瞬间来说,一个数据版本的row trx_id,有一下几中可能:
- 如果在绿色部分,表示这个版本是已提交的事务或当前事务自己生成的,这个数据是可见的;
- 如果落在红色部分,表示这个版本是由将来启动的事务生成的,是不可见的;
- 如果落在黄色部分,分两种:
1、若row trx_id在数组中,表示这个版本是由还没提交的事务生成的,不可见;
2、若不在数组中,表示这个版本是已经提交了的事务生成的,可见。
一个数据版本,对于一个事务视图来说,除了自己的更新总是可见外,有三种情况:
- 版本未提交,不可见;
- 版本已提交,但是在视图创建后提交的,不可见;
- 版本已提交,而且是在视图创建前提交的,可见。
更新数据如何实现
- 更新数据都是先读后写的,而这个读只能读当前的值,称为“当前读”(current read)。
- 查询语句加锁也是当前读。语句后加上lock in share mode或for update。
事务的可重复读如何实现
可重复读的核心就是一致性图(consistent read);而事务更新数据时只能用当前读。如果当前的记录的行锁被其他事务占用的话,就需要进入锁等待。
读提交和可重复读逻辑的区别:
- 在可重复读隔离级别下,只需要在事务开始的时候创建一致性视图,之后事务里的其他查询都公用这个一致性视图;
- 在都提交隔离级别下,每一个语句执行前都会重新算出一个新的视图。