1.MySQL的四个特性(ACID)
InnoDB存储引擎还提供了两种事务日志:redo log(重做日志)和undo log(回滚日志)。其中redo log用于保证事务持久性;undo log则是事务原子性实现的基础。
- 原子性 (Atomicity)
事务是一个原子操作单元,其对数据的修改要么全部执行,要么全不执行。
举例:假设有一个银行转账事务,从账户 A 转 100 元到账户 B。这个转账事务包括两个操作:从账户 A 中扣除 100 元和给账户 B 增加 100 元。这两个操作要么都成功,要么都失败,不可能只执行其中一个。
- 一致性 (Consistency)
事务必须使数据库从一个一致性状态转变到另一个一致性状态。
举例:继续使用上面的银行转账例子,转账前后,两个账户的总金额是不变的。即转账前 A 账户有 500 元,B 账户有 300 元,转账后 A 账户变为 400 元,B 账户变为 400 元,但两者的总和仍然是 800 元。
- 隔离性 (Isolation)
在事务进行和完成期间,事务的中间状态对其他事务是不可见的。
举例:考虑两个并发的转账事务,一个是账户 A 转给账户 B 100 元,另一个是账户 C 转给账户 D 50 元。由于隔离性,这两个事务不会互相干扰。即使它们同时发生,一个事务也不会看到另一个事务的中间状态。
- 持久性 (Durability)
一旦事务完成,则其结果就是永久性的。
举例:继续上面的银行转账例子,假设事务成功地从账户 A 转了 100 元到账户 B,那么即使系统崩溃或重启,这个转账的结果仍然是有效的,不会丢失。
2.mysql底层如何实现ACID
- 原子性和持久性:这两个特性通常是通过事务日志(Redo log 和 Undo log)来实现的。当事务提交时,对应的 Redo log 会被写入磁盘以保证持久性,而 Undo log 则用于回滚和保证原子性。
- 隔离性:在底层,MySQL通过锁机制和多版本并发控制(MVCC)来实现不同隔离级别的事务隔离。
- 一致性:依赖于事务的其它三个属性(原子性、隔离性、持久性)共同保证的,只要事务是原子的、隔离的、持久的,它自然就能保证数据库状态的一致性转换。
总的来说,MySQL 通过使用事务日志、数据库约束、锁机制和多版本并发控制等技术来实现事务的 ACID 特性。这些机制确保了事务的原子性、一致性、隔离性和持久性。
3.MySQL的四种隔离级别
- Read Uncommitted(读未提交)
在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(DirtyRead)。会有脏读、不可重复读和幻读的问题
增(INSERT)/删(DELETE)/改(UPDATE):会加行锁,防止其他事务同时操作相同的记录。
查(SELECT):不会加任何锁,读取到其他事务尚未提交的数据。可能产生脏读(Dirty Read)问题。
- Read Committed(读已提交)
读取已提交数据,不可重复读,它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。会有不可重复读和幻读的问题。
增(INSERT)/删(DELETE)/改(UPDATE):会加行锁,防止其他事务同时操作相同的记录。直到当前事务完成(提交或回滚)才会释放。
查(SELECT):不会加任何锁。只能读取到已经提交的数据,避免了脏读的问题,但可能产生不可重复读(Non-repeatable Read)和幻读(Phantom Read)。
- Repeatable Read(可重复读)
读已提交数据,可重复读。这是MySQL的默认事务隔离级别,会有幻读问题。
增(INSERT)/删(DELETE)/改(UPDATE):会加行锁,防止其他事务同时操作相同的记录。直到当前事务完成(提交或回滚)才会释放。
查(SELECT):一般情况下不加锁,但是如果使用了锁定读(例如SELECT…FOR SHARE 或 SELECT…FOR
UPDATE),则会加锁。该级别通过多版本并发控制(MVCC)机制解决了“不可重复读”问题,但可能产生幻读(Phantom Read)。
- Serializable(可串行化)
事务顺序执行,没有并行,完全杜绝幻读。这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。
增(INSERT)/删(DELETE)/改(UPDATE):会加行锁,防止其他事务同时操作相同的记录。直到当前事务完成(提交或回滚)才会释放。
查(SELECT):会加共享锁,所有的读操作都必须等待写操作执行完毕,从而保证完全的隔离,防止了脏读,不可重复读和幻读。
4.事务并发遇到的几个问题
1.脏写(两个修改的事务)
有多种情况,在事务A修改数据提交前,事务B修改了数据,发现没生效,事务B就是脏写了。(以上4种隔离级别下,都不存在脏写情况,原因下面有)
列举以下两种情况:
- 如下,事务B在事务A修改提交事务前,事务B先修改提交事务,然后事务A提交事务,事务A覆盖了事务B修改的数据。对事务B来说,发现自己修改的数据没生效,事务B就是脏写了。
- 或者事务B先提交事务,然后事务A回滚,也会造成事务B脏写
注:
文上提到的4种隔离级别下,都不存在脏写情况。因为在这些隔离级别下,当两个事务A和B尝试去更新同一条数据时,假定A先更新数据,会对更新的数据行记录加上排他锁(也叫写锁,悲观锁),除非事务A提交或终止从而释放排他锁,否则事务B都是无法更新数据的。(设计数据密集型应用只是说读提交隔离级别一定可以杜绝脏写问题,并未提到读未提交隔离级别,经过实践,读未提交下事务B的更新操作也是需要等待事务A的排他锁释放,才得以执行)
2.脏读(一个读事务、一个修改事务)
- 事务A修改数据后,但是还没有提交事务,此时被事务B读取到了数据,然后事务A回滚了,事务B就是脏读了,读到了无效的数据,脏数据。
示例:
事务A把小王的钱转了100给小明,但是还没有提交事务,小明登录自己的银行APP查看自己的余额,发现多了100。然后正准备提现,此时事务A回滚了。小明提现失败,再次查询发现没有了100大洋。小明很崩溃。
脏读演示:
首先把隔离级别改为读未提交(要重启命令提示符)
set global transaction_isolation ='read-uncommitted';
查看隔离级别
show global variables like '%isolation%';
如下所示,事务回滚,读的就是脏数据
3.不可重复读(一个读事务、一个修改事务)
一个事务中多次相同的查询,查到了不同的结果,就是不可重复读。
例子:事务A查询某值为5,事务B修改为8并提交事务,事务A再次查询时值变成了8,即为不可重复读。
4.幻读(一个读事务、一个新增事务)
在一个事务中使用相同的 SQL 两次读取,数据量不一致。
例子:事务A第一次查询到5条数据,事务B新增了3条数据并提交,事务A再次查询就变成了8条数据,这时事务A出现了幻读。
5、MVCC
RC、RR隔离级别都是基于MVCC实现的
那么为什么RC不能解决不可重复读问题呢?
答:读已提交事务每次查询都会创建一个read view,而可重复读每个事务只会创建一次read view。
这就导致读已提交每次都是读的最新数据会有不可重复读问题,而可重复读每次都是读的同一个版本数据。
6、扩展
1、update … where age = ? 锁的是什么?
如果是 MyISAM 存储引擎锁的是表
如果是InnoDB存储引擎,age是索引,则锁的是行数据(行锁是锁的索引);如果age不是索引,则锁的是全表。
2、为什么age不是索引会锁住全表?
这是因为在没有索引的情况下,InnoDB 存储引擎无法只锁定特定的行(因为没有索引来快速定位到特定的行),无法知道对应行数据在磁盘中的位置。
当前读:读取最新数据(update、insert、delete、select for update)
快照读:通过版本链读取undo log中数据(普通select)
3、可重复读隔离级别 中的 快照读有幻读情况吗?
没有,因为快照读通过mvcc实现的是从版本链中读取数据,不会有幻读情况。
但是当前读会有,当前读需要解决幻读可以通过间隙锁实现。
4、版本链是什么?
版本链是由undo log 和回滚指针(roll_pointer)连接起来的。
5、RR隔离级别是如何解决幻读的?
通过间隙锁解决了幻读,但是很多企业用的隔离级别是读已提交(RC),因为读已提交没有间隙锁,大大减小了锁开销,而且幻读可以看做是一种正常现象而不是问题。如果要解决幻读可以
6、如何解决RR隔离级别下间隙锁引起的死锁问题?
例:RR级别中一个事务里面先 select for update where age = 10,但是没有age=10这行数据,只有age=5 和
age = 15,那么会锁住(5,15)这个区间,也就是间隙锁。这时候再同一个事务里面 update where age = 10会加锁失败,就会死锁。
因为间隙锁不互斥,但是间隙锁和插入语句互斥
。如果是两个查询是没问题的。
- 降低隔离级别到rc
- 采用分布式锁
首先感谢以下博主资源,附上链接:
http://www.zhai14.com/blog/strenghen-comprehension-on-dirty-read-and-phantom.html
https://zhuanlan.zhihu.com/p/150107974
https://blog.csdn.net/qq_38238296/article/details/88363017
https://www.cnblogs.com/xrq730/p/5087378.html