介绍为什么需要事务,什么是ACID特性以及什么是事务隔离级别
为什么需要事务
- 数据一致性:当多个用户同时访问数据库时,可能会出现数据不一致的情况。通过使用事务,可以确保每个操作都被完全执行或者完全撤销,从而使得数据库保持一致性。
- 数据可靠性:如果一个事务在处理过程中发生错误,所有对数据库的修改都会被回滚,以便让数据库恢复到原始状态。这避免了在错误状态下导致数据被损坏的风险。
- 并发控制:由于MySQL支持并发访问,可能会出现多个用户同时对同一资源进行读写操作的情况。通过使用事务,可以防止并发操作带来的数据冲突问题,从而保证数据的完整性和正确性。
事务特性
将一系列的操作,组成一个工作单元,保证不可分割,全部操作执行成功或全部失败;ACID
事务的正确执行,需要满足ACID特性:
- 原子性 Atomicity:一个事务必须被视为一个不可分割的最小工作单元,整个事务中所有操作,要么全部成功,要么全部失败;
- 一致性 Consistency:保证任何情况下,事务执行前后的数据正确性;不会因为并发问题,导致数据错误;
- 隔离性 Isolation:一个事务在最终提交前,所变更的数据对其他事务是不可见的;保证并发场景下,数据的一致性;
- 持久性 Durability:一旦事务提交完成,修改将持久化到数据库中;
InnoDB是如何保证ACID
- 原子性:InnoDB通过Undo Log保证事务的原子性;实现回滚的操作;
- 隔离性:通过锁、MVCC保证事务的隔离性:
- 写并发:由锁来控制;
- 读并发:由MVCC来控制;
- 持久性:Redo Log、Undo Log来保证;
- 一致性:在以上的特性保证的前提下,一致性也就得到了保证;
事务的隔离级别
事务隔离级别,是一种Trade Off的结果;
并发数据读写,可能存在:脏读、不可重复读、幻读等问题;
但是如果完全隔离,比如串行化执行,又会带来并发性能问题;
因此:通过设定不同的隔离级别,应对不同的并发场景;
并发读问题
- 脏读:一个事务对数据进行增删改,但并没有提交,另一个事务却能读到未提交的数据;
- 不可重复读:前后多次读取,数据内容不一致;一事务对数据进行了更新或删除操作,提交后,另一事务中再次读取同一个数据,结果不一致;
- 解决不可重复读,关键在于事务间的数据可见范围;
- MVCC通过ReadView的生成时机
- 幻读:前后多次执行同样的读操作,返回的数据量不,通常是范围操作时,期间被其他事务插入或删除数据;
隔离级别
隔离级别 | 级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|---|
RU 读未提交 | 无保证 | 存在 | 存在 | 存在 |
RC 读已提交 | 语句级 | 不存在 | 存在 | 存在 |
RR 可重复读 | 事务级 | 不存在 | 不存在 | 存在(解决了部分幻读) |
串行化 | 最高级,效率低 | 不存在 | 不存在 | 不存在 |
- RU:Read Uncommited
- RC:Read Committed
- RR:Repeatable Read
不同的隔离级别如何解决并发问题
脏读:读取了未提交事务的数据,事务间完全没有隔离,就会发生脏读;
- MVCC通过活跃事务和版本链,保证了当前事务不会读取到其他活跃事务的数据版本;
不可重复度:其他事务提交后,当前事务再次读取数据不一致;
- RC隔离级别每次执行一致性读,MVCC都会生成新的ReadView,就会读取到最新的可读版本;
- RR隔离级别只生成一次ReadView,其他事务提交,也不会污染当前事务的ReadView,保证了每次读取数据一致;详见:[[05-MySQL-读写并发#MVCC#ReadView的生成时机]]
幻读:当前事务锁定了多条数据,其他事务插入了新的数据,导致当前事务读取了更多的数据;
- RC隔离级别仍然存在此问题,MVCC都会生成新的ReadView;
- RR隔离级别需要分情况:
- 如果当前事务仅有一致性读,不会发生幻读,ReadView只有一个;
- 如果当前事务仅有当前读,不会发生幻读,临键锁Next-Key Lock会锁定当前事务操作的数据范围,阻塞其他事务对此范围的数据写入;
- 如果当前事务混合使用一致性读和当前读,会发生幻读;
如何解决幻读:加锁
如果业务操作是先select,后update,并且存在多线程操作,只能在读的时候加锁防止其他事务插入,强制同步处理;
SELECT ... FOR UPDATE