MySQL 事务隔离
事务就是保证一组数据库操作,要么全部成功,要么全部失败。在MySQL中事务是在引擎层实现的。
一、事务特性:
ACID:原子性、一致性、隔离性、持久性
- 原子性:事务中全部操作,要么全部完成,要么全部失败;
- 一致性:几个并行事务,执行结果必须与按某一顺序串执行结果相一致;
- 隔离性:事务的执行不受其他事务干扰,事务执行的中间结果对其他事务是透明的;
- 持久性:任意提交的事务,系统必须保证该事务对数据库的改变不被丢失;
二、隔离性与隔离级别:
当数据库上有多个事务同时执行,可能出现脏读、不可重复读、幻读问题,为了解决这些问题,就有了隔离级别
。隔离得越严实,效率就会越低。
事务的隔离级别包括:
-
读未提交:一个事务还没有提交,它做的变更会被其他事务看到;
-
读提交:一个事务提交后,它做的变更会被其他事务看到;
-
可重复读:一个事务在执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的;
未提交变更对其他事务也不可变(InnoDB引擎默认)
-
串行化:同一记录,写会加写锁。读会加读锁。当出现读写冲突的时,后访问的事务必须等前一个事务执行完成,才能继续执行;
数据库里会创建一个视图,访问的时候会以视图的逻辑为准:
- 读未提交:隔离级别下,直接返回记录上的最新值,没有视图概念;
- 读提交:隔离级别下,视图在每个SQL语句开始执行时创建;
- 可重复读:隔离级别下,视图是在事务启动的时候创建,整个期间都在使用该视图;
- 串行化:隔离级别下,直接使用加锁方式来避免访问;
注意:
-
Oracle 数据库默认隔离级别,就是
读提交
,若要进行迁移MySQL,需要注意隔离级别:-
transaction-isolation 的值设置成 READ-COMMITED:
# 查看当前值 mysql> show variables like 'transaction_isolation'; +-----------------------+----------------+ | Variable_name | Value | +-----------------------+----------------+ | transaction_isolation | READ-COMMITTED | +-----------------------+----------------+
-
InnoDB支持RC(读提交)和RR(可重复读)隔离级别实现是用的一致性视图(consistent read view)。
三、事务隔离级别的实现:
以下所说的是,可重复读隔离级别的实现。
每条记录在更新时都会同时记录一条回滚操作。记录上的最新值,通过回滚操作,都可以得到前一个状态值。
同一条记录在系统中可存在多个版本,就是数据库的多版本并发控制(MVCC)。
为什么不使用长事务?
- 会对回滚有影响,长事务还占用锁资源。
四、事务启动的方式:
-
显示启动事务语句,begin 或 start transaction;提交语句 commit;回滚语句rollback;
-
set autocommit=0,这个命令会将这个线程自动提交关掉。
仅执行一个select语句,事务就已经开启,并不会自动提交。这个事务持续存在,直到主动执行commit 或 rollback语句,或断开连接。
-
提交事务并启动下一个事务:
commit work and chain
-
在information_schema库的innodb_trx表中查询长事务。下面语句,查找持久时间超过60s的事务;
select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60;
五、快照:
事务在启动时会拍一个快照,这个快照是基于整个库的。
- 基于整个库的意思,就是说一个事务内,整个库的修改对该事务都是不可见的(对于快照读情况),如果在事务内select T表,另外的事务执行了DDL(修改字段) T表,根据发生的事件,要不就锁住,要不就报错。
六、什么是MVCC呢?
- MVCC即多版本并发控制。
- MySQL大多数是事务型存储引擎实现的都不是简单的行级锁。基于提升并发性能的考虑,它们一般都同时实现了MVCC。
MVCC优缺点:
- 优点:MVCC大多数情况下代替了行锁,实现了对读的非阻塞,读不加锁,读写不冲突。
- 缺点:每行记录都需要额外的存储空间,需要做更多的行维护和检查工作。
事务如何实现MVCC呢?
1.每个事务都有一个事务ID,叫做transaction id(严格递增)。
2.事务在启动时,找到已经提交的最大事务ID标记为up_limit_id。
3.事务在更新一条语句时,比如id=1改为id=2,会把id=1的行之前的orw trx_id 写到undo log里,并在数据页上把id的值改为2,并且把修改这条语句的transaction id 记在该行行头。
4.在定一个规矩,一个事务要查看一条数据时,必须先用该事务的up_limit_id与该行的transaction id 做比对:
up_limit_id >= transaction id,那么可以看;
up_limit_id < transaction id ,只能去undo log里面取;
去undo log查找数据的时候,也需要对比,必须up_limit_id > transaction id 才返回数据;
什么是当前读?
- 由于当前读都是先读后写,只能读当前的值,所以当前读,会更新事务内的up_limit_id为该事务的transaction id;