MySQL----事务

一.事务四大特性 ACID

原子性(Atomicity): 一个事务中的操作要么全部成功要么全部失败;由undo log(回滚日志)实现

持久性(Durability): 事务执行完毕后,对数据的修改是永久的;由redo log(重做日志)实现

隔离性(Isolation): 事务在并发环境下各自独立运行不受外部并发操作影响;由MVCC(多版本并发控制)与锁机制共同实现的。MVCC解决读-写冲突,锁机制解决写-写冲突

一致性(Consistency): 事务执行后,数据库必须从一个合法的状态转移到另一个合法的状态,始终满足预定义的规则(如主键唯一、外键约束、业务逻辑等)。若违反规则,事务会回滚。 确保数据永远处于合法状态;由持久性+原子性+隔离性保证,即由redo log + undo log + MVCC + 锁共同实现。

二.并发事务三大问题

脏读: 事务A读取到了事务B修改后未提交的数据,且事务B回滚;假设有事务A与事务B同时进行,事务A先修改了数据但暂时未提交,此时事务B直接读取到了事务A修改后未提交的数据,但事务A在后续执行中发生了回滚操作,数据变更为了未修改前的旧数据,那么事务B就读取到了无效的错误数据,即为脏读。

不可重复读: 在一个事务内多次读取同一个数据,但前后读取到的数据不一致;假设有事务A与事务B同时进行,事务A先读取了一次数据,然后事务B修改了数据并提交,此时事务A再次读取数据就读取到了新数据,前后不一致即为不可重复读。

幻读: 在一个事务内多次查询符合条件的数据,但前后查询到的符合条件的数据数量不同;假设有事务A与事务B同时进行,事务A先查询符合条件的数据,此时事务B又插入了数量不等的符合事务A查询条件的数据,此时事务A再次查询凭空多出了符合条件的数据,即为幻读。

三.MVCC(多版本并发控制)

当前读: 读取数据的最新版本,且读取时保证其他并发事务不能修改当前记录,会对读取到的记录加锁,不同的隔离级别以及不同的语句加的锁不同。

快照读: 读取数据的历史可见版本(版本访问权限由RedView控制),不加锁是非阻塞读。

记录中的三个隐藏字段:

ReadView(读视图): 通过记录一系列未提交事务的id,控制快照读时可访问的数据版本

trx_id: 当前undolog创建者id

trx_id = readview创建者事务id,说明当前记录就是该事务修改,该版本数据可以访问

trx_id<最小活跃事务id,说明修改该数据的事务已经提交,该版本数据可以访问

trx_id>=max_trx_id(预分配事务id),说明数据是在视图创建后才被修改的,不能访问该版本数据

最小活跃id<=最后一次数据修改者id<预分配事务id 说明读视图创建时,修改数据的事 务已经开启,能否访问取决于修改数据的事务是否提交,若修改数据事务id在m_ids(当 前活跃事务id集合中)中,说明查询语句执行时,修改数据事务未提交不可访问该版本数 据,若修改数据事务id不在m_ids活跃事务id中 说明查询操作开始时修改数据事务已提交 可以访问该版本数据

四.事务隔离级别

读未提交(Read uncommitted): 无任何限制手段,直接读取最新的数据,一个事务还未提交,它做的变更就能被其他事务感知到;问题最多,存在脏读、不可重复读、幻读的问题。

读已提交(Read committed): 一个事务提交之后,它做的变更才能被其他事务感知到;解决了脏读问题,但仍然存在不可重复读与幻读问题。读已提交隔离级别下的普通select语句都是快照读,且每次查询前都会生成新的ReadView,由于一系列事务id的变化,相同的读操作可读取的数据版本可能会发生改变,因此解决了脏读问题,并未解决不可重复读与幻读。

READ COMMITTED 隔离级别下,当前读的加锁行为如下:

操作类型

锁类型

锁范围

说明

SELECT ... FOR UPDATE

排他锁(X)

仅锁定匹配的记录

不加间隙锁(Gap Lock),其他事务可以插入新记录(可能幻读)。

SELECT ... FOR SHARE

共享锁(S)

仅锁定匹配的记录

其他事务可加共享锁,但不能加排他锁(不加间隙锁)。

UPDATE

/DELETE

排他锁(X)

仅锁定匹配的记录

隐式先执行当前读(类似 SELECT ... FOR UPDATE

),再修改数据。

仅加记录锁,不加间隙锁,完全无法避免幻读

可重复读(repeatable read): 一个事务执行过程中,读到的数据和其开启时是一致的,一直保持不变;解决了不可重复读问题并解决了大部分幻读问题。可重复读隔离级别下普通select语句是快照读,且只在该事务开启后的第一次快照读时生成ReadView,能读取到的数据始终是不变的,解决了不可重复读问题。

在REPEATABLE READ下,当前读操作会加以下锁:

操作类型

锁类型

锁范围

SELECT ... FOR UPDATE

排他锁(X锁)

临键锁(Next-Key Lock)

SELECT ... FOR SHARE

共享锁(S锁)

临键锁(Next-Key Lock)

UPDATE

排他锁(X锁)

临键锁(Next-Key Lock)

DELETE

排他锁(X锁)

临键锁(Next-Key Lock)

在可重复读隔离级别下,快照读时由于只生成了一次ReadView,整个事务生命周期内可读取到的数据都是一样的,因此可重复读隔离级别时在快照读的情况下解决了不可重复读与所有幻读问题(如果在事务生命周期内仅使用快照读)。而当前读情况下通过加临建锁(行锁+间隙锁)解决了大部分幻读问题。

幻读仍会发生的场景:

快照读+隐式当前读(Delete/Update/Inseret)+快照读幻读:事务A先查询某条不存在的记录没查到(快照读),此时事务B向数据库中插入了该条不存在的数据并提交,那么此时如果事务A还是执行正常的快照读的话仍然是不会出现幻读问题的,但如果事务A在未查到数据的情况下直接更新事务B刚刚插入的数据(当前读),那么该条被插入记录的隐藏字段DB_TRX_ID就会变成事务A的id,该行记录就对事务A的快照读也可见了。

快照读+显示当前读+当前读幻读: 事务A先查询某条数据(快照读),此时事务B修改了该数据并提交,此时事务A执行显示当前读(SELECT ... FOR UPDATE/SELECT ... FOR SHARE)直接读取到了被修改后的数据导致幻读。

串行化: 完全解决了脏读,不可重复读,幻读所有问题,性能最差,当两个事务出现并发冲突时会阻塞或终止其中一个事务。所有Select默认都是当前读,加锁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值