如何理解MySQL的事务?
- 简单来说就是要保证一组数据库操作,要么全部成功,要么全部失败。MySQL中的事务支持是在引擎层实现的。
- 事务具有原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)四个特性。
SQL标准的事务隔离级别包括:
- 读未提交 read-uncommited
- 读提交 read-commited
-
可重复读 repeatable read
一个事务执行过程中看到的数据,总是和这个事务启动时看到的数据是一致的。在可重复读隔离级别下,未提交的变更对其他线程也是不可见的。 -
串行化 serializable
对同一行记录,读会加“读锁”,写会加“写锁”,当出现读写冲突时,前后访问的事务必须等前一个事务执行完成,才能继续执行。
查看隔离级别
- 在MySQL客户端中输入
show variables like 'transaction-isolation';
,即可查看当前的隔离级别。
MySQL中的2种“视图”
-
一种是view,是一个用查询语句定义的虚拟表,在调用的时候执行查询语句并生成结果,创建view的语法是
create view ...
,它的查询方法与表一样。 - 另一种是consistent read view,它是InnoDB在实现MVCC时用到的一致性视图,用于支持RC(读提交)和RR(可重复读)隔离级别的实现。
InnoDB隔离级别的实现
- 事务启动时,数据库里会创建一个视图,访问的时候以视图的逻辑结果为准。
- 可重复读隔离级别下,视图是在事务启动时创建的,整个事务存在期间都用这个视图。
- 读提交隔离级别下,视图是在每个SQL语句开始执行的时候创建的。
- 读未提交隔离级别下,直接返回记录上的最新值,没有视图。
- 串行化隔离级别下,直接用加锁的方式避免并行访问。
可重复读隔离级别的实现
- 在可重复读隔离级别下,MySQL中每条记录在更新的时候都会记录一条回滚操作。记录上的最新值,通过回滚操作(undo log),都会得到前一状态的值。不同时刻启动的事务有不同的视图,即同一条记录在系统中可以存在多个版本,这就是数据库的MVCC(多版本并发控制)。
- InnoDB里每个事务都有一个唯一的
transaction id
,它是事务开始的时候向InnoDB事务系统申请的,按申请顺序严格递增。 - 而每行数据也都是有多个版本的。每次事务更新数据的时候,都会生成一个新的版本,并把本次事务的
transaction id
赋值给这个数据版本的事务ID,记为row trx_id
。同时旧的数据版本要保留,并且在新的数据版本中,能有信息(undo log)可以直接拿到它。也就是说数据表中的同一行记录,可能存在多个版本(row),每个版本都有自己的row trx_id
。 - MVCC的实现,InnoDB为每个事务构造一个数组,用来保存这个事务的启动瞬间,当前所有启动了,但还没提交的事务的ID。
- 一个数据版本,对于一个事务视图来说,除了自己的更新总是可见的以外,还有3种情况:
1、版本未提交,不可见;
2、版本已提交,但是是在视图创建后提交的,不可见;
3、版本已提交,而且是在视图创建前提交的,可见。
https://blog.csdn.net/syilt/article/details/107647007
基于可重复读隔离的实现,说明为什么尽量不要使用长事务?
- 长事务意味着系统里面会存在很老的事务视图,由于这些事务随时可能访问数据库里面的任何数据,所以这个事务提交前,数据库里面它可能用到回滚记录都必须保留,这就会导致大量占用存储空间。
- MySQL5.5及以前版本,回滚日志(undo log)是和“数据字典”一起放在ibdata文件里的,即使长事务最终提交,回滚段被清理,ibdata文件也不会变小。
- 注意,长事务不是“长连接”。
MySQL事务的启动方式
- 显示启动,
begin;
或strat transaction;
(并不是立即开启,而是执行第一条操作InnoDB表的语句时才是真正开启,提这点主要想说明它影响视图的出现时机;如果想马上启动一个事务,获得一个read view,可以使用start transaction with consistent snap shot;
),配套的提交语句commit,回滚语句rollback,commit work and chain提交事务并自动开启下一个事务(节省再次执行begin语句的开销),此时一般是参数autocommit=1的情况。 - 当set autocommit=0的时候,线程不会自动提交。此时,若只执行一个select语句,事务就已经启动了,并且不会自动提交。直到commit或rollback或断开连接。
长事务查询
- 查询持续时间超过60s的事务:
select * from information_schema.innodb_trx where TIME_TO_SET(timediff(now(),trx_started)) > 60;
。
事务更新时的版本控制逻辑
- 更新数据都是先读后写,这个读只能是当前读(current read),即读当前的值(不管当前值得row trx_id是否大于本事务,都能读到);读到后会将自己修改后的数据和自己的事务ID赋给新的“当前值”。如果当前记录的行锁被其他事务占用,就需要进入锁等待状态。
- 除update语句外,select语句如果加锁,也是当前读。有读锁(S锁,共享锁)
lock in share mode;
,写锁(X锁,排它锁)for update
。
读提交和可重复读的主要区别
- 在可重复读隔离级别下,只需要在事务开始的时候创建一致性视图,之后事务里的其他查询都公用这个一致性视图。
- 在读提交隔离级别下,每一个语句执行前都会重新计算出一个新的视图。
网友评论