mysql 事务相关知识
ACID是衡量事务的四个特性:
原子性(Atomicity,或称不可分割性):语句要么全执行,要么全不执行,是事务最核心的特性,事务本身就是以原子性来定义的;实现主要基于undo log
一致性(Consistency):事务总是能使得数据库从一个一致性状态转移到另一种一致性状态
持久性(Durability):保证事务提交后不会因为宕机等原因导致数据丢失;实现主要基于redo log
隔离性(Isolation):保证事务执行尽可能不受其他事务影响;InnoDB默认的隔离级别是RR,RR的实现主要基于锁机制(包含next-key lock)、MVCC(包括数据的隐藏列、基于undo log的版本链、ReadView)
一致性和原子性的区别
原子性和一致性的的侧重点不同:原子性关注状态,要么全部成功,要么全部失败,不存在部分成功的状态。
而一致性关注数据的可见性,中间状态的数据对外部不可见,只有最初状态和最终状态的数据对外可见
一、MySQL常见的锁:
1、读锁(共享锁,也称为共享读锁或 S 锁):允许多个事务同时获取读取访问权限。多个事务可以同时持有读锁,且彼此之间不会产生干扰。读锁保证了事务读取的数据的一致性和稳定性。
2、写锁(独占锁,也称为排它锁或 X 锁):在获取写入访问权限时保证了独占性。当一个事务持有写锁时,其他事务无法获取读取或写入访问权限,从而确保了数据的完整性和一致性
3、间隙锁(Gap Lock):是 MySQL 中的一种特殊类型的锁,用于在事务中对范围进行保护,以防止并发事务向一个不存在的范围插入数据。Gap Lock 也只在 REPEATABLE READ 隔离级别下有效,左开右开
二、MySQL 提供了以下常见的读写锁机制:
1、表级锁(Table-level Locking):对整个表进行锁定,可以是读锁或写锁。在持有写锁期间,其他事务无法读取或写入该表。
2、行级锁(Row-level Locking):在行级别上对数据库对象(如行或页)进行锁定,可以是读锁或写锁。只有在需要访问特定行时才会进行锁定,从而提高并发性。行级锁是通过给索引上的索引项加锁来实现的,也就意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁。
3、页级锁(Page-level Locking):在数据库页级别上进行锁定,可以是读锁或写锁。它介于表级锁和行级锁之间,可以提供更高的并发性,但粒度较粗。
4、Next-Key Lock(临键锁) :行锁和间隙锁组合起来就是 Next-Key Lock也只在 REPEATABLE READ 隔离级别下有效,左开右闭
三、当前读和快照读
1、当前读:像select lock in share mode(共享锁/读锁), select for update ; update, insert ,delete(排他锁/写锁)这些操作都是一种当前读,为什么叫当前读?就是它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。
2、快照读:指的是在事务执行期间,基于某个时间点或快照时刻的数据库状态来执行读取操作。这意味着无论其他并发事务对数据进行了何种修改,快照读总是会看到一致的数据视图,不会受到其他事务的影响。像不加锁的select操作就是快照读,即不加锁的非阻塞读;快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读;
四、MVCC
MVCC,全称Multi-Version Concurrency Control,即多版本并发控制。MVCC模式的快照读情况下读操作只读该事务开始前的数据库的快照。
五、mysql并发事务所产生的一些问题
1、脏读,最容易理解。另一个事务修改了数据,但尚未提交,而本事务中的SELECT会读到这些未被提交的数据
2、不重复读。解决了脏读后,会遇到,同一个事务执行过程中,另外一个事务提交了新数据,因此本事务先后两次读到的数据结果会不一致
3、幻读:是指在一个事务中多次执行相同的查询语句,但在不同的查询之间,出现了新增或删除的行,导致结果集发生变化的情况。这种情况违反了事务的隔离性
4、第一类丢失更新:A事务撤销时,把已经提交的B事务的更新数据覆盖了。SQL标准中未对此做定义,所有数据库都已解决了第一类丢失更新的问题。
5、第二类丢失更新:A事务覆盖B事务已经提交的数据,造成B事务所做操作丢失。第二类丢失更新,和不可重复读本质上是同一类并发问题,通常将它看成不可重复读的特例。当两个或多个事务查询相同的记录,然后各自基于查询的结果更新记录时会造成第二类丢失更新问题。每个事务不知道其它事务的存在,最后一个事务对记录所做的更改将覆盖其它事务之前对该记录所做的更改。(MySQL数据库,设置事务隔离级别为可重复读无法避免发生“第二类丢失更新”问题,)
*解决第二类丢失更新
1、将事务隔离级别改为串行,能解决但并发性能低,还可能导致大量超时和锁竞争
2、悲观锁(在事务中,对于每个select操作都主动显式地加上共享锁或者排他锁(最好是共享锁),update,delete,insert已经自动加上排他锁了)
3、乐观锁(需要自己实现)
不可重复读和幻读区别:幻读主要涉及新增(insert)或删除(delete)操作引起的查询结果集发生变化,而不可
重复读主要涉及更新操作导致的同一行数据在不同读取之间的结果不一致
六、MySQL InnoDB事务的隔离级别有四级,默认是“可重复读”(REPEATABLE READ)
1、未提交读(READ UNCOMMITTED)。另一个事务修改了数据,但尚未提交,而本事务中的SELECT会读到这些未被提交的数据(存在脏读问题)。
2、提交读(READ COMMITTED)。本事务读取到的是最新的数据(其他事务提交后的)。问题是,在同一个事务里,前后两次相同的SELECT会读到不同的结果(存在不重复读问题)。
3、可重复读(REPEATABLE READ)。在同一个事务里,SELECT的结果是事务开始时时间点的状态,因此,同样的SELECT操作读到的结果会是一致的(快照读)。但是,会有幻读现象(可能会有幻读问题)。
4、串行化(SERIALIZABLE)。读操作会隐式获取共享锁,可以保证不同事务间的互斥(三种并发问题全部解决)
七、MySQL默认的事务隔离级别是"可重复读"(Repeatable Read) 是怎样防止幻读的,分两种情况。(隔离级别、幻读、Gap Lock、Next-Key Lock (baidu.com))
MVCC : 快照读就是简单的查询,查询的都是快照版本,这个场景下因为都是基于MVCC来查询快照的某个版本,所以不会存在幻读的问题,也可以认为是解决了幻读的方案之一,对于RC级别来说,因为每次查询都重新生成一个read view,也就是查询的都是最新的快照数据,所以会可能每次查询到不一样的数据,造成不可重复读,而对于RR级别来说只有第一次的时候生成read view,查询的是事务开始的时候的快照数据,所以就不存在不可重复读的问题,当然就更不可能有幻读的问题了。
快照读存在的问题:如果在同一个事务中,先更新了表中的一些行,然后进行查询,读到了更新后的数据
begin; select * from cate where id=1; update cate set kind=4700 where id=1; select * from cate where id=1; # 会查询到kind=4700 ROLLBACK;
Next-Key Lock :
所以,现在我们说幻读,其实不是指快照读的场景,而是指的是当前读的场景。当前读指的是lock in share mode、for update 、insert、update、delete这些需要加锁的操作。对于MVCC来说就是解决的快照读的场景,而对于当前读那么就是Next-Key Lock要解决的事情。
#当一个事务执行update操作时,它会在需要修改的行上加上排它锁(X锁)
#Innodb出现死锁时会怎么处理:
死锁是不会无限等待下去的,MySQL自带了死锁监控机制。死锁监控机制由参数 innodb_deadlock_detect 控制,默认就是打开的,当发现死锁时,会立刻挑选一个成本较小的事务作为“牺牲品”,回滚该事务,打破这个等待闭环