10-MySQL-InnoDB-Transaction

背景描述

目前在维护的 APP 进行了整体重构(其实不能算重构,应该是重新开发),新的服务端系统根据业务场景进行了功能拆分,实现了微服务,结束了以前服务端大一统的部署模式;伴随着微服务的上线,分布式事务也成了一个绕不开的问题。最近的几篇文章计划结合目前项目中的分布式事务的代码实现逻辑,对数据库事务的知识进行系统的学习和整理,当然数据库事务方面的知识很多,所以肯定不是一篇文章可以搞定的,分布式 就是要 “分布试”

PS.数据库事务的知识有哪些,一起来整理一下,今天先以 MySQL 数据库 InnoDB 存储引擎为例,总结一下单数据库事务。

事务的ACID特性

A:原子性(Atomicity)

事务被视为不可分割的最小单元,事务的所有操作要么全部提交成功,要么全部失败回滚。

C:一致性(Consistency)

一致性指事务将数据库从一种状态转变为下一种一致的状态。在事务开始之前和事务结束之后,数据库的完整性约束没有被破坏。在一致性状态下,所有事务对一个数据的读取结果都是相同的。

I:隔离性(Isolation)

一个事务所做的修改在最终提交以前,对其它事务是不可见的。

D:持久性(Durability)

一旦事务提交,则其所做的修改将会永远保存到数据库中。即使系统发生崩溃,事务的执行结果页不能丢失。

事务的并发一致性问题

丢失更新

第一类丢失更新

一个事务(A)更新某条记录数据,此时还未提交,这时另外一个事务(B)也更新了该记录数据并提交事务成功,这时 A 撤销事务,此时 B 更新成功的数据会丢失。

时间事务A事务B
T1开启事务
T2开启事务
T3查询账户余额为 100
T4查询账户余额为 100
T5更新数据 + 10 结果为 110
T6提交事务
T7更新数据 - 10 结果为 90
T8撤销事务
T7数据恢复为100(丢失更新)

第二类丢失更新

一个事务(A)更新某条记录数据,此时还未提交,这时另外一个事务(B)也更新了该记录数据并提交事务成功,此时 A 提交事务,此时 B 更新成功的数据会丢失。

时间事务A事务B
T1开启事务
T2开启事务
T3查询账户余额为100
T4查询账户余额为100
T5更新数据 - 10 结果为 90
T6提交事务
T7更新数据 + 10 结果 为 110
T8提交事务
T7数据结果为110(丢失更新)

脏读

脏读是指一个事务读取到了另外一个事务未提交的数据。一个事务正在对一条记录进行修改,在这个事务提交并完成前,这条记录的数据就处于不一致状态。这时, 另一个事务也来读取同一条记录,如果不加控制,第二个事务读取了这些“脏”数据,并据此做进一步的处理,就会产生未提交的数据依赖关系。

时间事务A事务B
T1开启事务
T2查询账户余额为100开启事务
T3充值50,余额修改为150
T4查询余额为150
T5撤销事务,余额改回100
T6汇入50,余额修改为200
T7提交事务

不可重复读

不可重复读是指一个事务读取到了另外一个事务已提交的数据。一个事务(A)读取某一个数据后,另外一个事务(B)对该数据进行了修改,当 A 再读取这个数据时,发现前后两次读取的数据不一致。

时间事务A事务B
T1开启事务
T2查询账户余额为100开启事务
T3更新账户余额为150
T4提交事务
T5查询账户余额为150
T6
T7提交事务

幻读(幻影读)

一个事务(A)按某一条件检索到 N 条数据,另外一个事务(B)新增或删除了满足条件的数据,这时 A 再按相同条件检索数据,查询到的结果 != N。

时间事务A事务B
T1开启事务开启事务
T2select * from table where condition = ‘xxx’ 返回 N 条记录
T3向 table 表插入一条满足 condition = ‘xxx’ 的数据
T4提交事务
T5select * from table where condition = ‘xxx’ 返回 N + 1 条记录
T6
T7提交事务

总结说明

  • 幻读和不可重复读的区别:

    • 不可重复读的重点是更新:在同一事务中,相同的条件,第一次和第二次读取到的数据不一致(中间有其它事务提交了更新);
    • 幻读的重点是新增或删除:在同一事务中,相同的条件,第一次和第二次读到的记录数据不一样(中间有其它事务提交了新增或者删除)。
  • 两类丢失更新问题:

    • 第一类丢失更新 (通过设置 Repeatable Read 隔离级别解决)
    • 第二类丢失更新 (需要应用程序控制,使用乐观锁解决)

事务隔离级别

SQL 标准定义了四种数据库事务的隔离级别,每一种级别中都规定了一个事务中所做的修改,哪些在事务内和事务间是可见的,哪些是不可见的。

  • Read Uncommitted:所有事务都可以看到其它事务未提交的执行结果。
  • Read Committed(RC):一个事务能够看到其它事务已提交的执行结果。
  • Repeatable Read(RR):在一个事务内多次执行同一个查询操作,前后几次获取的结果相同。
  • Serializable:串行化,每次读都需要获得表级共享锁,读写相互阻塞。
隔离级别脏读不可重复读幻读
Read UncommittedYESYESYES
Read CommittedNOYESYES
Repeatable ReadNONOYES
SerializableNONONO

四种数据库事务的隔离级别只是 SQL 标准的定义,对于不同的数据库也会有不同的实现,比如:Oracle 仅支持 Read Committed 和 Serializable 隔离级别,其中 Read Committed 是默认的隔离级别;对于 MySQL 支持 SQL 标准的四种隔离级别,其中 Repeatable Read 为默认的隔离级别。

https://github.com/ctripcorp/apollo/wiki/Java%E5%AE%A2%E6%88%B7%E7%AB%AF%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97#1241-environment

https://github.com/ctripcorp/apollo/wiki/Apollo%E5%BC%80%E6%94%BE%E5%B9%B3%E5%8F%B0

https://www.jianshu.com/p/03d1bf80f7e8

https://blog.csdn.net/z69183787/article/details/52213670

https://www.jianshu.com/p/592b2cdbc589

https://www.jianshu.com/p/d829df873332

https://blog.csdn.net/weixin_28760063/article/details/81369266

https://juejin.im/post/5b5a0bf9f265da0f6523913b

感谢您的阅读,本文由 董宗磊的博客 版权所有。如若转载,请注明出处:董宗磊的博客(https://dongzl.github.io/2019/11/19/10-MySQL-InnoDB-Transaction/
从一个功能设计聊聊策略模式的使用
Hexo 环境搭建 & 常用插件集成