一、事务及事务特性
1、原子性 --> 都成功或者都失败;
2、一致性 --> 一个事务操作数据结束后,数据总数不变;转账模型;
3、隔离性 --> 事务之间要隔离;
4、问题:
-- 事务A读取数据后被抢占资源;
-- 事务B修改数据;
-- 事务A还在旧数据的基础上做操作;
5、持久性 --> 已经提交的数据永久保存;
二、事务并发引发的问题
1、脏读 --> B事务读取到了数据后,数据发生回滚;读到了不存在的数据;
2、不可重复读 --> ②次比①次新;人换了;
3、幻读 --> B事务两次读取的条数不一样;人数不一样;
4、严重程度:
脏读 > 不可重复读 > 幻读;
三、4种事务隔离级别
1、事务隔离级别
隔离级别 | 脏读 | 不可重复读 | 幻读 |
读未提交 read uncommitted | 可能 | 可能 | 可能 |
读已提交 read committed | | 可能 | 可能 |
可重复读 repeatable read | | | 可能 |
串行化 | | | |
2、事务的使用语法
-- begin,start transaction;
-- commit;
-- 查看事务隔离级别:select @@tx_isolation; 或者 @@transaction_isolation;
-- 设置事务隔离级别:set session transaction isolation level + 隔离级别
3、保存点
-- savepoint ;
-- 检查自动提交:show variables like '%autocommit%';
-- 关闭自动提交:set autocommit = 0;
-- 设置保存点:savepoint s1;
-- 回滚到目标保存点:rollback to savepoint s1;
4、隐式提交
-- 关闭自动提交后,还有一些语句会触发隐式提交;
-- DDL : create、drop、alter
-- 在事务里面使用这些语句会自动提交事务;
5、事务控制和锁定语句
-- 在事务A中使用begin或start transaction 开启一个新事务,会隐式提交A事务;
四、MVCC及版本链
1、undo 日志:
-- insert、update、delete;innoDB记录一条日志;
-- 每一条记录做一次变动,undo日志记录一次;
-- undo编号从0开始;
2、版本链:
-- 作用:实现事务隔离;
-- 示例:
3、ReadView -- 读视图
m_ids | 读的时候产生视图;视图中记录:当前活跃的读写事务的id; |
min_trx_id | 最小事务的id |
max_trx_id | 最大事务的id |
create_trx_id | 生成的新事务的id |
五、事务隔离机制
串行化 | 一个事务完全执行完,下一个事务再开始 |
读未提交 | 一直读版本链中最新的数据; 读最新【修改】的数据; |
读已提交 READ COMMITTED | 读最新【commit -- 提交】的数据;解决了脏读问题; select 语句执行过程如下: 1、每一次select时,先创建一个read view -- 读视图 2、获取到:m_ids=[80,120]; min_trx_id=80, max_trx_id=121, create_trx_id=0; 3、到版本链中挑选记录,先从最新的记录开始,获取它的事务id; 4、拿着这个事务id到m_ids比较,如果找到,那说明这个事务是个活跃的,不可用; 5、向前回滚一条,直到拿到的记录不是活跃的; |
可重复读 REPEATABLE READ | 【不】读最新【commit -- 提交】的数据; 只读上次读到的数据;解决了不可重读; 1、在一个事务内,第一次select时,创建一个read view -- 读视图; 2、往后只要select语句没变,就一直用旧的读视图; 3、只要读视图没变,那查到的数据永远一样; |
六、MySQL幻读问题
-- MySQL默认事务隔离级别:可重复读(REPEATABLE READ);
-- 该隔离机制下,同一个事务 A 内,相同的SQL语句会使用相同的读视图(READ VIEW);
-- 但是,如果 A 中SQL查询结果为空,那么不创建读视图;
-- 此时,另一个事务 B 创建数据但未提交;
-- 且 A 对这条新数据,做了update操作,那就会更新【版本链】;
-- 从而导致:A 下一次查询结果不为空,那就创建视图;