文章内容是学习过程中的知识总结,如有纰漏,欢迎指正
前言
事务是一个最小的
不可再分的
工作单元;通常一个事务对应一个完整的业务(如银行账户转账业务就是一个最小的工作单元);在
关系型数据库
中,一个事务可以是一条SQL语句,一组SQL语句或整个程序
以下是本篇文章正文内容,欢迎讨论交流
一、MySql存在的事务问题
-
脏写:
事务A和事务B同时修改一条数据,B进行了事务提交,A由于某些原因进行了事务回滚 -
脏读:
事务B读取到事务A未提交的数据 -
不可重复读:
在同一个事务中,两次查询同一条记录读到的结果不一致 -
幻读:
在一个事务内多次查询某个符合查询条件的「记录数量」,如果出现前后两次查询到的记录数量不一样的情况,就意味着发生了幻读
严重程度:脏写>脏读>不可重复读>幻读
1.脏写
事务A和B同时修改id=1,name=“张三”的行记录,事务A修改name=“李四”,事务未提交。事务B修改name=“王五”,进行事务提交。
事务A此时进行事务回滚,该记录数据还是id=1,name=“张三”,事务B的修改就不存在了,这种情况就叫脏写。
2.脏读
假设有 A 和 B 这两个事务同时在处理,事务 A 先开始从数据库中读取用户的余额数据,然后再执行更新操作,如果此时事务 A 还没有提交事务,而此时事务 B 也从数据库中读取用户的余额数据,那么事务 B 读取到的余额数据是刚才事务 A 更新后的数据,即使没有提交事务。
因为事务 A 是还没提交事务的,也就是它随时可能发生回滚操作,
如果上图这种情况事务 A 发生了回滚,那么事务 B 刚才得到的数据就是过期的数据,这种现象就被称为脏读。
3.不可重复读
假设有 A 和 B 这两个事务同时在处理,事务 A 先开始从数据库中读取用户的余额,然后继续执行代码逻辑,
在这过程中如果事务 B 更新了这条数据,并提交了事务,那么当事务 A 再次读取该数据时,就会发现前后两次读到的数据是不一致的,这种现象就被称为不可重复读。
4.幻读
假设有 A 和 B 这两个事务,事务 A 先开始从数据库查询账户余额大于 100 万的记录,发现共有 5 条,然后事务 B 也按相同的搜索条件也是查询出了 5 条记录。
接下来,事务 A 插入了一条余额超过 100 万的账号,并提交了事务,此时数据库超过 100 万余额的账号个数就变为 6。
然后事务 B 再次查询账户余额大于 100 万的记录,此时查询到的记录数量有 6 条,
发现和前一次读到的记录数量不一样了,就像产生了幻觉一样,这种现象就被称为幻读。
二、事务的隔离级别
-
读未提交
(READ UNCOMMITTED)一个事务还没提交时,他所做的变更能被别的事务看到 -
读已交提
(READ COMMITTED)一个事务提交之后他所做的操作才能被别的事务看到 -
可重复读
(REPEATABLE READ)一个事务内任何时候读到的数据都和事务启动时候读到的一致,
Mysql数据库Innodb引擎默认的事务隔离级别
-
串行化
(SERIALIZABLE)对记录加上读写锁,多个事务同时操作该记录时,只有前一个事务操作结束锁释放之后才能执行下一个事务
隔离水平:串行化>可重复读>读已提交>读未提交
性能效率:串行化<可重复读<读已提交<读未提交
三、事务的隔离级别及对应解决的问题
针对不同的隔离级别,并发事务时可能发生的现象也会不同
-
在
「读未提交」
隔离级别下,可能发生脏读、不可重复读和幻读现象; -
在
「读提交」
隔离级别下,可能发生不可重复读和幻读现象,但是不可能发生脏读现象; -
在
「可重复读」
隔离级别下,可能发生幻读现象,但是不可能脏读和不可重复读现象; -
在
「串行化」
隔离级别下,脏读、不可重复读和幻读现象都不可能会发生。
那么Mysql Innodb引擎在默认可重复读事务隔离级别下解决了幻读问题吗?
答案:必须解决了,并且使用的不是串行化
总结
-
脏写在所有的隔离级别中都不会发生,如果某个事务想修改另一个事务未提交的数据,就必须阻塞,等待那个事务结束
-
要解决脏读现象,就要升级到「读已提交」以上的隔离级别
-
要解决不可重复读现象,就要升级到「可重复读」的隔离级别
-
要解决幻读现象不建议将隔离级别升级到「串行化」