扁平事务
要么都执行,要么都回滚,InnoDB最常用,最常见的事务.
带有保存点的扁平事务
事务的操作过程有 begin, A, B, C, D, commit 几个过程,那么带有保存点的扁平事务过程大致如下:
begin–> 隐含保存点1(save work 1)–>A–>B(save work2)–>C–>D(rollback work2) -->commit
上述过程中如果遇到rollback work2, 只需要回滚到保存点2,不需要全部回滚.
简单来说,带有保存点的扁平事务就是有计划的回滚操作。
保存点是容易失的(volatile), 而非持久的.系统崩溃,所有保存点都将丢失.
链事务
链事务提交一个事务时,释放不需要的数据对象,将必要的上下文传递给下一个要开始的事务. 下一个事务可以看到上一个事务的结果.
带有保存点的偏平事务可以回滚到任意正确的保存点,链事务只能回滚到当前事务.
扁平全程持锁,链事务在commit后释放锁.
链事务如: T1->T2->T3
嵌套事务
可以理解为一颗事务树,顶层事务控制着下面的子事务. 所有的叶子节点是扁平事务,实际工作是由叶子节点完成的.
分布式事务
分布式环境下运行的扁平事务.
InnoDB支持上述除嵌套事务以外的所有事务类型.
Transaction Control language 事务控制语言
一个或一组sql组成一个执行单元,这些单元要么全部执行要么都不执行
如转账, 两个地方必须一起变化(如果某一条语句执行错误,整个单元回滚)
innodb支持事务,而myisam、memory不支持事务(show egines)
ps:隐式事务 如insert、update、delete
原子性:代表一个事务是不可分割的工作单位
一致性: 从一个一致性状态转换到另一个一致性状态 ,中间状态不可见,关注最初和最后的状态。所有相关的数据规则必须应用于事务的修改,以保持事务的完整性,事务结束时,其内部数据结构(b树引索或双向链表)也都必须正确。
分布式一致性https://www.cnblogs.com/liulaoshi/p/7821339.html
隔离性: 事务的隔离性是指一个事务不能被其他事务干扰,一个事务内部操作及使用的数据堆并发的其他事务时隔离的(还是基于隔离级别)。
持久性: 一个事务一旦提交 ,他对数据库中的数据改变时永久性的,接下来的其他操作和数据库故障不应影响它。
2.显式事务:事务具有明显的开启结束标记 (前提 : 必须先设置自动提交功能为禁用)
set autocommit = 0; #开启事务
start transaction; #可选的
select insert update delete
commit; #提交事务
#rollback; #回滚事务
3.对于同时运行的多个事务,当这些事务访问相同的数据时,会导致各种并发问题,
更新丢失: 最后的更新覆盖其他事务的更新
脏读:对于两个事务T1,T2,T1读取了已经被T2更新但还没被提交的字段,若T2回滚,T1读取的内容就是临时且无效的
不可重复读: 对于两个事务T1,T2,T1读取了一个字段,T2更新了该字段之后,T1再次读取同一字段,值就不同了。(不符合隔离性)
幻读: 对于两个事务T1,T2,T1读取了一个字段,T2插入新的行,T1再次读取同一字段,就会多出几行。(当前读到新增数据)
设置隔离级别来避免
1.未提交读(read-uncommitted)
在一个事务中,可以读取到其他事务未提交的数据变化,这种读取其他会话还没提交的事务,叫做脏读现象,在生产环境中切勿使用。
2.已提交读(read-committed)
在一个事务中,可以读取到其他事务已经提交的数据变化,这种读取也就叫做不可重复读,因为两次同样的查询可能会得到不一样的结果。
3.可重复读(repetable-read)
MySQL默认隔离级别,在一个事务中,直到事务结束前,都可以反复读取到事务刚开始时看到的数据,并一直不会发生变化,避免了脏读、不可重复读现象,但是它还是无法解决幻读问题。
4.可串行化(serializable)
这是最高的隔离级别,它强制事务串行执行,避免了前面说的幻读现象,简单来说,它会在读取的每一行数据上都加锁,所以可能会导致大量的超时和锁争用问题。(加锁,相当于另外一个事务的插入操作会被阻塞)
隔离级别一览表
隔离级别 读数据一致性 脏读 不可重复读 幻读
未提交读 最低级别,只保证不读取物理上损坏的数据 有 有 有
已提交读 语句级 无 有 有
可重复读 事务级 无 无 有
可串行化 最高级别,事务级 无 无 无
隔离级别越严格,内部工作机制越复杂,较松散的隔离级别通常可以支持更高的并发。
#查看隔离级别
SELECT @@tx_isolation
#设置隔离级别
#当前mysql连接
set transaction isolation level read committed;
#全局
set global trasaction isolation level read committed
mysql默认隔是个事务离级别的原因,所以需要正确的理解幻读。
幻读错误的理解:说幻读是 事务A 执行两次 select 操作得到不同的数据集,即 select 1 得到 10 条记录,select 2 得到 11 条记录。这其实并不是幻读,这是不可重复读的一种,只会在 R-U R-C 级别下出现,而在 mysql 默认的 RR 隔离级别是不会出现的。
这里给出我对幻读的比较白话的理解:
幻读,并不是说两次读取获取的结果集不同,幻读侧重的方面是某一次的 select 操作得到的结果所表征的数据状态无法支撑后续的业务操作。更为具体一些:select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入,此时就发生了幻读。
MVCC的全称是多版本并发控制。MVCC使得InnoDB的事务隔离级别下执行一致性读操作有了保证。简单说就是为了查询一些正在被另一个事务更新的行,并且可以看到它们被更新之前的值。这是一个用来增强并发性的强大技术,可以使得查询不用等待另一个事务释放锁。
MVCC会给每一行增加三个字段。分别是DB-TRX-ID DB-ROLL-PTR,DB-ROW-ID
增删查改
在InnoDB中,给每行增加两个隐藏字段来实现MVCC,一个用来记录数据行的创建时间,另一个用来记录行的过期时间,在实际操作中,存储的并不是时间,而是事务版本号,每开启一个新事务,事务的版本号就会递增。所以增删改查中对版本号的作用如下:
- select:
- 读取创建版本小于或等于当前事务版本号,并且删除版本为空或大于当前事务版本的记录。这样可以保证在读取之前记录都是存在的
- insert:
- 将当前事务的版本号保存至行的创建版本号
- update
- 新插入一行,并以当前事务版本号作为新行的创建版本号,同时将原记录行的删除版本号设置为当前事务版本号
- delete
- 将当前事务版本号保存至行的删除版本号
快照读和当前读
快照读:读取的是快照版本,也就是历史版本
一般的 select * from … where … 语句都是快照读
当前读:读取的是最新版版
普通的select就是快照读,而update,delete,insert,select…LOCK In SHARE MODE,SELECT…for update就是当前读
首先要知道的是MVCC 就InnoDB 秒级建立数据快照的能力。 快照读就是读取数据的时候会根据一定规则读取事务可见版本的数据。 而当前读就是读取最新版本的数据。什么情况下使用的是快照读:(快照读,不会加锁)
如果事务中都使用快照读,那么就不会产生幻读现象,但是快照读和当前读混用就会产生幻读。
一致性非锁定读和锁定读
锁定读
- 在一个事务中,标准的SELECT语句是不会加锁,但是有两种情况例外。SELECT … LOCK IN SHARE MODE 和 SELECT … FOR UPDATE。
SELECT … LOCK IN SHARE MODE:给记录假设共享锁,这样其他事务职能读不能修改,直到当前事务提交
- SELECT … FOR UPDATE:给索引记录加锁,这种情况跟UPDATE的加锁情况是一样的
一致性非锁定读
- consistent read(一致性读),InnoDB用多版本来提供查询数据库在某个时间点的快照。如果隔离级别是REPEATABLE READ,那么在同一个事务中的所有一致性读都读的是事务中第一个的读读到的快照;如果是READ COMMITTED,那么一个事务中的每一个一致性读都会读到它自己刷新的快照版本。Consistent read(一致性读)是READ COMMITTED和REPEATABLE READ隔离级别下普通SELECT语句默认的模式。一致性读不会给它锁访问的表加任何形式的锁,因此其他事务可以同时并发的修改它们
锁
- 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)理论分析
- 在默认的隔离级别中,普通的SELECT用的是一致性读不加锁。而对于锁定读,UPDATE和DELETE,则需要加锁,至于加什么锁是有不同情况的。如果对一个唯一索引使用了唯一的检索条件,那么只需要锁定相应的索引记录就好;如果是没有使用唯一索引作为检索条件,或者用到了索引范围扫描,那么将会使用间隙锁或者next-key锁来以此阻塞其他会话向这个范围内的间隙插入数据
- 利用MVCC实现一致性非锁定读,保证在同一个事务中多次读取相同的数据返回的结果是一样的,解决了不可重复读问题
- 利用Gap Locks和Next-key可以阻止其他事务在锁定区间内插入数据,解决了幻读问题
综上所述,MySQL的默认隔离级别的实现依赖于MVCC和锁,准确点说就是一致性读和锁
原文链接 https://www.cnblogs.com/develop-SZT/p/10339138.html
为什么要非锁定读?有什么影响?
#3.savepoint 的使用
SET autocommit = 0;
START TRANSACTION;
DELETE FROM account WHERE id = 25;
SAVEPOINT a;#设置保存点
DELETE FROM account WHERE id = 28;
ROLLBACK TO a; #回滚到保存点
rollback机制
1, redo log(事务日志)保证事务的原子性和持久性(物理日志)
2, undo log保证事务的一致性,InnoDB的MVCC也是用undo log来实现的(逻辑日志).
3, redo log中带有有checkPoint,用来高效的恢复数据.
4, 物理日志记录的是修改页的的详情,逻辑日志记录的是操作语句. 物理日志恢复的速度快于逻辑日志.
https://www.csdn.net/gather_22/MtTaMgwsMjMxNC1ibG9n.html
当事务提交的时候,innodb不会立即删除undo log,因为后续还可能会用到undo log,如隔离级别为repeatable read时,事务读取的都是开启事务时的最新提交行版本,只要该事务不结束,该行版本就不能删除,即undo log不能删除。
但是在事务提交的时候,会将该事务对应的undo log放入到删除列表中,未来通过purge来删除。并且提交事务时,还会判断undo log分配的页是否可以重用,如果可以重用,则会分配给后面来的事务,避免为每个独立的事务分配独立的undo log页而浪费存储空间和性能。
通过undo log记录delete和update操作的结果发现:(insert操作无需分析,就是插入行而已)
delete操作实际上不会直接删除,而是将delete对象打上delete flag,标记为删除,最终的删除操作是purge线程完成的。
update分为两种情况:update的列是否是主键列。
如果不是主键列,在undo log中直接反向记录是如何update的。即update是直接进行的。
如果是主键列,update分两部执行:先删除该行,再插入一行目标行。
https://www.cnblogs.com/f-ck-need-u/archive/2018/05/08/9010872.html
#truncate不支持事务的回滚
SET autocommit =0;
START TRANSACTION;
TRUNCATE TABLE account;
ROLLBACK;