事务篇
# 何为事务?
事务分为:分布式事务,数据库事务;
数据库事务:多个对数据库的操作构成一个整体逻辑,要么全部执行,要不全不执行;
START TRANSACTION;
SQL1,SQL2;
COMMIT;
2
3
# 事务四大特性
ACID
A(原子性):事务是最小执行单位,要么全部完成,要么完全不起作用;
C(一致性):事务前后,数据一致;
I(隔离性):并发访问数据库,一个用户的事务不被其它事务干扰;
D(持久性):事务提交后,对数据库里的数据是持久的;
AID是手段,C是目的;
如何保证?
原子性通过 undo log 来保证;
隔离性通过 MVCC 或 锁机制 来保证;
持久性通过 redo log 来保证;
一致性通过 原子性+隔离性+持久性来保证;
# 并发事务带来的问题?
脏读:一个事务读到 另一个未提交事务修改过的数据 ;
A事务读取到B事务未提交修改的数据,B后续还有可能回滚,所以A读取到一个脏数据;
不可重复读:同一个事务多次读取同一个数据,前后读到的数据不一致;
幻读:同一个事务查询符合查询条件的 记录数量,前后查询数量不一致;
# 事务隔离级别
- 读未提交:一个事务未提交,修改的数据被其它事务看见;
- 读已提交:只有事务提交,修改的数据才能被其他事务看见;
- 可重复读:事务启动后,所有数据是一致的;(InnoDB默认)
- 串行化:对记录加锁,多个事务执行这条记录时,发生读写冲突,后访问事务必须等前一个事务执行完成;
隔离级别如何实现?
读未提交:读取最新数据;
读已提交:在每条语句执行前,生成ReadView;
可重复读:启动事务时,生成ReadView,整个事务期间,都是用这个ReadView;
串行化:读写锁;
# 如何解决不可重复度?
- 设置事务隔离级别;
- 行级锁:select...for update语句防止其他事务修改数据;
- 乐观锁:读取数据之后,执行写操作之前,再次检查数据是否被其他事务修改;
# InnoDB可重复读如何解决幻读?
快照读,通过MVCC解决了幻读;
可重复读下,开启事务后,在执行第一个查询语句后,会创建一个Read View,后续的查询语句会利用这个Read View,通过这个Read View可以在undo log版本连找到事务开始时的数据,所以事务过程中每次查询的数据都是一样的,即使中途有其他事务插入了新纪录,是查询不出来这条数据;
当前读,通过临键锁解决了幻读;
其他事务在临键锁范围内插入一条记录,这个插入语句被阻塞;
# ReadView在MVCC如何工作?
ReadView 4个重要字段:
- creator_trx_id :创建该 Read View 的事务的事务 id;
- m_ids : 在创建 Read View 时,当前数据库中「活跃事务」的事务 id 列表;
- min_trx_id :在创建 Read View 时,当前数据库中「活跃事务」中事务 id 最小的事务,也就是 m_ids 的最小值;
- max_trx_id :全局事务中最大的事务 id 值 + 1;
聚簇索引记录隐藏列:
- trx_id:当前事务的事务id;
- roll_pointer:指向undo版本链的指针;
创建Read View后,记录中trx_id的三种情况:
- 如果记录的
trx_id
值小于 Read View 中的min_trx_id
值,表示这个版本的记录是在创建 Read View 前已经提交的事务生成的,所以该版本的记录对当前事务可见; - 如果记录的
trx_id
值大于等于 Read View 中的max_trx_id
值,表示这个版本的记录是在创建 Read View 后才启动的事务生成的,所以该版本的记录对当前事务不可见; - 如果记录的
trx_id
值在 Read View 的min_trx_id
和max_trx_id
之间,需要判断trx_id
是否在m_ids
列表中:- 如果记录的
trx_id
在m_ids
列表中,表示生成该版本记录的活跃事务依然活跃着(还没提交事务),所以该版本的记录对当前事务不可见; - 如果记录的
trx_id
不在m_ids
列表中,表示生成该版本记录的活跃事务已经被提交,所以该版本的记录对当前事务可见;
- 如果记录的
# InnoDB可重复读可以完全解决幻读吗?
先快照读 后当前读;
- T1 时刻:事务 A 先执行「快照读语句」:select * from t_test where id > 100 得到了 3 条记录。
- T2 时刻:事务 B 往插入一个 id= 200 的记录并提交;
- T3 时刻:事务 A 再执行「当前读语句」 select * from t_test where id > 100 for update 就会得到 4 条记录,此时也发生了幻读现象。
在开启事务之后,马上执行 select ... for update 这类当前读的语句,因为它会对记录加 next-key lock,从而避免其他事务插入一条新记录;
# 临键锁是否可以防止删除操作导致的幻读?
可以,删除语句陷入等待状态;